Merge branch 'drm-fixes' of git://people.freedesktop.org/~airlied/linux
authorLinus Torvalds <torvalds@linux-foundation.org>
Fri, 1 Jun 2012 22:40:29 +0000 (15:40 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 1 Jun 2012 22:40:29 +0000 (15:40 -0700)
Pull drm fixes from Dave Airlie:
 "A bunch of fixes:
   - vmware memory corruption
   - ttm spinlock balance
   - cirrus/mgag200 work in the presence of efifb
  and finally Alex and Jerome managed to track down a magic set of bits
  that on certain rv740 and evergreen cards allow the correct use of the
  complete set of render backends, this makes the cards operate
  correctly in a number of scenarios we had issues in before, it also
  manages to boost speed on benchmarks my large amounts on these
  specific gpus."

* 'drm-fixes' of git://people.freedesktop.org/~airlied/linux:
  drm/edid: Make the header fixup threshold tunable
  drm/radeon: fix regression in UMS CS ioctl
  drm/vmwgfx: Fix nasty write past alloced memory area
  drm/ttm: Fix spinlock imbalance
  drm/radeon: fixup tiling group size and backendmap on r6xx-r9xx (v4)
  drm/radeon: fix HD6790, HD6570 backend programming
  drm/radeon: properly program gart on rv740, juniper, cypress, barts, hemlock
  drm/radeon: fix bank information in tiling config
  drm/mgag200: kick off conflicting framebuffers earlier.
  drm/cirrus: kick out conflicting framebuffers earlier
  cirrus: avoid crash if driver fails to load

1333 files changed:
.mailmap
Documentation/ABI/testing/sysfs-block-rssd
Documentation/ABI/testing/sysfs-bus-fcoe [new file with mode: 0644]
Documentation/ABI/testing/sysfs-bus-i2c-devices-lm3533 [new file with mode: 0644]
Documentation/ABI/testing/sysfs-bus-rbd
Documentation/ABI/testing/sysfs-class-backlight-driver-lm3533 [new file with mode: 0644]
Documentation/ABI/testing/sysfs-class-led-driver-lm3533 [new file with mode: 0644]
Documentation/CodingStyle
Documentation/cgroups/memory.txt
Documentation/cgroups/resource_counter.txt
Documentation/devicetree/bindings/gpio/gpio-mm-lantiq.txt [new file with mode: 0644]
Documentation/devicetree/bindings/gpio/gpio-stp-xway.txt [new file with mode: 0644]
Documentation/devicetree/bindings/iommu/nvidia,tegra20-gart.txt [new file with mode: 0644]
Documentation/devicetree/bindings/mfd/da9052-i2c.txt [new file with mode: 0644]
Documentation/devicetree/bindings/mfd/tps65910.txt [new file with mode: 0644]
Documentation/devicetree/bindings/mfd/twl6040.txt [new file with mode: 0644]
Documentation/devicetree/bindings/rtc/lpc32xx-rtc.txt [new file with mode: 0644]
Documentation/devicetree/bindings/rtc/spear-rtc.txt [new file with mode: 0644]
Documentation/feature-removal-schedule.txt
Documentation/filesystems/Locking
Documentation/filesystems/proc.txt
Documentation/filesystems/vfs.txt
Documentation/i2c/functionality
Documentation/i2c/i2c-protocol
Documentation/kernel-parameters.txt
Documentation/leds/ledtrig-transient.txt [new file with mode: 0644]
Documentation/power/charger-manager.txt
Documentation/power/power_supply_class.txt
Documentation/sysctl/fs.txt
Documentation/vm/pagemap.txt
Documentation/vm/transhuge.txt
Documentation/watchdog/watchdog-kernel-api.txt
Documentation/watchdog/watchdog-parameters.txt
MAINTAINERS
arch/alpha/include/asm/posix_types.h
arch/alpha/kernel/signal.c
arch/arm/Kconfig
arch/arm/boot/dts/exynos5250.dtsi
arch/arm/boot/dts/lpc32xx.dtsi
arch/arm/boot/dts/phy3250.dts
arch/arm/boot/dts/vexpress-v2p-ca15-tc1.dts
arch/arm/boot/dts/vexpress-v2p-ca5s.dts
arch/arm/boot/dts/vexpress-v2p-ca9.dts
arch/arm/configs/imx_v4_v5_defconfig
arch/arm/configs/imx_v6_v7_defconfig
arch/arm/include/asm/io.h
arch/arm/include/asm/posix_types.h
arch/arm/include/asm/thread_info.h
arch/arm/kernel/entry-common.S
arch/arm/kernel/ptrace.c
arch/arm/kernel/signal.c
arch/arm/kernel/signal.h
arch/arm/kernel/smp.c
arch/arm/kernel/traps.c
arch/arm/mach-exynos/Kconfig
arch/arm/mach-exynos/Makefile
arch/arm/mach-exynos/clock-exynos5.c
arch/arm/mach-exynos/cpuidle.c
arch/arm/mach-exynos/include/mach/pm-core.h
arch/arm/mach-exynos/include/mach/pmu.h
arch/arm/mach-exynos/include/mach/regs-clock.h
arch/arm/mach-exynos/include/mach/regs-pmu.h
arch/arm/mach-exynos/pm.c
arch/arm/mach-exynos/pmu.c
arch/arm/mach-ixp4xx/common.c
arch/arm/mach-ixp4xx/include/mach/gpio.h
arch/arm/mach-s3c24xx/include/mach/irqs.h
arch/arm/mach-s3c24xx/irq-s3c2416.c
arch/arm/mach-s3c24xx/s3c2416.c
arch/arm/mach-s3c64xx/cpuidle.c
arch/arm/mach-s3c64xx/mach-crag6410-module.c
arch/arm/mach-s3c64xx/mach-crag6410.c
arch/arm/mach-sa1100/neponset.c
arch/arm/mach-ux500/board-mop500.c
arch/arm/mach-vexpress/v2m.c
arch/arm/plat-samsung/include/plat/s3c2416.h
arch/avr32/include/asm/posix_types.h
arch/avr32/kernel/entry-avr32b.S
arch/avr32/kernel/signal.c
arch/blackfin/include/asm/posix_types.h
arch/blackfin/include/asm/thread_info.h
arch/blackfin/kernel/signal.c
arch/blackfin/kernel/trace.c
arch/blackfin/mach-common/entry.S
arch/c6x/kernel/signal.c
arch/cris/Kconfig
arch/cris/arch-v10/kernel/signal.c
arch/cris/arch-v32/kernel/signal.c
arch/cris/include/asm/posix_types.h
arch/cris/kernel/ptrace.c
arch/frv/include/asm/posix_types.h
arch/frv/include/asm/thread_info.h
arch/frv/kernel/entry.S
arch/frv/kernel/signal.c
arch/h8300/include/asm/posix_types.h
arch/h8300/kernel/signal.c
arch/hexagon/kernel/signal.c
arch/ia64/include/asm/posix_types.h
arch/ia64/include/asm/thread_info.h
arch/ia64/kernel/perfmon.c
arch/ia64/kernel/process.c
arch/ia64/kernel/signal.c
arch/ia64/kernel/sys_ia64.c
arch/m32r/include/asm/posix_types.h
arch/m32r/kernel/signal.c
arch/m68k/include/asm/posix_types.h
arch/m68k/kernel/signal.c
arch/microblaze/include/asm/thread_info.h
arch/microblaze/kernel/signal.c
arch/mips/Kconfig
arch/mips/alchemy/devboards/db1200.c
arch/mips/ath79/Kconfig
arch/mips/ath79/Makefile
arch/mips/ath79/clock.c
arch/mips/ath79/common.c
arch/mips/ath79/dev-common.c
arch/mips/ath79/dev-gpio-buttons.c
arch/mips/ath79/dev-leds-gpio.c
arch/mips/ath79/dev-wmac.c
arch/mips/ath79/early_printk.c
arch/mips/ath79/gpio.c
arch/mips/ath79/irq.c
arch/mips/ath79/mach-db120.c [new file with mode: 0644]
arch/mips/ath79/mach-pb44.c
arch/mips/ath79/mach-ubnt-xm.c
arch/mips/ath79/machtypes.h
arch/mips/ath79/pci.c [new file with mode: 0644]
arch/mips/ath79/pci.h [new file with mode: 0644]
arch/mips/ath79/setup.c
arch/mips/bcm63xx/boards/Makefile
arch/mips/cavium-octeon/setup.c
arch/mips/cavium-octeon/smp.c
arch/mips/fw/arc/Makefile
arch/mips/include/asm/clkdev.h [new file with mode: 0644]
arch/mips/include/asm/mach-ath79/ar71xx_regs.h
arch/mips/include/asm/mach-ath79/ath79.h
arch/mips/include/asm/mach-ath79/irq.h
arch/mips/include/asm/mach-ath79/pci-ath724x.h [deleted file]
arch/mips/include/asm/mach-ath79/pci.h [new file with mode: 0644]
arch/mips/include/asm/mach-bcm63xx/bcm63xx_gpio.h
arch/mips/include/asm/mach-lantiq/falcon/falcon_irq.h [new file with mode: 0644]
arch/mips/include/asm/mach-lantiq/falcon/irq.h [new file with mode: 0644]
arch/mips/include/asm/mach-lantiq/falcon/lantiq_soc.h [new file with mode: 0644]
arch/mips/include/asm/mach-lantiq/gpio.h [new file with mode: 0644]
arch/mips/include/asm/mach-lantiq/lantiq.h
arch/mips/include/asm/mach-lantiq/lantiq_platform.h
arch/mips/include/asm/mach-lantiq/xway/lantiq_irq.h
arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h
arch/mips/include/asm/mips-boards/generic.h
arch/mips/include/asm/module.h
arch/mips/include/asm/octeon/cvmx-pcieep-defs.h [deleted file]
arch/mips/include/asm/pci.h
arch/mips/include/asm/posix_types.h
arch/mips/include/asm/prom.h
arch/mips/include/asm/setup.h
arch/mips/include/asm/sparsemem.h
arch/mips/include/asm/stat.h
arch/mips/include/asm/termios.h
arch/mips/include/asm/traps.h
arch/mips/include/asm/uasm.h
arch/mips/jz4740/Makefile
arch/mips/kernel/cpu-probe.c
arch/mips/kernel/perf_event_mipsxx.c
arch/mips/kernel/proc.c
arch/mips/kernel/prom.c
arch/mips/kernel/setup.c
arch/mips/kernel/signal-common.h
arch/mips/kernel/signal.c
arch/mips/kernel/signal32.c
arch/mips/kernel/signal_n32.c
arch/mips/kernel/smp.c
arch/mips/kernel/traps.c
arch/mips/lantiq/Kconfig
arch/mips/lantiq/Makefile
arch/mips/lantiq/Platform
arch/mips/lantiq/clk.c
arch/mips/lantiq/clk.h
arch/mips/lantiq/devices.c [deleted file]
arch/mips/lantiq/devices.h [deleted file]
arch/mips/lantiq/dts/Makefile [new file with mode: 0644]
arch/mips/lantiq/dts/danube.dtsi [new file with mode: 0644]
arch/mips/lantiq/dts/easy50712.dts [new file with mode: 0644]
arch/mips/lantiq/early_printk.c
arch/mips/lantiq/falcon/Makefile [new file with mode: 0644]
arch/mips/lantiq/falcon/prom.c [new file with mode: 0644]
arch/mips/lantiq/falcon/reset.c [new file with mode: 0644]
arch/mips/lantiq/falcon/sysctrl.c [new file with mode: 0644]
arch/mips/lantiq/irq.c
arch/mips/lantiq/machtypes.h [deleted file]
arch/mips/lantiq/prom.c
arch/mips/lantiq/prom.h
arch/mips/lantiq/setup.c [deleted file]
arch/mips/lantiq/xway/Kconfig [deleted file]
arch/mips/lantiq/xway/Makefile
arch/mips/lantiq/xway/clk-ase.c [deleted file]
arch/mips/lantiq/xway/clk-xway.c [deleted file]
arch/mips/lantiq/xway/clk.c [new file with mode: 0644]
arch/mips/lantiq/xway/devices.c [deleted file]
arch/mips/lantiq/xway/devices.h [deleted file]
arch/mips/lantiq/xway/dma.c
arch/mips/lantiq/xway/ebu.c [deleted file]
arch/mips/lantiq/xway/gpio.c
arch/mips/lantiq/xway/gpio_ebu.c [deleted file]
arch/mips/lantiq/xway/gpio_stp.c [deleted file]
arch/mips/lantiq/xway/mach-easy50601.c [deleted file]
arch/mips/lantiq/xway/mach-easy50712.c [deleted file]
arch/mips/lantiq/xway/pmu.c [deleted file]
arch/mips/lantiq/xway/prom-ase.c [deleted file]
arch/mips/lantiq/xway/prom-xway.c [deleted file]
arch/mips/lantiq/xway/prom.c [new file with mode: 0644]
arch/mips/lantiq/xway/reset.c
arch/mips/lantiq/xway/setup-ase.c [deleted file]
arch/mips/lantiq/xway/setup-xway.c [deleted file]
arch/mips/lantiq/xway/sysctrl.c [new file with mode: 0644]
arch/mips/mm/c-octeon.c
arch/mips/mm/c-r4k.c
arch/mips/oprofile/Makefile
arch/mips/oprofile/op_model_mipsxx.c
arch/mips/pci/Makefile
arch/mips/pci/fixup-lantiq.c [new file with mode: 0644]
arch/mips/pci/ops-loongson2.c
arch/mips/pci/pci-ar71xx.c [new file with mode: 0644]
arch/mips/pci/pci-ar724x.c [new file with mode: 0644]
arch/mips/pci/pci-ath724x.c [deleted file]
arch/mips/pci/pci-lantiq.c
arch/mips/pci/pci.c
arch/mips/pmc-sierra/yosemite/Makefile
arch/mips/pmc-sierra/yosemite/setup.c
arch/mips/powertv/Makefile
arch/mips/powertv/asic/Makefile
arch/mips/powertv/pci/Makefile
arch/mips/rb532/devices.c
arch/mips/sni/setup.c
arch/mn10300/include/asm/posix_types.h
arch/mn10300/kernel/signal.c
arch/openrisc/kernel/signal.c
arch/parisc/Kconfig
arch/parisc/include/asm/posix_types.h
arch/parisc/include/asm/smp.h
arch/parisc/include/asm/stat.h
arch/parisc/include/asm/thread_info.h
arch/parisc/include/asm/uaccess.h
arch/parisc/kernel/entry.S
arch/parisc/kernel/parisc_ksyms.c
arch/parisc/kernel/signal.c
arch/parisc/kernel/signal32.c
arch/parisc/kernel/vmlinux.lds.S
arch/parisc/lib/lusercopy.S
arch/powerpc/include/asm/posix_types.h
arch/powerpc/include/asm/stat.h
arch/powerpc/include/asm/thread_info.h
arch/powerpc/kernel/signal.c
arch/powerpc/kernel/signal.h
arch/powerpc/kernel/signal_32.c
arch/powerpc/kernel/signal_64.c
arch/powerpc/mm/mmu_context_nohash.c
arch/s390/Kconfig
arch/s390/include/asm/bitops.h
arch/s390/include/asm/cio.h
arch/s390/include/asm/cmpxchg.h
arch/s390/include/asm/cputime.h
arch/s390/include/asm/ctl_reg.h
arch/s390/include/asm/current.h
arch/s390/include/asm/elf.h
arch/s390/include/asm/futex.h
arch/s390/include/asm/idals.h
arch/s390/include/asm/io.h
arch/s390/include/asm/irq.h
arch/s390/include/asm/kexec.h
arch/s390/include/asm/kmap_types.h
arch/s390/include/asm/mmu_context.h
arch/s390/include/asm/module.h
arch/s390/include/asm/os_info.h
arch/s390/include/asm/percpu.h
arch/s390/include/asm/pgalloc.h
arch/s390/include/asm/pgtable.h
arch/s390/include/asm/posix_types.h
arch/s390/include/asm/processor.h
arch/s390/include/asm/rwsem.h
arch/s390/include/asm/setup.h
arch/s390/include/asm/sfp-util.h
arch/s390/include/asm/string.h
arch/s390/include/asm/thread_info.h
arch/s390/include/asm/timer.h
arch/s390/include/asm/tlb.h
arch/s390/include/asm/tlbflush.h
arch/s390/include/asm/types.h
arch/s390/include/asm/uaccess.h
arch/s390/include/asm/vdso.h
arch/s390/kernel/base.S
arch/s390/kernel/compat_signal.c
arch/s390/kernel/early.c
arch/s390/kernel/entry.h
arch/s390/kernel/head_kdump.S
arch/s390/kernel/ipl.c
arch/s390/kernel/irq.c
arch/s390/kernel/machine_kexec.c
arch/s390/kernel/os_info.c
arch/s390/kernel/perf_cpum_cf.c
arch/s390/kernel/setup.c
arch/s390/kernel/signal.c
arch/s390/kernel/smp.c
arch/s390/kernel/sysinfo.c
arch/s390/lib/uaccess_mvcos.c
arch/s390/lib/uaccess_std.c
arch/s390/mm/maccess.c
arch/s390/mm/vmem.c
arch/s390/oprofile/hwsampler.c
arch/score/kernel/signal.c
arch/sh/include/asm/posix_types_32.h
arch/sh/include/asm/posix_types_64.h
arch/sh/include/asm/thread_info.h
arch/sh/kernel/signal_32.c
arch/sh/kernel/signal_64.c
arch/sh/kernel/smp.c
arch/sparc/Kconfig
arch/sparc/include/asm/asi.h
arch/sparc/include/asm/asmmacro.h
arch/sparc/include/asm/dma-mapping.h
arch/sparc/include/asm/leon.h
arch/sparc/include/asm/leon_amba.h
arch/sparc/include/asm/pgtsrmmu.h
arch/sparc/include/asm/posix_types.h
arch/sparc/include/asm/psr.h
arch/sparc/include/asm/sections.h
arch/sparc/include/asm/thread_info_32.h
arch/sparc/include/asm/thread_info_64.h
arch/sparc/kernel/Makefile
arch/sparc/kernel/cpu.c
arch/sparc/kernel/entry.S
arch/sparc/kernel/etrap_32.S
arch/sparc/kernel/head_32.S
arch/sparc/kernel/ioport.c
arch/sparc/kernel/irq_32.c
arch/sparc/kernel/kernel.h
arch/sparc/kernel/leon_kernel.c
arch/sparc/kernel/leon_pmc.c
arch/sparc/kernel/leon_smp.c
arch/sparc/kernel/process_32.c
arch/sparc/kernel/prom_common.c
arch/sparc/kernel/rtrap_32.S
arch/sparc/kernel/setup_32.c
arch/sparc/kernel/signal32.c
arch/sparc/kernel/signal_32.c
arch/sparc/kernel/signal_64.c
arch/sparc/kernel/sys_sparc_64.c
arch/sparc/kernel/trampoline_32.S
arch/sparc/kernel/traps_64.c
arch/sparc/kernel/vmlinux.lds.S
arch/sparc/kernel/wof.S
arch/sparc/kernel/wuf.S
arch/sparc/math-emu/math_64.c
arch/sparc/mm/Makefile
arch/sparc/mm/leon_mm.c
arch/sparc/mm/srmmu.c
arch/sparc/mm/srmmu_access.S [new file with mode: 0644]
arch/tile/include/asm/compat.h
arch/tile/include/asm/thread_info.h
arch/tile/kernel/compat_signal.c
arch/tile/kernel/process.c
arch/tile/kernel/signal.c
arch/um/include/shared/frame_kern.h
arch/um/kernel/process.c
arch/um/kernel/reboot.c
arch/um/kernel/signal.c
arch/um/kernel/trap.c
arch/unicore32/kernel/signal.c
arch/x86/Kbuild
arch/x86/ia32/ia32_signal.c
arch/x86/include/asm/acpi.h
arch/x86/include/asm/bitops.h
arch/x86/include/asm/pgtable-3level.h
arch/x86/include/asm/posix_types_32.h
arch/x86/include/asm/processor.h
arch/x86/include/asm/realmode.h [new file with mode: 0644]
arch/x86/include/asm/sighandling.h
arch/x86/include/asm/sta2x11.h [new file with mode: 0644]
arch/x86/include/asm/thread_info.h
arch/x86/include/asm/trampoline.h [deleted file]
arch/x86/kernel/Makefile
arch/x86/kernel/acpi/Makefile
arch/x86/kernel/acpi/realmode/.gitignore [deleted file]
arch/x86/kernel/acpi/realmode/Makefile [deleted file]
arch/x86/kernel/acpi/realmode/bioscall.S [deleted file]
arch/x86/kernel/acpi/realmode/copy.S [deleted file]
arch/x86/kernel/acpi/realmode/regs.c [deleted file]
arch/x86/kernel/acpi/realmode/video-bios.c [deleted file]
arch/x86/kernel/acpi/realmode/video-mode.c [deleted file]
arch/x86/kernel/acpi/realmode/video-vesa.c [deleted file]
arch/x86/kernel/acpi/realmode/video-vga.c [deleted file]
arch/x86/kernel/acpi/realmode/wakemain.c [deleted file]
arch/x86/kernel/acpi/realmode/wakeup.S [deleted file]
arch/x86/kernel/acpi/realmode/wakeup.h [deleted file]
arch/x86/kernel/acpi/realmode/wakeup.lds.S [deleted file]
arch/x86/kernel/acpi/sleep.c
arch/x86/kernel/acpi/sleep.h
arch/x86/kernel/acpi/wakeup_rm.S [deleted file]
arch/x86/kernel/cpu/mcheck/mce.c
arch/x86/kernel/cpu/mtrr/cleanup.c
arch/x86/kernel/e820.c
arch/x86/kernel/entry_32.S
arch/x86/kernel/head32.c
arch/x86/kernel/head64.c
arch/x86/kernel/head_32.S
arch/x86/kernel/head_64.S
arch/x86/kernel/hpet.c
arch/x86/kernel/mpparse.c
arch/x86/kernel/reboot.c
arch/x86/kernel/reboot_32.S [deleted file]
arch/x86/kernel/setup.c
arch/x86/kernel/signal.c
arch/x86/kernel/smpboot.c
arch/x86/kernel/tboot.c
arch/x86/kernel/trampoline.c [deleted file]
arch/x86/kernel/trampoline_32.S [deleted file]
arch/x86/kernel/trampoline_64.S [deleted file]
arch/x86/kernel/vmlinux.lds.S
arch/x86/kvm/mmu.c
arch/x86/mm/init.c
arch/x86/mm/numa.c
arch/x86/mm/numa_emulation.c
arch/x86/mm/pat.c
arch/x86/mm/srat.c
arch/x86/realmode/Makefile [new file with mode: 0644]
arch/x86/realmode/init.c [new file with mode: 0644]
arch/x86/realmode/rm/.gitignore [new file with mode: 0644]
arch/x86/realmode/rm/Makefile [new file with mode: 0644]
arch/x86/realmode/rm/bioscall.S [new file with mode: 0644]
arch/x86/realmode/rm/copy.S [new file with mode: 0644]
arch/x86/realmode/rm/header.S [new file with mode: 0644]
arch/x86/realmode/rm/realmode.h [new file with mode: 0644]
arch/x86/realmode/rm/realmode.lds.S [new file with mode: 0644]
arch/x86/realmode/rm/reboot_32.S [new file with mode: 0644]
arch/x86/realmode/rm/regs.c [new file with mode: 0644]
arch/x86/realmode/rm/stack.S [new file with mode: 0644]
arch/x86/realmode/rm/trampoline_32.S [new file with mode: 0644]
arch/x86/realmode/rm/trampoline_64.S [new file with mode: 0644]
arch/x86/realmode/rm/trampoline_common.S [new file with mode: 0644]
arch/x86/realmode/rm/video-bios.c [new file with mode: 0644]
arch/x86/realmode/rm/video-mode.c [new file with mode: 0644]
arch/x86/realmode/rm/video-vesa.c [new file with mode: 0644]
arch/x86/realmode/rm/video-vga.c [new file with mode: 0644]
arch/x86/realmode/rm/wakemain.c [new file with mode: 0644]
arch/x86/realmode/rm/wakeup.h [new file with mode: 0644]
arch/x86/realmode/rm/wakeup_asm.S [new file with mode: 0644]
arch/x86/realmode/rmpiggy.S [new file with mode: 0644]
arch/x86/syscalls/syscall_32.tbl
arch/x86/syscalls/syscall_64.tbl
arch/x86/tools/relocs.c
arch/x86/um/signal.c
arch/x86/xen/enlighten.c
arch/xtensa/kernel/signal.c
block/Kconfig.iosched
block/blk-cgroup.c
block/blk-cgroup.h
block/blk-core.c
block/blk-ioc.c
block/blk-sysfs.c
block/blk-throttle.c
block/blk.h
block/cfq-iosched.c
block/cfq.h [deleted file]
block/deadline-iosched.c
block/elevator.c
block/noop-iosched.c
drivers/acpi/bgrt.c
drivers/acpi/sleep.c
drivers/atm/solos-pci.c
drivers/base/node.c
drivers/base/regmap/regmap-i2c.c
drivers/base/soc.c
drivers/block/drbd/drbd_actlog.c
drivers/block/drbd/drbd_bitmap.c
drivers/block/drbd/drbd_int.h
drivers/block/drbd/drbd_main.c
drivers/block/drbd/drbd_nl.c
drivers/block/drbd/drbd_proc.c
drivers/block/drbd/drbd_receiver.c
drivers/block/drbd/drbd_req.c
drivers/block/drbd/drbd_req.h
drivers/block/drbd/drbd_worker.c
drivers/block/floppy.c
drivers/block/mtip32xx/mtip32xx.c
drivers/block/mtip32xx/mtip32xx.h
drivers/block/rbd.c
drivers/block/xen-blkfront.c
drivers/edac/amd64_edac.c
drivers/edac/amd76x_edac.c
drivers/edac/cell_edac.c
drivers/edac/cpc925_edac.c
drivers/edac/e752x_edac.c
drivers/edac/e7xxx_edac.c
drivers/edac/edac_core.h
drivers/edac/edac_device.c
drivers/edac/edac_mc.c
drivers/edac/edac_mc_sysfs.c
drivers/edac/edac_module.h
drivers/edac/edac_pci.c
drivers/edac/i3000_edac.c
drivers/edac/i3200_edac.c
drivers/edac/i5000_edac.c
drivers/edac/i5100_edac.c
drivers/edac/i5400_edac.c
drivers/edac/i7300_edac.c
drivers/edac/i7core_edac.c
drivers/edac/i82443bxgx_edac.c
drivers/edac/i82860_edac.c
drivers/edac/i82875p_edac.c
drivers/edac/i82975x_edac.c
drivers/edac/mce_amd.h
drivers/edac/mpc85xx_edac.c
drivers/edac/mv64x60_edac.c
drivers/edac/pasemi_edac.c
drivers/edac/ppc4xx_edac.c
drivers/edac/r82600_edac.c
drivers/edac/sb_edac.c
drivers/edac/tile_edac.c
drivers/edac/x38_edac.c
drivers/gpio/Kconfig
drivers/gpio/Makefile
drivers/gpio/gpio-ich.c [new file with mode: 0644]
drivers/gpio/gpio-mm-lantiq.c [new file with mode: 0644]
drivers/gpio/gpio-sch.c
drivers/gpio/gpio-sta2x11.c [new file with mode: 0644]
drivers/gpio/gpio-stp-xway.c [new file with mode: 0644]
drivers/gpio/gpio-tps65910.c
drivers/gpio/gpio-wm831x.c
drivers/gpu/drm/i810/i810_dma.c
drivers/gpu/drm/i915/i915_drv.h
drivers/gpu/drm/i915/i915_gem_dmabuf.c
drivers/gpu/drm/nouveau/nouveau_drv.h
drivers/gpu/drm/nouveau/nouveau_prime.c
drivers/gpu/drm/radeon/radeon.h
drivers/gpu/drm/radeon/radeon_prime.c
drivers/gpu/drm/udl/udl_fb.c
drivers/gpu/drm/udl/udl_gem.c
drivers/hwmon/Kconfig
drivers/hwmon/sch5627.c
drivers/hwmon/sch5636.c
drivers/hwmon/sch56xx-common.c
drivers/hwmon/sch56xx-common.h
drivers/i2c/algos/i2c-algo-bit.c
drivers/i2c/busses/i2c-nuc900.c
drivers/i2c/busses/i2c-s3c2410.c
drivers/i2c/i2c-dev.c
drivers/input/joystick/as5011.c
drivers/input/misc/wm831x-on.c
drivers/input/touchscreen/wm831x-ts.c
drivers/iommu/amd_iommu.c
drivers/iommu/iommu.c
drivers/iommu/omap-iommu.c
drivers/iommu/tegra-gart.c
drivers/iommu/tegra-smmu.c
drivers/leds/Kconfig
drivers/leds/Makefile
drivers/leds/led-class.c
drivers/leds/leds-da9052.c [new file with mode: 0644]
drivers/leds/leds-lm3530.c
drivers/leds/leds-lm3533.c [new file with mode: 0644]
drivers/leds/leds-lp5521.c
drivers/leds/leds-mc13783.c
drivers/leds/leds-pca955x.c
drivers/leds/ledtrig-backlight.c
drivers/leds/ledtrig-gpio.c
drivers/leds/ledtrig-heartbeat.c
drivers/leds/ledtrig-timer.c
drivers/leds/ledtrig-transient.c [new file with mode: 0644]
drivers/message/fusion/mptbase.c
drivers/message/fusion/mptctl.c
drivers/mfd/Kconfig
drivers/mfd/Makefile
drivers/mfd/ab8500-core.c
drivers/mfd/ab8500-debugfs.c
drivers/mfd/ab8500-gpadc.c
drivers/mfd/ab8500-i2c.c [deleted file]
drivers/mfd/ab8500-sysctrl.c
drivers/mfd/anatop-mfd.c
drivers/mfd/asic3.c
drivers/mfd/cs5535-mfd.c
drivers/mfd/da9052-core.c
drivers/mfd/da9052-i2c.c
drivers/mfd/da9052-spi.c
drivers/mfd/db8500-prcmu.c
drivers/mfd/intel_msic.c
drivers/mfd/janz-cmodio.c
drivers/mfd/lm3533-core.c [new file with mode: 0644]
drivers/mfd/lm3533-ctrlbank.c [new file with mode: 0644]
drivers/mfd/lpc_ich.c [new file with mode: 0644]
drivers/mfd/lpc_sch.c
drivers/mfd/max77693-irq.c [new file with mode: 0644]
drivers/mfd/max77693.c [new file with mode: 0644]
drivers/mfd/mc13xxx-core.c
drivers/mfd/mc13xxx-i2c.c [new file with mode: 0644]
drivers/mfd/mc13xxx-spi.c [new file with mode: 0644]
drivers/mfd/mc13xxx.h [new file with mode: 0644]
drivers/mfd/pcf50633-core.c
drivers/mfd/rc5t583.c
drivers/mfd/rdc321x-southbridge.c
drivers/mfd/s5m-core.c
drivers/mfd/sta2x11-mfd.c [new file with mode: 0644]
drivers/mfd/stmpe-spi.c
drivers/mfd/tps65090.c
drivers/mfd/tps65217.c
drivers/mfd/tps65910-irq.c
drivers/mfd/tps65910.c
drivers/mfd/twl4030-irq.c
drivers/mfd/twl6040-core.c
drivers/mfd/twl6040-irq.c
drivers/mfd/vx855.c
drivers/mfd/wm831x-auxadc.c
drivers/mfd/wm831x-core.c
drivers/mfd/wm831x-irq.c
drivers/mfd/wm8350-core.c
drivers/mfd/wm8350-i2c.c
drivers/mfd/wm8400-core.c
drivers/mfd/wm8994-core.c
drivers/mfd/wm8994-regmap.c
drivers/misc/ab8500-pwm.c
drivers/mtd/maps/lantiq-flash.c
drivers/net/ethernet/rdc/r6040.c
drivers/net/ethernet/renesas/sh_eth.c
drivers/net/ethernet/smsc/smsc911x.c
drivers/net/usb/asix.c
drivers/net/usb/qmi_wwan.c
drivers/net/virtio_net.c
drivers/net/wireless/ath/ath5k/base.c
drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
drivers/net/wireless/ath/ath9k/ar9003_eeprom.h
drivers/net/wireless/ath/ath9k/ar9330_1p1_initvals.h
drivers/net/wireless/ath/ath9k/hw.c
drivers/net/wireless/ath/ath9k/main.c
drivers/net/wireless/ath/ath9k/xmit.c
drivers/net/wireless/brcm80211/brcmfmac/usb.c
drivers/net/wireless/iwlwifi/Kconfig
drivers/net/wireless/iwlwifi/Makefile
drivers/net/wireless/iwlwifi/iwl-2000.c
drivers/net/wireless/iwlwifi/iwl-6000.c
drivers/net/wireless/iwlwifi/iwl-agn-rs.c
drivers/net/wireless/iwlwifi/iwl-agn-sta.c
drivers/net/wireless/iwlwifi/iwl-drv.c
drivers/net/wireless/iwlwifi/iwl-phy-db.c [deleted file]
drivers/net/wireless/iwlwifi/iwl-phy-db.h [deleted file]
drivers/net/wireless/iwlwifi/iwl-trans-pcie-int.h
drivers/net/wireless/iwlwifi/iwl-trans-pcie-tx.c
drivers/net/wireless/iwlwifi/iwl-trans-pcie.c
drivers/net/wireless/ti/wl1251/sdio.c
drivers/net/wireless/ti/wl1251/spi.c
drivers/net/wireless/ti/wlcore/acx.c
drivers/net/wireless/ti/wlcore/acx.h
drivers/net/wireless/ti/wlcore/rx.c
drivers/net/xen-netback/netback.c
drivers/nfc/pn544_hci.c
drivers/of/of_pci_irq.c
drivers/pci/pci.c
drivers/platform/x86/toshiba_acpi.c
drivers/power/Kconfig
drivers/power/ab8500_btemp.c
drivers/power/ab8500_charger.c
drivers/power/ab8500_fg.c
drivers/power/charger-manager.c
drivers/power/ds2781_battery.c
drivers/power/isp1704_charger.c
drivers/power/max17042_battery.c
drivers/power/power_supply_sysfs.c
drivers/power/sbs-battery.c
drivers/power/smb347-charger.c
drivers/power/wm831x_power.c
drivers/rapidio/Kconfig
drivers/rapidio/devices/Makefile
drivers/rapidio/devices/tsi721.c
drivers/rapidio/devices/tsi721.h
drivers/rapidio/devices/tsi721_dma.c [new file with mode: 0644]
drivers/rapidio/rio.c
drivers/regulator/anatop-regulator.c
drivers/regulator/tps65910-regulator.c
drivers/regulator/wm831x-dcdc.c
drivers/regulator/wm831x-isink.c
drivers/regulator/wm831x-ldo.c
drivers/remoteproc/remoteproc_core.c
drivers/rtc/Kconfig
drivers/rtc/Makefile
drivers/rtc/rtc-ds1307.c
drivers/rtc/rtc-ep93xx.c
drivers/rtc/rtc-lpc32xx.c
drivers/rtc/rtc-m41t93.c
drivers/rtc/rtc-pcf8563.c
drivers/rtc/rtc-pl031.c
drivers/rtc/rtc-s3c.c
drivers/rtc/rtc-spear.c
drivers/rtc/rtc-tegra.c
drivers/rtc/rtc-wm831x.c
drivers/s390/block/dasd_int.h
drivers/s390/char/sclp_sdias.c
drivers/scsi/be2iscsi/be_mgmt.c
drivers/scsi/bfa/bfad_attr.c
drivers/scsi/bfa/bfad_im.c
drivers/scsi/bfa/bfad_im.h
drivers/scsi/bnx2fc/bnx2fc.h
drivers/scsi/bnx2fc/bnx2fc_els.c
drivers/scsi/bnx2fc/bnx2fc_fcoe.c
drivers/scsi/bnx2fc/bnx2fc_hwi.c
drivers/scsi/bnx2fc/bnx2fc_io.c
drivers/scsi/bnx2fc/bnx2fc_tgt.c
drivers/scsi/fcoe/Makefile
drivers/scsi/fcoe/fcoe.c
drivers/scsi/fcoe/fcoe.h
drivers/scsi/fcoe/fcoe_ctlr.c
drivers/scsi/fcoe/fcoe_sysfs.c [new file with mode: 0644]
drivers/scsi/fcoe/fcoe_transport.c
drivers/scsi/qla2xxx/Kconfig
drivers/scsi/qla2xxx/Makefile
drivers/scsi/qla2xxx/qla_attr.c
drivers/scsi/qla2xxx/qla_bsg.c
drivers/scsi/qla2xxx/qla_dbg.c
drivers/scsi/qla2xxx/qla_dbg.h
drivers/scsi/qla2xxx/qla_def.h
drivers/scsi/qla2xxx/qla_gbl.h
drivers/scsi/qla2xxx/qla_gs.c
drivers/scsi/qla2xxx/qla_init.c
drivers/scsi/qla2xxx/qla_iocb.c
drivers/scsi/qla2xxx/qla_isr.c
drivers/scsi/qla2xxx/qla_mbx.c
drivers/scsi/qla2xxx/qla_mid.c
drivers/scsi/qla2xxx/qla_nx.c
drivers/scsi/qla2xxx/qla_nx.h
drivers/scsi/qla2xxx/qla_os.c
drivers/scsi/qla2xxx/qla_target.c [new file with mode: 0644]
drivers/scsi/qla2xxx/qla_target.h [new file with mode: 0644]
drivers/scsi/qla2xxx/tcm_qla2xxx.c [new file with mode: 0644]
drivers/scsi/qla2xxx/tcm_qla2xxx.h [new file with mode: 0644]
drivers/scsi/qla4xxx/ql4_attr.c
drivers/scsi/qla4xxx/ql4_def.h
drivers/scsi/qla4xxx/ql4_fw.h
drivers/scsi/qla4xxx/ql4_glbl.h
drivers/scsi/qla4xxx/ql4_init.c
drivers/scsi/qla4xxx/ql4_mbx.c
drivers/scsi/qla4xxx/ql4_nx.c
drivers/scsi/qla4xxx/ql4_nx.h
drivers/scsi/qla4xxx/ql4_os.c
drivers/scsi/qla4xxx/ql4_version.h
drivers/scsi/scsi_lib.c
drivers/scsi/scsi_pm.c
drivers/scsi/scsi_scan.c
drivers/scsi/scsi_wait_scan.c
drivers/scsi/ufs/ufshcd.c
drivers/staging/android/ashmem.c
drivers/tty/pty.c
drivers/tty/serial/lantiq.c
drivers/tty/serial/sb1250-duart.c
drivers/tty/serial/zs.c
drivers/tty/tty_ldisc.c
drivers/tty/tty_mutex.c
drivers/video/backlight/Kconfig
drivers/video/backlight/Makefile
drivers/video/backlight/adp5520_bl.c
drivers/video/backlight/adp8860_bl.c
drivers/video/backlight/adp8870_bl.c
drivers/video/backlight/ams369fg06.c
drivers/video/backlight/apple_bl.c
drivers/video/backlight/backlight.c
drivers/video/backlight/corgi_lcd.c
drivers/video/backlight/cr_bllcd.c
drivers/video/backlight/da903x_bl.c
drivers/video/backlight/generic_bl.c
drivers/video/backlight/ili9320.c
drivers/video/backlight/jornada720_bl.c
drivers/video/backlight/jornada720_lcd.c
drivers/video/backlight/l4f00242t03.c
drivers/video/backlight/lcd.c
drivers/video/backlight/ld9040.c
drivers/video/backlight/lm3533_bl.c [new file with mode: 0644]
drivers/video/backlight/lms283gf05.c
drivers/video/backlight/ltv350qv.c
drivers/video/backlight/omap1_bl.c
drivers/video/backlight/pcf50633-backlight.c
drivers/video/backlight/progear_bl.c
drivers/video/backlight/s6e63m0.c
drivers/video/backlight/tdo24m.c
drivers/video/backlight/tosa_bl.c
drivers/video/backlight/tosa_lcd.c
drivers/video/backlight/wm831x_bl.c
drivers/video/fbmem.c
drivers/video/matrox/matroxfb_maven.c
drivers/video/omap2/displays/panel-acx565akm.c
drivers/watchdog/Kconfig
drivers/watchdog/Makefile
drivers/watchdog/da9052_wdt.c [new file with mode: 0644]
drivers/watchdog/iTCO_vendor.h
drivers/watchdog/iTCO_vendor_support.c
drivers/watchdog/iTCO_wdt.c
drivers/watchdog/lantiq_wdt.c
drivers/watchdog/sp805_wdt.c
drivers/watchdog/via_wdt.c
drivers/watchdog/watchdog_core.c
drivers/watchdog/watchdog_core.h [new file with mode: 0644]
drivers/watchdog/watchdog_dev.c
drivers/watchdog/watchdog_dev.h [deleted file]
drivers/xen/xen-acpi-processor.c
fs/9p/vfs_inode_dotl.c
fs/affs/affs.h
fs/aio.c
fs/attr.c
fs/bad_inode.c
fs/binfmt_elf.c
fs/binfmt_flat.c
fs/bio.c
fs/btrfs/acl.c
fs/btrfs/backref.c
fs/btrfs/backref.h
fs/btrfs/btrfs_inode.h
fs/btrfs/check-integrity.c
fs/btrfs/ctree.c
fs/btrfs/ctree.h
fs/btrfs/delayed-inode.c
fs/btrfs/delayed-ref.c
fs/btrfs/delayed-ref.h
fs/btrfs/disk-io.c
fs/btrfs/disk-io.h
fs/btrfs/export.c
fs/btrfs/extent-tree.c
fs/btrfs/extent_io.c
fs/btrfs/extent_io.h
fs/btrfs/file.c
fs/btrfs/free-space-cache.c
fs/btrfs/inode.c
fs/btrfs/ioctl.c
fs/btrfs/ioctl.h
fs/btrfs/ordered-data.c
fs/btrfs/ordered-data.h
fs/btrfs/print-tree.c
fs/btrfs/reada.c
fs/btrfs/scrub.c
fs/btrfs/super.c
fs/btrfs/transaction.c
fs/btrfs/tree-log.c
fs/btrfs/ulist.c
fs/btrfs/ulist.h
fs/btrfs/volumes.c
fs/btrfs/volumes.h
fs/btrfs/xattr.c
fs/buffer.c
fs/ceph/export.c
fs/ceph/file.c
fs/ceph/ioctl.c
fs/ceph/ioctl.h
fs/ceph/mds_client.c
fs/ceph/mds_client.h
fs/ceph/snap.c
fs/ceph/xattr.c
fs/cifs/Kconfig
fs/cifs/Makefile
fs/cifs/README
fs/cifs/cifs_debug.c
fs/cifs/cifs_debug.h
fs/cifs/cifsfs.c
fs/cifs/cifsglob.h
fs/cifs/cifsproto.h
fs/cifs/cifssmb.c
fs/cifs/connect.c
fs/cifs/file.c
fs/cifs/ioctl.c
fs/cifs/misc.c
fs/cifs/readdir.c
fs/cifs/smb1ops.c [new file with mode: 0644]
fs/cifs/smb2ops.c [new file with mode: 0644]
fs/cifs/transport.c
fs/compat.c
fs/dcache.c
fs/direct-io.c
fs/ecryptfs/inode.c
fs/eventfd.c
fs/eventpoll.c
fs/exec.c
fs/exportfs/expfs.c
fs/ext4/Kconfig
fs/ext4/balloc.c
fs/ext4/bitmap.c
fs/ext4/dir.c
fs/ext4/ext4.h
fs/ext4/ext4_extents.h
fs/ext4/ext4_jbd2.c
fs/ext4/ext4_jbd2.h
fs/ext4/extents.c
fs/ext4/file.c
fs/ext4/ialloc.c
fs/ext4/inode.c
fs/ext4/ioctl.c
fs/ext4/mballoc.c
fs/ext4/mmp.c
fs/ext4/namei.c
fs/ext4/resize.c
fs/ext4/super.c
fs/ext4/xattr.c
fs/ext4/xattr.h
fs/fat/dir.c
fs/fat/fat.h
fs/fat/fatent.c
fs/fat/inode.c
fs/fcntl.c
fs/file_table.c
fs/fuse/file.c
fs/fuse/inode.c
fs/gfs2/export.c
fs/hpfs/alloc.c
fs/hpfs/anode.c
fs/hpfs/buffer.c
fs/hpfs/dir.c
fs/hpfs/dnode.c
fs/hpfs/ea.c
fs/hpfs/hpfs.h
fs/hpfs/hpfs_fn.h
fs/hpfs/inode.c
fs/hpfs/map.c
fs/hpfs/namei.c
fs/hpfs/super.c
fs/inode.c
fs/internal.h
fs/ioprio.c
fs/isofs/export.c
fs/jbd2/Kconfig
fs/jbd2/commit.c
fs/jbd2/journal.c
fs/jbd2/recovery.c
fs/jbd2/revoke.c
fs/jbd2/transaction.c
fs/jffs2/jffs2_fs_sb.h
fs/jffs2/os-linux.h
fs/jffs2/super.c
fs/jffs2/wbuf.c
fs/lockd/clntlock.c
fs/lockd/svc.c
fs/locks.c
fs/namei.c
fs/namespace.c
fs/ncpfs/file.c
fs/ncpfs/ncp_fs_sb.h
fs/nfs/Kconfig
fs/nfs/Makefile
fs/nfs/blocklayout/blocklayout.c
fs/nfs/blocklayout/blocklayoutdev.c
fs/nfs/callback.c
fs/nfs/client.c
fs/nfs/delegation.c
fs/nfs/delegation.h
fs/nfs/dir.c
fs/nfs/direct.c
fs/nfs/file.c
fs/nfs/fscache.c
fs/nfs/fscache.h
fs/nfs/getroot.c
fs/nfs/idmap.c
fs/nfs/inode.c
fs/nfs/internal.h
fs/nfs/namespace.c
fs/nfs/netns.h
fs/nfs/nfs2xdr.c
fs/nfs/nfs3proc.c
fs/nfs/nfs3xdr.c
fs/nfs/nfs4_fs.h
fs/nfs/nfs4filelayout.c
fs/nfs/nfs4filelayout.h
fs/nfs/nfs4filelayoutdev.c
fs/nfs/nfs4namespace.c
fs/nfs/nfs4proc.c
fs/nfs/nfs4renewd.c
fs/nfs/nfs4state.c
fs/nfs/nfs4xdr.c
fs/nfs/objlayout/objio_osd.c
fs/nfs/objlayout/objlayout.c
fs/nfs/pagelist.c
fs/nfs/pnfs.c
fs/nfs/pnfs.h
fs/nfs/proc.c
fs/nfs/read.c
fs/nfs/super.c
fs/nfs/write.c
fs/nfsd/auth.c
fs/nfsd/export.c
fs/nfsd/fault_inject.c
fs/nfsd/idmap.h
fs/nfsd/netns.h
fs/nfsd/nfs4callback.c
fs/nfsd/nfs4idmap.c
fs/nfsd/nfs4recover.c
fs/nfsd/nfs4state.c
fs/nfsd/nfs4xdr.c
fs/nfsd/nfsctl.c
fs/nfsd/nfsfh.c
fs/nfsd/nfssvc.c
fs/nfsd/state.h
fs/nfsd/vfs.c
fs/nfsd/xdr4.h
fs/nilfs2/file.c
fs/nilfs2/ioctl.c
fs/nilfs2/namei.c
fs/nls/Kconfig
fs/nls/Makefile
fs/nls/nls_macceltic.c [new file with mode: 0644]
fs/nls/nls_maccenteuro.c [new file with mode: 0644]
fs/nls/nls_maccroatian.c [new file with mode: 0644]
fs/nls/nls_maccyrillic.c [new file with mode: 0644]
fs/nls/nls_macgaelic.c [new file with mode: 0644]
fs/nls/nls_macgreek.c [new file with mode: 0644]
fs/nls/nls_maciceland.c [new file with mode: 0644]
fs/nls/nls_macinuit.c [new file with mode: 0644]
fs/nls/nls_macroman.c [new file with mode: 0644]
fs/nls/nls_macromanian.c [new file with mode: 0644]
fs/nls/nls_macturkish.c [new file with mode: 0644]
fs/notify/fsnotify.c
fs/ntfs/file.c
fs/ocfs2/blockcheck.c
fs/ocfs2/dlm/dlmast.c
fs/ocfs2/dlm/dlmcommon.h
fs/ocfs2/dlm/dlmdomain.c
fs/ocfs2/export.c
fs/ocfs2/inode.c
fs/ocfs2/ioctl.c
fs/ocfs2/move_extents.c
fs/ocfs2/namei.c
fs/ocfs2/symlink.c
fs/ocfs2/symlink.h
fs/open.c
fs/pipe.c
fs/pnode.c
fs/proc/array.c
fs/proc/base.c
fs/proc/internal.h
fs/proc/task_mmu.c
fs/proc/task_nommu.c
fs/proc_namespace.c
fs/read_write.c
fs/readdir.c
fs/reiserfs/inode.c
fs/reiserfs/journal.c
fs/reiserfs/reiserfs.h
fs/reiserfs/resize.c
fs/reiserfs/super.c
fs/select.c
fs/signalfd.c
fs/splice.c
fs/statfs.c
fs/sync.c
fs/ubifs/dir.c
fs/udf/namei.c
fs/utimes.c
fs/xattr.c
fs/xfs/kmem.c
fs/xfs/kmem.h
fs/xfs/xfs_export.c
fs/xfs/xfs_file.c
fs/xfs/xfs_log.c
fs/xfs/xfs_log_priv.h
fs/xfs/xfs_trans.c
fs/xfs/xfs_trans.h
include/asm-generic/Kbuild
include/asm-generic/bitsperlong.h
include/asm-generic/pgtable.h
include/asm-generic/posix_types.h
include/drm/drm_mem_util.h
include/linux/Kbuild
include/linux/apple_bl.h
include/linux/bio.h
include/linux/blk_types.h
include/linux/blkdev.h
include/linux/bootmem.h
include/linux/bug.h
include/linux/ceph/auth.h
include/linux/ceph/ceph_fs.h
include/linux/ceph/decode.h
include/linux/ceph/messenger.h
include/linux/ceph/osd_client.h
include/linux/ceph/osdmap.h
include/linux/compaction.h
include/linux/compat.h
include/linux/cpu.h
include/linux/cred.h
include/linux/crush/crush.h
include/linux/crush/mapper.h
include/linux/dmaengine.h
include/linux/drbd.h
include/linux/drbd_limits.h
include/linux/drbd_nl.h
include/linux/edac.h
include/linux/elevator.h
include/linux/errno.h
include/linux/eventfd.h
include/linux/exportfs.h
include/linux/fb.h
include/linux/fs.h
include/linux/fsnotify_backend.h
include/linux/genetlink.h
include/linux/huge_mm.h
include/linux/i2c.h
include/linux/interrupt.h
include/linux/iocontext.h
include/linux/iommu.h
include/linux/ioprio.h
include/linux/ipc_namespace.h
include/linux/jbd2.h
include/linux/jbd_common.h
include/linux/kallsyms.h
include/linux/kcmp.h [new file with mode: 0644]
include/linux/kernel-page-flags.h
include/linux/kernel.h
include/linux/kexec.h
include/linux/key.h
include/linux/kmod.h
include/linux/lcd.h
include/linux/led-lm3530.h
include/linux/leds.h
include/linux/lglock.h
include/linux/lockd/bind.h
include/linux/memcontrol.h
include/linux/mempolicy.h
include/linux/mfd/abx500/ab8500.h
include/linux/mfd/anatop.h
include/linux/mfd/asic3.h
include/linux/mfd/da9052/da9052.h
include/linux/mfd/lm3533.h [new file with mode: 0644]
include/linux/mfd/lpc_ich.h [new file with mode: 0644]
include/linux/mfd/max77693-private.h [new file with mode: 0644]
include/linux/mfd/max77693.h [new file with mode: 0644]
include/linux/mfd/sta2x11-mfd.h [new file with mode: 0644]
include/linux/mfd/stmpe.h
include/linux/mfd/tps65910.h
include/linux/mfd/twl6040.h
include/linux/mfd/wm831x/core.h
include/linux/mfd/wm8350/core.h
include/linux/mfd/wm8400-private.h
include/linux/mfd/wm8994/core.h
include/linux/mfd/wm8994/registers.h
include/linux/mm.h
include/linux/mm_inline.h
include/linux/mm_types.h
include/linux/mmdebug.h
include/linux/mmzone.h
include/linux/msdos_fs.h
include/linux/net.h
include/linux/netdevice.h
include/linux/nfs4.h
include/linux/nfs_fs.h
include/linux/nfs_fs_sb.h
include/linux/nfs_page.h
include/linux/nfs_xdr.h
include/linux/nfsd/export.h
include/linux/of_pci.h
include/linux/oom.h
include/linux/pagemap.h
include/linux/pci.h
include/linux/pci_ids.h
include/linux/power/charger-manager.h
include/linux/power/max17042_battery.h
include/linux/power_supply.h
include/linux/prctl.h
include/linux/res_counter.h
include/linux/rio.h
include/linux/rio_drv.h
include/linux/rmap.h
include/linux/rtc.h
include/linux/rtc/ds1307.h [new file with mode: 0644]
include/linux/sched.h
include/linux/security.h
include/linux/signal.h
include/linux/skbuff.h
include/linux/slab.h
include/linux/sunrpc/svc.h
include/linux/sunrpc/svcauth.h
include/linux/sunrpc/svcauth_gss.h
include/linux/swap.h
include/linux/syscalls.h
include/linux/task_work.h [new file with mode: 0644]
include/linux/thread_info.h
include/linux/tracehook.h
include/linux/types.h
include/linux/watchdog.h
include/net/dst.h
include/net/sock.h
include/scsi/fcoe_sysfs.h [new file with mode: 0644]
include/scsi/libfcoe.h
include/trace/events/vmscan.h
init/Kconfig
init/do_mounts.c
init/do_mounts_initrd.c
init/do_mounts_md.c
init/do_mounts_rd.c
init/initramfs.c
ipc/mq_sysctl.c
ipc/mqueue.c
ipc/shm.c
kernel/Makefile
kernel/cgroup.c
kernel/cpu.c
kernel/cpu_pm.c
kernel/cred.c
kernel/exit.c
kernel/fork.c
kernel/irq/manage.c
kernel/kallsyms.c
kernel/kcmp.c [new file with mode: 0644]
kernel/kmod.c
kernel/lglock.c [new file with mode: 0644]
kernel/pid_namespace.c
kernel/res_counter.c
kernel/resource.c
kernel/signal.c
kernel/sys.c
kernel/sys_ni.c
kernel/task_work.c [new file with mode: 0644]
kernel/trace/ring_buffer.c
lib/bitmap.c
lib/dma-debug.c
lib/list_debug.c
lib/radix-tree.c
lib/spinlock_debug.c
lib/string_helpers.c
lib/swiotlb.c
lib/test-kstrtox.c
lib/vsprintf.c
mm/Kconfig
mm/Makefile
mm/bootmem.c
mm/cleancache.c
mm/compaction.c
mm/filemap.c
mm/filemap_xip.c
mm/huge_memory.c
mm/hugetlb.c
mm/internal.h
mm/madvise.c
mm/memblock.c
mm/memcontrol.c
mm/memory-failure.c
mm/memory.c
mm/memory_hotplug.c
mm/mempolicy.c
mm/mmap.c
mm/mmzone.c
mm/mremap.c
mm/nobootmem.c
mm/nommu.c
mm/oom_kill.c
mm/page_alloc.c
mm/process_vm_access.c
mm/readahead.c
mm/rmap.c
mm/shmem.c
mm/sparse.c
mm/swap.c
mm/swapfile.c
mm/thrash.c [deleted file]
mm/truncate.c
mm/util.c
mm/vmalloc.c
mm/vmscan.c
mm/vmstat.c
net/ceph/auth_none.c
net/ceph/auth_x.c
net/ceph/crush/crush.c
net/ceph/crush/mapper.c
net/ceph/messenger.c
net/ceph/osd_client.c
net/ceph/osdmap.c
net/core/drop_monitor.c
net/ipv4/esp4.c
net/ipv4/tcp_memcontrol.c
net/ipv6/esp6.c
net/ipv6/ip6_output.c
net/l2tp/l2tp_ip.c
net/l2tp/l2tp_ip6.c
net/l2tp/l2tp_netlink.c
net/mac80211/mlme.c
net/mac80211/tx.c
net/mac80211/util.c
net/netlink/genetlink.c
net/rds/ib.h
net/sched/sch_atm.c
net/sunrpc/auth_gss/gss_krb5_wrap.c
net/sunrpc/auth_gss/svcauth_gss.c
net/sunrpc/clnt.c
net/sunrpc/rpc_pipe.c
net/sunrpc/rpcb_clnt.c
net/sunrpc/svc.c
net/sunrpc/svc_xprt.c
net/sunrpc/svcauth_unix.c
net/sunrpc/xprt.c
net/wanrouter/Kconfig
net/xfrm/xfrm_policy.c
scripts/checkpatch.pl
security/apparmor/lsm.c
security/capability.c
security/commoncap.c
security/keys/compat.c
security/keys/internal.h
security/keys/keyctl.c
security/keys/process_keys.c
security/keys/request_key.c
security/security.c
security/selinux/hooks.c
security/selinux/selinuxfs.c
security/smack/smack_lsm.c
sound/pci/rme9652/hdspm.c
sound/soc/fsl/imx-ssi.c
sound/soc/sh/fsi.c
sound/usb/pcm.c
tools/lib/traceevent/event-parse.c
tools/lib/traceevent/parse-filter.c
tools/perf/Documentation/perfconfig.example
tools/perf/Makefile
tools/perf/builtin-annotate.c
tools/perf/builtin-evlist.c
tools/perf/builtin-record.c
tools/perf/builtin-report.c
tools/perf/builtin-top.c
tools/perf/perf.h
tools/perf/ui/browser.c
tools/perf/ui/browser.h
tools/perf/ui/browsers/annotate.c
tools/perf/ui/browsers/hists.c
tools/perf/ui/setup.c
tools/perf/util/config.c
tools/perf/util/evsel.c
tools/perf/util/evsel.h
tools/perf/util/parse-events.c
tools/perf/util/thread_map.c
tools/testing/selftests/Makefile
tools/testing/selftests/kcmp/Makefile [new file with mode: 0644]
tools/testing/selftests/kcmp/kcmp_test.c [new file with mode: 0644]
tools/testing/selftests/mqueue/.gitignore [new file with mode: 0644]
tools/testing/selftests/mqueue/Makefile [new file with mode: 0644]
tools/testing/selftests/mqueue/mq_open_tests.c [new file with mode: 0644]
tools/testing/selftests/mqueue/mq_perf_tests.c [new file with mode: 0644]
tools/vm/page-types.c
usr/Kconfig

index 9b0d0267a3c3f1ea75a674fe858fac2165a8b683..2909c33bc54e231057fe06852d249d52f4439b15 100644 (file)
--- a/.mailmap
+++ b/.mailmap
@@ -113,3 +113,5 @@ Uwe Kleine-König <Uwe.Kleine-Koenig@digi.com>
 Valdis Kletnieks <Valdis.Kletnieks@vt.edu>
 Takashi YOSHII <takashi.yoshii.zj@renesas.com>
 Yusuke Goda <goda.yusuke@renesas.com>
+Gustavo Padovan <gustavo@las.ic.unicamp.br>
+Gustavo Padovan <padovan@profusion.mobi>
index d535757799feda3d447c2ddbbed201794cea9279..679ce354312281846e4ef2decee0689b0775c985 100644 (file)
@@ -6,13 +6,21 @@ Description:    This is a read-only file. Dumps below driver information and
                 hardware registers.
                     - S ACTive
                     - Command Issue
-                    - Allocated
                     - Completed
                     - PORT IRQ STAT
                     - HOST IRQ STAT
+                    - Allocated
+                    - Commands in Q
 
 What:           /sys/block/rssd*/status
 Date:           April 2012
 KernelVersion:  3.4
 Contact:        Asai Thambi S P <asamymuthupa@micron.com>
-Description:   This is a read-only file. Indicates the status of the device.
+Description:    This is a read-only file. Indicates the status of the device.
+
+What:           /sys/block/rssd*/flags
+Date:           May 2012
+KernelVersion:  3.5
+Contact:        Asai Thambi S P <asamymuthupa@micron.com>
+Description:    This is a read-only file. Dumps the flags in port and driver
+                data structure
diff --git a/Documentation/ABI/testing/sysfs-bus-fcoe b/Documentation/ABI/testing/sysfs-bus-fcoe
new file mode 100644 (file)
index 0000000..469d09c
--- /dev/null
@@ -0,0 +1,77 @@
+What:          /sys/bus/fcoe/ctlr_X
+Date:          March 2012
+KernelVersion: TBD
+Contact:       Robert Love <robert.w.love@intel.com>, devel@open-fcoe.org
+Description:   'FCoE Controller' instances on the fcoe bus
+Attributes:
+
+       fcf_dev_loss_tmo: Device loss timeout peroid (see below). Changing
+                         this value will change the dev_loss_tmo for all
+                         FCFs discovered by this controller.
+
+       lesb_link_fail:   Link Error Status Block (LESB) link failure count.
+
+       lesb_vlink_fail:  Link Error Status Block (LESB) virtual link
+                         failure count.
+
+       lesb_miss_fka:    Link Error Status Block (LESB) missed FCoE
+                         Initialization Protocol (FIP) Keep-Alives (FKA).
+
+       lesb_symb_err:    Link Error Status Block (LESB) symbolic error count.
+
+       lesb_err_block:   Link Error Status Block (LESB) block error count.
+
+       lesb_fcs_error:   Link Error Status Block (LESB) Fibre Channel
+                         Serivces error count.
+
+Notes: ctlr_X (global increment starting at 0)
+
+What:          /sys/bus/fcoe/fcf_X
+Date:          March 2012
+KernelVersion: TBD
+Contact:       Robert Love <robert.w.love@intel.com>, devel@open-fcoe.org
+Description:   'FCoE FCF' instances on the fcoe bus. A FCF is a Fibre Channel
+               Forwarder, which is a FCoE switch that can accept FCoE
+               (Ethernet) packets, unpack them, and forward the embedded
+               Fibre Channel frames into a FC fabric. It can also take
+               outbound FC frames and pack them in Ethernet packets to
+               be sent to their destination on the Ethernet segment.
+Attributes:
+
+       fabric_name: Identifies the fabric that the FCF services.
+
+       switch_name: Identifies the FCF.
+
+       priority:    The switch's priority amongst other FCFs on the same
+                    fabric.
+
+       selected:    1 indicates that the switch has been selected for use;
+                    0 indicates that the swich will not be used.
+
+       fc_map:      The Fibre Channel MAP
+
+       vfid:        The Virtual Fabric ID
+
+       mac:         The FCF's MAC address
+
+       fka_peroid:  The FIP Keep-Alive peroid
+
+       fabric_state: The internal kernel state
+                     "Unknown" - Initialization value
+                     "Disconnected" - No link to the FCF/fabric
+                     "Connected" - Host is connected to the FCF
+                     "Deleted" - FCF is being removed from the system
+
+       dev_loss_tmo: The device loss timeout peroid for this FCF.
+
+Notes: A device loss infrastructre similar to the FC Transport's
+       is present in fcoe_sysfs. It is nice to have so that a
+       link flapping adapter doesn't continually advance the count
+       used to identify the discovered FCF. FCFs will exist in a
+       "Disconnected" state until either the timer expires and the
+       FCF becomes "Deleted" or the FCF is rediscovered and becomes
+       "Connected."
+
+
+Users: The first user of this interface will be the fcoeadm application,
+       which is commonly packaged in the fcoe-utils package.
diff --git a/Documentation/ABI/testing/sysfs-bus-i2c-devices-lm3533 b/Documentation/ABI/testing/sysfs-bus-i2c-devices-lm3533
new file mode 100644 (file)
index 0000000..1b62230
--- /dev/null
@@ -0,0 +1,15 @@
+What:          /sys/bus/i2c/devices/.../output_hvled[n]
+Date:          April 2012
+KernelVersion: 3.5
+Contact:       Johan Hovold <jhovold@gmail.com>
+Description:
+               Set the controlling backlight device for high-voltage current
+               sink HVLED[n] (n = 1, 2) (0, 1).
+
+What:          /sys/bus/i2c/devices/.../output_lvled[n]
+Date:          April 2012
+KernelVersion: 3.5
+Contact:       Johan Hovold <jhovold@gmail.com>
+Description:
+               Set the controlling led device for low-voltage current sink
+               LVLED[n] (n = 1..5) (0..3).
index dbedafb095e24d3d3a8e2d93b6cbd727268d1754..bcd88eb7ebcd240abcdbe0ef34f4a517f0f404bd 100644 (file)
@@ -65,11 +65,11 @@ snap_*
 Entries under /sys/bus/rbd/devices/<dev-id>/snap_<snap-name>
 -------------------------------------------------------------
 
-id
+snap_id
 
        The rados internal snapshot id assigned for this snapshot
 
-size
+snap_size
 
        The size of the image when this snapshot was taken.
 
diff --git a/Documentation/ABI/testing/sysfs-class-backlight-driver-lm3533 b/Documentation/ABI/testing/sysfs-class-backlight-driver-lm3533
new file mode 100644 (file)
index 0000000..77cf7ac
--- /dev/null
@@ -0,0 +1,48 @@
+What:          /sys/class/backlight/<backlight>/als_channel
+Date:          May 2012
+KernelVersion: 3.5
+Contact:       Johan Hovold <jhovold@gmail.com>
+Description:
+               Get the ALS output channel used as input in
+               ALS-current-control mode (0, 1), where
+
+               0 - out_current0 (backlight 0)
+               1 - out_current1 (backlight 1)
+
+What:          /sys/class/backlight/<backlight>/als_en
+Date:          May 2012
+KernelVersion: 3.5
+Contact:       Johan Hovold <jhovold@gmail.com>
+Description:
+               Enable ALS-current-control mode (0, 1).
+
+What:          /sys/class/backlight/<backlight>/id
+Date:          April 2012
+KernelVersion: 3.5
+Contact:       Johan Hovold <jhovold@gmail.com>
+Description:
+               Get the id of this backlight (0, 1).
+
+What:          /sys/class/backlight/<backlight>/linear
+Date:          April 2012
+KernelVersion: 3.5
+Contact:       Johan Hovold <jhovold@gmail.com>
+Description:
+               Set the brightness-mapping mode (0, 1), where
+
+               0 - exponential mode
+               1 - linear mode
+
+What:          /sys/class/backlight/<backlight>/pwm
+Date:          April 2012
+KernelVersion: 3.5
+Contact:       Johan Hovold <jhovold@gmail.com>
+Description:
+               Set the PWM-input control mask (5 bits), where
+
+               bit 5 - PWM-input enabled in Zone 4
+               bit 4 - PWM-input enabled in Zone 3
+               bit 3 - PWM-input enabled in Zone 2
+               bit 2 - PWM-input enabled in Zone 1
+               bit 1 - PWM-input enabled in Zone 0
+               bit 0 - PWM-input enabled
diff --git a/Documentation/ABI/testing/sysfs-class-led-driver-lm3533 b/Documentation/ABI/testing/sysfs-class-led-driver-lm3533
new file mode 100644 (file)
index 0000000..620ebb3
--- /dev/null
@@ -0,0 +1,65 @@
+What:          /sys/class/leds/<led>/als_channel
+Date:          May 2012
+KernelVersion: 3.5
+Contact:       Johan Hovold <jhovold@gmail.com>
+Description:
+               Set the ALS output channel to use as input in
+               ALS-current-control mode (1, 2), where
+
+               1 - out_current1
+               2 - out_current2
+
+What:          /sys/class/leds/<led>/als_en
+Date:          May 2012
+KernelVersion: 3.5
+Contact:       Johan Hovold <jhovold@gmail.com>
+Description:
+               Enable ALS-current-control mode (0, 1).
+
+What:          /sys/class/leds/<led>/falltime
+What:          /sys/class/leds/<led>/risetime
+Date:          April 2012
+KernelVersion: 3.5
+Contact:       Johan Hovold <jhovold@gmail.com>
+Description:
+               Set the pattern generator fall and rise times (0..7), where
+
+               0 - 2048 us
+               1 - 262 ms
+               2 - 524 ms
+               3 - 1.049 s
+               4 - 2.097 s
+               5 - 4.194 s
+               6 - 8.389 s
+               7 - 16.78 s
+
+What:          /sys/class/leds/<led>/id
+Date:          April 2012
+KernelVersion: 3.5
+Contact:       Johan Hovold <jhovold@gmail.com>
+Description:
+               Get the id of this led (0..3).
+
+What:          /sys/class/leds/<led>/linear
+Date:          April 2012
+KernelVersion: 3.5
+Contact:       Johan Hovold <jhovold@gmail.com>
+Description:
+               Set the brightness-mapping mode (0, 1), where
+
+               0 - exponential mode
+               1 - linear mode
+
+What:          /sys/class/leds/<led>/pwm
+Date:          April 2012
+KernelVersion: 3.5
+Contact:       Johan Hovold <jhovold@gmail.com>
+Description:
+               Set the PWM-input control mask (5 bits), where
+
+               bit 5 - PWM-input enabled in Zone 4
+               bit 4 - PWM-input enabled in Zone 3
+               bit 3 - PWM-input enabled in Zone 2
+               bit 2 - PWM-input enabled in Zone 1
+               bit 1 - PWM-input enabled in Zone 0
+               bit 0 - PWM-input enabled
index c58b236bbe0467938e601e498008d4856bbbce52..cb9258b8fd35b25b8ac750b18b4237204213fbd4 100644 (file)
@@ -671,8 +671,9 @@ ones already enabled by DEBUG.
                Chapter 14: Allocating memory
 
 The kernel provides the following general purpose memory allocators:
-kmalloc(), kzalloc(), kcalloc(), vmalloc(), and vzalloc().  Please refer to
-the API documentation for further information about them.
+kmalloc(), kzalloc(), kmalloc_array(), kcalloc(), vmalloc(), and
+vzalloc().  Please refer to the API documentation for further information
+about them.
 
 The preferred form for passing a size of a struct is the following:
 
@@ -686,6 +687,17 @@ Casting the return value which is a void pointer is redundant. The conversion
 from void pointer to any other pointer type is guaranteed by the C programming
 language.
 
+The preferred form for allocating an array is the following:
+
+       p = kmalloc_array(n, sizeof(...), ...);
+
+The preferred form for allocating a zeroed array is the following:
+
+       p = kcalloc(n, sizeof(...), ...);
+
+Both forms check for overflow on the allocation size n * sizeof(...),
+and return NULL if that occurred.
+
 
                Chapter 15: The inline disease
 
index 9b1067afb2245f702d962ca4a93c241af6641a02..dd88540bb995e88dab868e1f42cbd218b888d7bd 100644 (file)
@@ -184,12 +184,14 @@ behind this approach is that a cgroup that aggressively uses a shared
 page will eventually get charged for it (once it is uncharged from
 the cgroup that brought it in -- this will happen on memory pressure).
 
+But see section 8.2: when moving a task to another cgroup, its pages may
+be recharged to the new cgroup, if move_charge_at_immigrate has been chosen.
+
 Exception: If CONFIG_CGROUP_CGROUP_MEM_RES_CTLR_SWAP is not used.
 When you do swapoff and make swapped-out pages of shmem(tmpfs) to
 be backed into memory in force, charges for pages are accounted against the
 caller of swapoff rather than the users of shmem.
 
-
 2.4 Swap Extension (CONFIG_CGROUP_MEM_RES_CTLR_SWAP)
 
 Swap Extension allows you to record charge for swap. A swapped-in page is
@@ -374,14 +376,15 @@ cgroup might have some charge associated with it, even though all
 tasks have migrated away from it. (because we charge against pages, not
 against tasks.)
 
-Such charges are freed or moved to their parent. At moving, both of RSS
-and CACHES are moved to parent.
-rmdir() may return -EBUSY if freeing/moving fails. See 5.1 also.
+We move the stats to root (if use_hierarchy==0) or parent (if
+use_hierarchy==1), and no change on the charge except uncharging
+from the child.
 
 Charges recorded in swap information is not updated at removal of cgroup.
 Recorded information is discarded and a cgroup which uses swap (swapcache)
 will be charged as a new owner of it.
 
+About use_hierarchy, see Section 6.
 
 5. Misc. interfaces.
 
@@ -394,13 +397,15 @@ will be charged as a new owner of it.
 
   Almost all pages tracked by this memory cgroup will be unmapped and freed.
   Some pages cannot be freed because they are locked or in-use. Such pages are
-  moved to parent and this cgroup will be empty. This may return -EBUSY if
-  VM is too busy to free/move all pages immediately.
+  moved to parent(if use_hierarchy==1) or root (if use_hierarchy==0) and this
+  cgroup will be empty.
 
   Typical use case of this interface is that calling this before rmdir().
   Because rmdir() moves all pages to parent, some out-of-use page caches can be
   moved to the parent. If you want to avoid that, force_empty will be useful.
 
+  About use_hierarchy, see Section 6.
+
 5.2 stat file
 
 memory.stat file includes following statistics
@@ -430,17 +435,10 @@ hierarchical_memory_limit - # of bytes of memory limit with regard to hierarchy
 hierarchical_memsw_limit - # of bytes of memory+swap limit with regard to
                        hierarchy under which memory cgroup is.
 
-total_cache            - sum of all children's "cache"
-total_rss              - sum of all children's "rss"
-total_mapped_file      - sum of all children's "cache"
-total_pgpgin           - sum of all children's "pgpgin"
-total_pgpgout          - sum of all children's "pgpgout"
-total_swap             - sum of all children's "swap"
-total_inactive_anon    - sum of all children's "inactive_anon"
-total_active_anon      - sum of all children's "active_anon"
-total_inactive_file    - sum of all children's "inactive_file"
-total_active_file      - sum of all children's "active_file"
-total_unevictable      - sum of all children's "unevictable"
+total_<counter>                - # hierarchical version of <counter>, which in
+                       addition to the cgroup's own value includes the
+                       sum of all hierarchical children's values of
+                       <counter>, i.e. total_cache
 
 # The following additional stats are dependent on CONFIG_DEBUG_VM.
 
@@ -622,8 +620,7 @@ memory cgroup.
   bit | what type of charges would be moved ?
  -----+------------------------------------------------------------------------
    0  | A charge of an anonymous page(or swap of it) used by the target task.
-      | Those pages and swaps must be used only by the target task. You must
-      | enable Swap Extension(see 2.4) to enable move of swap charges.
+      | You must enable Swap Extension(see 2.4) to enable move of swap charges.
  -----+------------------------------------------------------------------------
    1  | A charge of file pages(normal file, tmpfs file(e.g. ipc shared memory)
       | and swaps of tmpfs file) mmapped by the target task. Unlike the case of
@@ -636,8 +633,6 @@ memory cgroup.
 
 8.3 TODO
 
-- Implement madvise(2) to let users decide the vma to be moved or not to be
-  moved.
 - All of moving charge operations are done under cgroup_mutex. It's not good
   behavior to hold the mutex too long, so we may need some trick.
 
index f3c4ec3626a280bfd1b7f8811c9ea2cc3502f213..0c4a344e78fa4c32693bf231240597bee5a8fe2b 100644 (file)
@@ -92,6 +92,14 @@ to work with it.
 
        The _locked routines imply that the res_counter->lock is taken.
 
+ f. void res_counter_uncharge_until
+               (struct res_counter *rc, struct res_counter *top,
+                unsinged long val)
+
+       Almost same as res_cunter_uncharge() but propagation of uncharge
+       stops when rc == top. This is useful when kill a res_coutner in
+       child cgroup.
+
  2.1 Other accounting routines
 
     There are more routines that may help you with common needs, like
diff --git a/Documentation/devicetree/bindings/gpio/gpio-mm-lantiq.txt b/Documentation/devicetree/bindings/gpio/gpio-mm-lantiq.txt
new file mode 100644 (file)
index 0000000..f93d514
--- /dev/null
@@ -0,0 +1,38 @@
+Lantiq SoC External Bus memory mapped GPIO controller
+
+By attaching hardware latches to the EBU it is possible to create output
+only gpios. This driver configures a special memory address, which when
+written to outputs 16 bit to the latches.
+
+The node describing the memory mapped GPIOs needs to be a child of the node
+describing the "lantiq,localbus".
+
+Required properties:
+- compatible : Should be "lantiq,gpio-mm-lantiq"
+- reg : Address and length of the register set for the device
+- #gpio-cells : Should be two.  The first cell is the pin number and
+  the second cell is used to specify optional parameters (currently
+  unused).
+- gpio-controller : Marks the device node as a gpio controller.
+
+Optional properties:
+- lantiq,shadow : The default value that we shall assume as already set on the
+  shift register cascade.
+
+Example:
+
+localbus@0 {
+       #address-cells = <2>;
+       #size-cells = <1>;
+       ranges = <0 0 0x0 0x3ffffff /* addrsel0 */
+               1 0 0x4000000 0x4000010>; /* addsel1 */
+       compatible = "lantiq,localbus", "simple-bus";
+
+       gpio_mm0: gpio@4000000 {
+               compatible = "lantiq,gpio-mm";
+               reg = <1 0x0 0x10>;
+               gpio-controller;
+               #gpio-cells = <2>;
+               lantiq,shadow = <0x77f>
+       };
+}
diff --git a/Documentation/devicetree/bindings/gpio/gpio-stp-xway.txt b/Documentation/devicetree/bindings/gpio/gpio-stp-xway.txt
new file mode 100644 (file)
index 0000000..854de13
--- /dev/null
@@ -0,0 +1,42 @@
+Lantiq SoC Serial To Parallel (STP) GPIO controller
+
+The Serial To Parallel (STP) is found on MIPS based Lantiq socs. It is a
+peripheral controller used to drive external shift register cascades. At most
+3 groups of 8 bits can be driven. The hardware is able to allow the DSL modem
+to drive the 2 LSBs of the cascade automatically.
+
+
+Required properties:
+- compatible : Should be "lantiq,gpio-stp-xway"
+- reg : Address and length of the register set for the device
+- #gpio-cells : Should be two.  The first cell is the pin number and
+  the second cell is used to specify optional parameters (currently
+  unused).
+- gpio-controller : Marks the device node as a gpio controller.
+
+Optional properties:
+- lantiq,shadow : The default value that we shall assume as already set on the
+  shift register cascade.
+- lantiq,groups : Set the 3 bit mask to select which of the 3 groups are enabled
+  in the shift register cascade.
+- lantiq,dsl : The dsl core can control the 2 LSBs of the gpio cascade. This 2 bit
+  property can enable this feature.
+- lantiq,phy1 : The gphy1 core can control 3 bits of the gpio cascade.
+- lantiq,phy2 : The gphy2 core can control 3 bits of the gpio cascade.
+- lantiq,rising : use rising instead of falling edge for the shift register
+
+Example:
+
+gpio1: stp@E100BB0 {
+       compatible = "lantiq,gpio-stp-xway";
+       reg = <0xE100BB0 0x40>;
+       #gpio-cells = <2>;
+       gpio-controller;
+
+       lantiq,shadow = <0xffff>;
+       lantiq,groups = <0x7>;
+       lantiq,dsl = <0x3>;
+       lantiq,phy1 = <0x7>;
+       lantiq,phy2 = <0x7>;
+       /* lantiq,rising; */
+};
diff --git a/Documentation/devicetree/bindings/iommu/nvidia,tegra20-gart.txt b/Documentation/devicetree/bindings/iommu/nvidia,tegra20-gart.txt
new file mode 100644 (file)
index 0000000..099d936
--- /dev/null
@@ -0,0 +1,14 @@
+NVIDIA Tegra 20 GART
+
+Required properties:
+- compatible: "nvidia,tegra20-gart"
+- reg: Two pairs of cells specifying the physical address and size of
+  the memory controller registers and the GART aperture respectively.
+
+Example:
+
+       gart {
+               compatible = "nvidia,tegra20-gart";
+               reg = <0x7000f024 0x00000018    /* controller registers */
+                      0x58000000 0x02000000>;  /* GART aperture */
+       };
diff --git a/Documentation/devicetree/bindings/mfd/da9052-i2c.txt b/Documentation/devicetree/bindings/mfd/da9052-i2c.txt
new file mode 100644 (file)
index 0000000..1857f4a
--- /dev/null
@@ -0,0 +1,60 @@
+* Dialog DA9052/53 Power Management Integrated Circuit (PMIC)
+
+Required properties:
+- compatible : Should be "dlg,da9052", "dlg,da9053-aa",
+                        "dlg,da9053-ab", or "dlg,da9053-bb"
+
+Sub-nodes:
+- regulators : Contain the regulator nodes. The DA9052/53 regulators are
+  bound using their names as listed below:
+
+    buck0     : regulator BUCK0
+    buck1     : regulator BUCK1
+    buck2     : regulator BUCK2
+    buck3     : regulator BUCK3
+    ldo4      : regulator LDO4
+    ldo5      : regulator LDO5
+    ldo6      : regulator LDO6
+    ldo7      : regulator LDO7
+    ldo8      : regulator LDO8
+    ldo9      : regulator LDO9
+    ldo10     : regulator LDO10
+    ldo11     : regulator LDO11
+    ldo12     : regulator LDO12
+    ldo13     : regulator LDO13
+
+  The bindings details of individual regulator device can be found in:
+  Documentation/devicetree/bindings/regulator/regulator.txt
+
+Examples:
+
+i2c@63fc8000 { /* I2C1 */
+       status = "okay";
+
+       pmic: dialog@48 {
+               compatible = "dlg,da9053-aa";
+               reg = <0x48>;
+
+               regulators {
+                       buck0 {
+                               regulator-min-microvolt = <500000>;
+                               regulator-max-microvolt = <2075000>;
+                       };
+
+                       buck1 {
+                               regulator-min-microvolt = <500000>;
+                               regulator-max-microvolt = <2075000>;
+                       };
+
+                       buck2 {
+                               regulator-min-microvolt = <925000>;
+                               regulator-max-microvolt = <2500000>;
+                       };
+
+                       buck3 {
+                               regulator-min-microvolt = <925000>;
+                               regulator-max-microvolt = <2500000>;
+                       };
+               };
+       };
+};
diff --git a/Documentation/devicetree/bindings/mfd/tps65910.txt b/Documentation/devicetree/bindings/mfd/tps65910.txt
new file mode 100644 (file)
index 0000000..645f5ea
--- /dev/null
@@ -0,0 +1,133 @@
+TPS65910 Power Management Integrated Circuit
+
+Required properties:
+- compatible: "ti,tps65910" or "ti,tps65911"
+- reg: I2C slave address
+- interrupts: the interrupt outputs of the controller
+- #gpio-cells: number of cells to describe a GPIO, this should be 2.
+  The first cell is the GPIO number.
+  The second cell is used to specify additional options <unused>.
+- gpio-controller: mark the device as a GPIO controller
+- #interrupt-cells: the number of cells to describe an IRQ, this should be 2.
+  The first cell is the IRQ number.
+  The second cell is the flags, encoded as the trigger masks from
+  Documentation/devicetree/bindings/interrupts.txt
+- regulators: This is the list of child nodes that specify the regulator
+  initialization data for defined regulators. Not all regulators for the given
+  device need to be present. The definition for each of these nodes is defined
+  using the standard binding for regulators found at
+  Documentation/devicetree/bindings/regulator/regulator.txt.
+
+  The valid names for regulators are:
+  tps65910: vrtc, vio, vdd1, vdd2, vdd3, vdig1, vdig2, vpll, vdac, vaux1,
+            vaux2, vaux33, vmmc
+  tps65911: vrtc, vio, vdd1, vdd3, vddctrl, ldo1, ldo2, ldo3, ldo4, ldo5,
+            ldo6, ldo7, ldo8
+
+Optional properties:
+- ti,vmbch-threshold: (tps65911) main battery charged threshold
+  comparator. (see VMBCH_VSEL in TPS65910 datasheet)
+- ti,vmbch2-threshold: (tps65911) main battery discharged threshold
+  comparator. (see VMBCH_VSEL in TPS65910 datasheet)
+- ti,en-gpio-sleep: enable sleep control for gpios
+  There should be 9 entries here, one for each gpio.
+
+Regulator Optional properties:
+- ti,regulator-ext-sleep-control: enable external sleep
+  control through external inputs [0 (not enabled), 1 (EN1), 2 (EN2) or 4(EN3)]
+  If this property is not defined, it defaults to 0 (not enabled).
+
+Example:
+
+       pmu: tps65910@d2 {
+               compatible = "ti,tps65910";
+               reg = <0xd2>;
+               interrupt-parent = <&intc>;
+               interrupts = < 0 118 0x04 >;
+
+               #gpio-cells = <2>;
+               gpio-controller;
+
+               #interrupt-cells = <2>;
+               interrupt-controller;
+
+               ti,vmbch-threshold = 0;
+               ti,vmbch2-threshold = 0;
+
+               ti,en-gpio-sleep = <0 0 1 0 0 0 0 0 0>;
+
+               regulators {
+                       vdd1_reg: vdd1 {
+                               regulator-min-microvolt = < 600000>;
+                               regulator-max-microvolt = <1500000>;
+                               regulator-always-on;
+                               regulator-boot-on;
+                               ti,regulator-ext-sleep-control = <0>;
+                       };
+                       vdd2_reg: vdd2 {
+                               regulator-min-microvolt = < 600000>;
+                               regulator-max-microvolt = <1500000>;
+                               regulator-always-on;
+                               regulator-boot-on;
+                               ti,regulator-ext-sleep-control = <4>;
+                       };
+                       vddctrl_reg: vddctrl {
+                               regulator-min-microvolt = < 600000>;
+                               regulator-max-microvolt = <1400000>;
+                               regulator-always-on;
+                               regulator-boot-on;
+                               ti,regulator-ext-sleep-control = <0>;
+                       };
+                       vio_reg: vio {
+                               regulator-min-microvolt = <1500000>;
+                               regulator-max-microvolt = <1800000>;
+                               regulator-always-on;
+                               regulator-boot-on;
+                               ti,regulator-ext-sleep-control = <1>;
+                       };
+                       ldo1_reg: ldo1 {
+                               regulator-min-microvolt = <1000000>;
+                               regulator-max-microvolt = <3300000>;
+                               ti,regulator-ext-sleep-control = <0>;
+                       };
+                       ldo2_reg: ldo2 {
+                               regulator-min-microvolt = <1050000>;
+                               regulator-max-microvolt = <1050000>;
+                               ti,regulator-ext-sleep-control = <0>;
+                       };
+                       ldo3_reg: ldo3 {
+                               regulator-min-microvolt = <1000000>;
+                               regulator-max-microvolt = <3300000>;
+                               ti,regulator-ext-sleep-control = <0>;
+                       };
+                       ldo4_reg: ldo4 {
+                               regulator-min-microvolt = <1000000>;
+                               regulator-max-microvolt = <3300000>;
+                               regulator-always-on;
+                               ti,regulator-ext-sleep-control = <0>;
+                       };
+                       ldo5_reg: ldo5 {
+                               regulator-min-microvolt = <1000000>;
+                               regulator-max-microvolt = <3300000>;
+                               ti,regulator-ext-sleep-control = <0>;
+                       };
+                       ldo6_reg: ldo6 {
+                               regulator-min-microvolt = <1200000>;
+                               regulator-max-microvolt = <1200000>;
+                               ti,regulator-ext-sleep-control = <0>;
+                       };
+                       ldo7_reg: ldo7 {
+                               regulator-min-microvolt = <1200000>;
+                               regulator-max-microvolt = <1200000>;
+                               regulator-always-on;
+                               regulator-boot-on;
+                               ti,regulator-ext-sleep-control = <1>;
+                       };
+                       ldo8_reg: ldo8 {
+                               regulator-min-microvolt = <1000000>;
+                               regulator-max-microvolt = <3300000>;
+                               regulator-always-on;
+                               ti,regulator-ext-sleep-control = <1>;
+                       };
+               };
+       };
diff --git a/Documentation/devicetree/bindings/mfd/twl6040.txt b/Documentation/devicetree/bindings/mfd/twl6040.txt
new file mode 100644 (file)
index 0000000..bc67c6f
--- /dev/null
@@ -0,0 +1,62 @@
+Texas Instruments TWL6040 family
+
+The TWL6040s are 8-channel high quality low-power audio codecs providing audio
+and vibra functionality on OMAP4+ platforms.
+They are connected ot the host processor via i2c for commands, McPDM for audio
+data and commands.
+
+Required properties:
+- compatible : Must be "ti,twl6040";
+- reg: must be 0x4b for i2c address
+- interrupts: twl6040 has one interrupt line connecteded to the main SoC
+- interrupt-parent: The parent interrupt controller
+- twl6040,audpwron-gpio: Power on GPIO line for the twl6040
+
+- vio-supply: Regulator for the twl6040 VIO supply
+- v2v1-supply: Regulator for the twl6040 V2V1 supply
+
+Optional properties, nodes:
+- enable-active-high: To power on the twl6040 during boot.
+
+Vibra functionality
+Required properties:
+- vddvibl-supply: Regulator for the left vibra motor
+- vddvibr-supply: Regulator for the right vibra motor
+- vibra { }: Configuration section for vibra parameters containing the following
+            properties:
+- ti,vibldrv-res: Resistance parameter for left driver
+- ti,vibrdrv-res: Resistance parameter for right driver
+- ti,viblmotor-res: Resistance parameter for left motor
+- ti,viblmotor-res: Resistance parameter for right motor
+
+Optional properties within vibra { } section:
+- vddvibl_uV: If the vddvibl default voltage need to be changed
+- vddvibr_uV: If the vddvibr default voltage need to be changed
+
+Example:
+&i2c1 {
+       twl6040: twl@4b {
+               compatible = "ti,twl6040";
+               reg = <0x4b>;
+
+               interrupts = <0 119 4>;
+               interrupt-parent = <&gic>;
+               twl6040,audpwron-gpio = <&gpio4 31 0>;
+
+               vio-supply = <&v1v8>;
+               v2v1-supply = <&v2v1>;
+               enable-active-high;
+
+               /* regulators for vibra motor */
+               vddvibl-supply = <&vbat>;
+               vddvibr-supply = <&vbat>;
+
+               vibra {
+                       /* Vibra driver, motor resistance parameters */
+                       ti,vibldrv-res = <8>;
+                       ti,vibrdrv-res = <3>;
+                       ti,viblmotor-res = <10>;
+                       ti,vibrmotor-res = <10>;
+               };
+       };
+};
diff --git a/Documentation/devicetree/bindings/rtc/lpc32xx-rtc.txt b/Documentation/devicetree/bindings/rtc/lpc32xx-rtc.txt
new file mode 100644 (file)
index 0000000..a87a1e9
--- /dev/null
@@ -0,0 +1,15 @@
+* NXP LPC32xx SoC Real Time Clock controller
+
+Required properties:
+- compatible: must be "nxp,lpc3220-rtc"
+- reg: physical base address of the controller and length of memory mapped
+  region.
+- interrupts: The RTC interrupt
+
+Example:
+
+       rtc@40024000 {
+               compatible = "nxp,lpc3220-rtc";
+               reg = <0x40024000 0x1000>;
+               interrupts = <52 0>;
+       };
diff --git a/Documentation/devicetree/bindings/rtc/spear-rtc.txt b/Documentation/devicetree/bindings/rtc/spear-rtc.txt
new file mode 100644 (file)
index 0000000..ca67ac6
--- /dev/null
@@ -0,0 +1,17 @@
+* SPEAr RTC
+
+Required properties:
+- compatible : "st,spear600-rtc"
+- reg : Address range of the rtc registers
+- interrupt-parent: Should be the phandle for the interrupt controller
+  that services interrupts for this device
+- interrupt: Should contain the rtc interrupt number
+
+Example:
+
+       rtc@fc000000 {
+               compatible = "st,spear600-rtc";
+               reg = <0xfc000000 0x1000>;
+               interrupt-parent = <&vic1>;
+               interrupts = <12>;
+       };
index ebaffe208ccb73e0727879ab1806830b5a66b5dc..56000b33340bbe33f0b141934c756470d32ecbbd 100644 (file)
@@ -606,3 +606,9 @@ Why:        There are two mci drivers: at91-mci and atmel-mci. The PDC support
 Who:   Ludovic Desroches <ludovic.desroches@atmel.com>
 
 ----------------------------
+
+What:  net/wanrouter/
+When:  June 2013
+Why:   Unsupported/unmaintained/unused since 2.6
+
+----------------------------
index 4fca82e5276e71726f81285ba0ffef867e917618..8e2da1e06e3b2371eb82ef07105e63ad97d224b6 100644 (file)
@@ -60,8 +60,8 @@ ata *);
        ssize_t (*getxattr) (struct dentry *, const char *, void *, size_t);
        ssize_t (*listxattr) (struct dentry *, char *, size_t);
        int (*removexattr) (struct dentry *, const char *);
-       void (*truncate_range)(struct inode *, loff_t, loff_t);
        int (*fiemap)(struct inode *, struct fiemap_extent_info *, u64 start, u64 len);
+       void (*update_time)(struct inode *, struct timespec *, int);
 
 locking rules:
        all may block
@@ -87,8 +87,9 @@ setxattr:     yes
 getxattr:      no
 listxattr:     no
 removexattr:   yes
-truncate_range:        yes
 fiemap:                no
+update_time:   no
+
        Additionally, ->rmdir(), ->unlink() and ->rename() have ->i_mutex on
 victim.
        cross-directory ->rename() has (per-superblock) ->s_vfs_rename_sem.
index ef088e55ab2ede67783855ecac4e59942b084bf2..fb0a6aeb936c86237fe19bcdf630339fc43ad348 100644 (file)
@@ -40,6 +40,7 @@ Table of Contents
   3.4  /proc/<pid>/coredump_filter - Core dump filtering settings
   3.5  /proc/<pid>/mountinfo - Information about mounts
   3.6  /proc/<pid>/comm  & /proc/<pid>/task/<tid>/comm
+  3.7   /proc/<pid>/task/<tid>/children - Information about task children
 
   4    Configuring procfs
   4.1  Mount options
@@ -310,6 +311,11 @@ Table 1-4: Contents of the stat files (as of 2.6.30-rc7)
   start_data    address above which program data+bss is placed
   end_data      address below which program data+bss is placed
   start_brk     address above which program heap can be expanded with brk()
+  arg_start     address above which program command line is placed
+  arg_end       address below which program command line is placed
+  env_start     address above which program environment is placed
+  env_end       address below which program environment is placed
+  exit_code     the thread's exit_code in the form reported by the waitpid system call
 ..............................................................................
 
 The /proc/PID/maps file containing the currently mapped memory regions and
@@ -743,6 +749,7 @@ Committed_AS:   100056 kB
 VmallocTotal:   112216 kB
 VmallocUsed:       428 kB
 VmallocChunk:   111088 kB
+AnonHugePages:   49152 kB
 
     MemTotal: Total usable ram (i.e. physical ram minus a few reserved
               bits and the kernel binary code)
@@ -776,6 +783,7 @@ VmallocChunk:   111088 kB
        Dirty: Memory which is waiting to get written back to the disk
    Writeback: Memory which is actively being written back to the disk
    AnonPages: Non-file backed pages mapped into userspace page tables
+AnonHugePages: Non-file backed huge pages mapped into userspace page tables
       Mapped: files which have been mmaped, such as libraries
         Slab: in-kernel data structures cache
 SReclaimable: Part of Slab, that might be reclaimed, such as caches
@@ -1576,6 +1584,23 @@ then the kernel's TASK_COMM_LEN (currently 16 chars) will result in a truncated
 comm value.
 
 
+3.7    /proc/<pid>/task/<tid>/children - Information about task children
+-------------------------------------------------------------------------
+This file provides a fast way to retrieve first level children pids
+of a task pointed by <pid>/<tid> pair. The format is a space separated
+stream of pids.
+
+Note the "first level" here -- if a child has own children they will
+not be listed here, one needs to read /proc/<children-pid>/task/<tid>/children
+to obtain the descendants.
+
+Since this interface is intended to be fast and cheap it doesn't
+guarantee to provide precise results and some children might be
+skipped, especially if they've exited right after we printed their
+pids, so one need to either stop or freeze processes being inspected
+if precise results are needed.
+
+
 ------------------------------------------------------------------------------
 Configuring procfs
 ------------------------------------------------------------------------------
index 0d0492028082c0ecda1a0931cc5100765624a80a..efd23f4817044ac9d55932bd9476d309a02918dc 100644 (file)
@@ -363,7 +363,7 @@ struct inode_operations {
        ssize_t (*getxattr) (struct dentry *, const char *, void *, size_t);
        ssize_t (*listxattr) (struct dentry *, char *, size_t);
        int (*removexattr) (struct dentry *, const char *);
-       void (*truncate_range)(struct inode *, loff_t, loff_t);
+       void (*update_time)(struct inode *, struct timespec *, int);
 };
 
 Again, all methods are called without any locks being held, unless
@@ -472,9 +472,9 @@ otherwise noted.
   removexattr: called by the VFS to remove an extended attribute from
        a file. This method is called by removexattr(2) system call.
 
-  truncate_range: a method provided by the underlying filesystem to truncate a
-       range of blocks , i.e. punch a hole somewhere in a file.
-
+  update_time: called by the VFS to update a specific time or the i_version of
+       an inode.  If this is not defined the VFS will update the inode itself
+       and call mark_inode_dirty_sync.
 
 The Address Space Object
 ========================
@@ -760,7 +760,7 @@ struct file_operations
 ----------------------
 
 This describes how the VFS can manipulate an open file. As of kernel
-2.6.22, the following members are defined:
+3.5, the following members are defined:
 
 struct file_operations {
        struct module *owner;
@@ -790,6 +790,8 @@ struct file_operations {
        int (*flock) (struct file *, int, struct file_lock *);
        ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, size_t, unsigned int);
        ssize_t (*splice_read)(struct file *, struct pipe_inode_info *, size_t, unsigned int);
+       int (*setlease)(struct file *, long arg, struct file_lock **);
+       long (*fallocate)(struct file *, int mode, loff_t offset, loff_t len);
 };
 
 Again, all methods are called without any locks being held, unless
@@ -858,6 +860,11 @@ otherwise noted.
   splice_read: called by the VFS to splice data from file to a pipe. This
               method is used by the splice(2) system call
 
+  setlease: called by the VFS to set or release a file lock lease.
+           setlease has the file_lock_lock held and must not sleep.
+
+  fallocate: called by the VFS to preallocate blocks or punch a hole.
+
 Note that the file operations are implemented by the specific
 filesystem in which the inode resides. When opening a device node
 (character or block special) most filesystems will call special
index 42c17c1fb3cdf74e25a12e11d4416dd41e5f1f9a..b0ff2ab596ce56d4082850d19cc446a6d0c2782e 100644 (file)
@@ -18,9 +18,9 @@ For the most up-to-date list of functionality constants, please check
                                   adapters typically can not do these)
   I2C_FUNC_10BIT_ADDR             Handles the 10-bit address extensions
   I2C_FUNC_PROTOCOL_MANGLING      Knows about the I2C_M_IGNORE_NAK,
-                                  I2C_M_REV_DIR_ADDR, I2C_M_NOSTART and
-                                  I2C_M_NO_RD_ACK flags (which modify the
-                                  I2C protocol!)
+                                  I2C_M_REV_DIR_ADDR and I2C_M_NO_RD_ACK
+                                  flags (which modify the I2C protocol!)
+  I2C_FUNC_NOSTART                Can skip repeated start sequence
   I2C_FUNC_SMBUS_QUICK            Handles the SMBus write_quick command
   I2C_FUNC_SMBUS_READ_BYTE        Handles the SMBus read_byte command
   I2C_FUNC_SMBUS_WRITE_BYTE       Handles the SMBus write_byte command
@@ -50,6 +50,9 @@ A few combinations of the above flags are also defined for your convenience:
                                   emulated by a real I2C adapter (using
                                   the transparent emulation layer)
 
+In kernel versions prior to 3.5 I2C_FUNC_NOSTART was implemented as
+part of I2C_FUNC_PROTOCOL_MANGLING.
+
 
 ADAPTER IMPLEMENTATION
 ----------------------
index 10518dd588146f6d57c1e3c9c912bb525bc86425..0b3e62d1f77a1853765338afd9ed9d6ea56902d2 100644 (file)
@@ -49,7 +49,9 @@ a byte read, followed by a byte write:
 Modified transactions
 =====================
 
-We have found some I2C devices that needs the following modifications:
+The following modifications to the I2C protocol can also be generated,
+with the exception of I2C_M_NOSTART these are usually only needed to
+work around device issues:
 
   Flag I2C_M_NOSTART: 
     In a combined transaction, no 'S Addr Wr/Rd [A]' is generated at some
@@ -60,6 +62,11 @@ We have found some I2C devices that needs the following modifications:
     we do not generate Addr, but we do generate the startbit S. This will
     probably confuse all other clients on your bus, so don't try this.
 
+    This is often used to gather transmits from multiple data buffers in
+    system memory into something that appears as a single transfer to the
+    I2C device but may also be used between direction changes by some
+    rare devices.
+
   Flags I2C_M_REV_DIR_ADDR
     This toggles the Rd/Wr flag. That is, if you want to do a write, but
     need to emit an Rd instead of a Wr, or vice versa, you set this
index b40b413db88e2aeea1f19f2200dbf6049da36206..c45513d806abc48d3e11fa756e4f11138fb179ce 100644 (file)
@@ -335,6 +335,12 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
                                          requirements as needed. This option
                                          does not override iommu=pt
 
+       amd_iommu_dump= [HW,X86-64]
+                       Enable AMD IOMMU driver option to dump the ACPI table
+                       for AMD IOMMU. With this option enabled, AMD IOMMU
+                       driver will print ACPI tables for AMD IOMMU during
+                       IOMMU initialization.
+
        amijoy.map=     [HW,JOY] Amiga joystick support
                        Map of devices attached to JOY0DAT and JOY1DAT
                        Format: <a>,<b>
diff --git a/Documentation/leds/ledtrig-transient.txt b/Documentation/leds/ledtrig-transient.txt
new file mode 100644 (file)
index 0000000..3bd38b4
--- /dev/null
@@ -0,0 +1,152 @@
+LED Transient Trigger
+=====================
+
+The leds timer trigger does not currently have an interface to activate
+a one shot timer. The current support allows for setting two timers, one for
+specifying how long a state to be on, and the second for how long the state
+to be off. The delay_on value specifies the time period an LED should stay
+in on state, followed by a delay_off value that specifies how long the LED
+should stay in off state. The on and off cycle repeats until the trigger
+gets deactivated. There is no provision for one time activation to implement
+features that require an on or off state to be held just once and then stay in
+the original state forever.
+
+Without one shot timer interface, user space can still use timer trigger to
+set a timer to hold a state, however when user space application crashes or
+goes away without deactivating the timer, the hardware will be left in that
+state permanently.
+
+As a specific example of this use-case, let's look at vibrate feature on
+phones. Vibrate function on phones is implemented using PWM pins on SoC or
+PMIC. There is a need to activate one shot timer to control the vibrate
+feature, to prevent user space crashes leaving the phone in vibrate mode
+permanently causing the battery to drain.
+
+Transient trigger addresses the need for one shot timer activation. The
+transient trigger can be enabled and disabled just like the other leds
+triggers.
+
+When an led class device driver registers itself, it can specify all leds
+triggers it supports and a default trigger. During registration, activation
+routine for the default trigger gets called. During registration of an led
+class device, the LED state does not change.
+
+When the driver unregisters, deactivation routine for the currently active
+trigger will be called, and LED state is changed to LED_OFF.
+
+Driver suspend changes the LED state to LED_OFF and resume doesn't change
+the state. Please note that there is no explicit interaction between the
+suspend and resume actions and the currently enabled trigger. LED state
+changes are suspended while the driver is in suspend state. Any timers
+that are active at the time driver gets suspended, continue to run, without
+being able to actually change the LED state. Once driver is resumed, triggers
+start functioning again.
+
+LED state changes are controlled using brightness which is a common led
+class device property. When brightness is set to 0 from user space via
+echo 0 > brightness, it will result in deactivating the current trigger.
+
+Transient trigger uses standard register and unregister interfaces. During
+trigger registration, for each led class device that specifies this trigger
+as its default trigger, trigger activation routine will get called. During
+registration, the LED state does not change, unless there is another trigger
+active, in which case LED state changes to LED_OFF.
+
+During trigger unregistration, LED state gets changed to LED_OFF.
+
+Transient trigger activation routine doesn't change the LED state. It
+creates its properties and does its initialization. Transient trigger
+deactivation routine, will cancel any timer that is active before it cleans
+up and removes the properties it created. It will restore the LED state to
+non-transient state. When driver gets suspended, irrespective of the transient
+state, the LED state changes to LED_OFF.
+
+Transient trigger can be enabled and disabled from user space on led class
+devices, that support this trigger as shown below:
+
+echo transient > trigger
+echo none > trigger
+
+NOTE: Add a new property trigger state to control the state.
+
+This trigger exports three properties, activate, state, and duration. When
+transient trigger is activated these properties are set to default values.
+
+- duration allows setting timer value in msecs. The initial value is 0.
+- activate allows activating and deactivating the timer specified by
+  duration as needed. The initial and default value is 0.  This will allow
+  duration to be set after trigger activation.
+- state allows user to specify a transient state to be held for the specified
+  duration.
+
+       activate - one shot timer activate mechanism.
+               1 when activated, 0 when deactivated.
+               default value is zero when transient trigger is enabled,
+               to allow duration to be set.
+
+               activate state indicates a timer with a value of specified
+               duration running.
+               deactivated state indicates that there is no active timer
+               running.
+
+       duration - one shot timer value. When activate is set, duration value
+               is used to start a timer that runs once. This value doesn't
+               get changed by the trigger unless user does a set via
+               echo new_value > duration
+
+       state - transient state to be held. It has two values 0 or 1. 0 maps
+               to LED_OFF and 1 maps to LED_FULL. The specified state is
+               held for the duration of the one shot timer and then the
+               state gets changed to the non-transient state which is the
+               inverse of transient state.
+               If state = LED_FULL, when the timer runs out the state will
+               go back to LED_OFF.
+               If state = LED_OFF, when the timer runs out the state will
+               go back to LED_FULL.
+               Please note that current LED state is not checked prior to
+               changing the state to the specified state.
+               Driver could map these values to inverted depending on the
+               default states it defines for the LED in its brightness_set()
+               interface which is called from the led brightness_set()
+               interfaces to control the LED state.
+
+When timer expires activate goes back to deactivated state, duration is left
+at the set value to be used when activate is set at a future time. This will
+allow user app to set the time once and activate it to run it once for the
+specified value as needed. When timer expires, state is restored to the
+non-transient state which is the inverse of the transient state.
+
+       echo 1 > activate - starts timer = duration when duration is not 0.
+       echo 0 > activate - cancels currently running timer.
+       echo n > duration - stores timer value to be used upon next
+                            activate. Currently active timer if
+                            any, continues to run for the specified time.
+       echo 0 > duration - stores timer value to be used upon next
+                            activate. Currently active timer if any,
+                            continues to run for the specified time.
+       echo 1 > state    - stores desired transient state LED_FULL to be
+                           held for the specified duration.
+       echo 0 > state    - stores desired transient state LED_OFF to be
+                           held for the specified duration.
+
+What is not supported:
+======================
+- Timer activation is one shot and extending and/or shortening the timer
+  is not supported.
+
+Example use-case 1:
+       echo transient > trigger
+       echo n > duration
+       echo 1 > state
+repeat the following step as needed:
+       echo 1 > activate - start timer = duration to run once
+       echo 1 > activate - start timer = duration to run once
+       echo none > trigger
+
+This trigger is intended to be used for for the following example use cases:
+ - Control of vibrate (phones, tablets etc.) hardware by user space app.
+ - Use of LED by user space app as activity indicator.
+ - Use of LED by user space app as a kind of watchdog indicator -- as
+       long as the app is alive, it can keep the LED illuminated, if it dies
+       the LED will be extinguished automatically.
+ - Use by any user space app that needs a transient GPIO output.
index fdcca991df3067100330da55136d25b240fde3f5..b4f7f4b23f648e3e129bf3b8d31b68e17e004ee2 100644 (file)
@@ -44,6 +44,16 @@ Charger Manager supports the following:
        Normally, the platform will need to resume and suspend some devices
        that are used by Charger Manager.
 
+* Support for premature full-battery event handling
+       If the battery voltage drops by "fullbatt_vchkdrop_uV" after
+       "fullbatt_vchkdrop_ms" from the full-battery event, the framework
+       restarts charging. This check is also performed while suspended by
+       setting wakeup time accordingly and using suspend_again.
+
+* Support for uevent-notify
+       With the charger-related events, the device sends
+       notification to users with UEVENT.
+
 2. Global Charger-Manager Data related with suspend_again
 ========================================================
 In order to setup Charger Manager with suspend-again feature
@@ -55,7 +65,7 @@ if there are multiple batteries. If there are multiple batteries, the
 multiple instances of Charger Manager share the same charger_global_desc
 and it will manage in-suspend monitoring for all instances of Charger Manager.
 
-The user needs to provide all the two entries properly in order to activate
+The user needs to provide all the three entries properly in order to activate
 in-suspend monitoring:
 
 struct charger_global_desc {
@@ -74,6 +84,11 @@ bool (*rtc_only_wakeup)(void);
        same struct. If there is any other wakeup source triggered the
        wakeup, it should return false. If the "rtc" is the only wakeup
        reason, it should return true.
+
+bool assume_timer_stops_in_suspend;
+       : if true, Charger Manager assumes that
+       the timer (CM uses jiffies as timer) stops during suspend. Then, CM
+       assumes that the suspend-duration is same as the alarm length.
 };
 
 3. How to setup suspend_again
@@ -111,6 +126,16 @@ enum polling_modes polling_mode;
          CM_POLL_CHARGING_ONLY: poll this battery if and only if the
                                 battery is being charged.
 
+unsigned int fullbatt_vchkdrop_ms;
+unsigned int fullbatt_vchkdrop_uV;
+       : If both have non-zero values, Charger Manager will check the
+       battery voltage drop fullbatt_vchkdrop_ms after the battery is fully
+       charged. If the voltage drop is over fullbatt_vchkdrop_uV, Charger
+       Manager will try to recharge the battery by disabling and enabling
+       chargers. Recharge with voltage drop condition only (without delay
+       condition) is needed to be implemented with hardware interrupts from
+       fuel gauges or charger devices/chips.
+
 unsigned int fullbatt_uV;
        : If specified with a non-zero value, Charger Manager assumes
        that the battery is full (capacity = 100) if the battery is not being
@@ -122,6 +147,8 @@ unsigned int polling_interval_ms;
        this battery every polling_interval_ms or more frequently.
 
 enum data_source battery_present;
+       : CM_BATTERY_PRESENT: assume that the battery exists.
+       CM_NO_BATTERY: assume that the battery does not exists.
        CM_FUEL_GAUGE: get battery presence information from fuel gauge.
        CM_CHARGER_STAT: get battery presence from chargers.
 
@@ -151,7 +178,17 @@ bool measure_battery_temp;
        the value of measure_battery_temp.
 };
 
-5. Other Considerations
+5. Notify Charger-Manager of charger events: cm_notify_event()
+=========================================================
+If there is an charger event is required to notify
+Charger Manager, a charger device driver that triggers the event can call
+cm_notify_event(psy, type, msg) to notify the corresponding Charger Manager.
+In the function, psy is the charger driver's power_supply pointer, which is
+associated with Charger-Manager. The parameter "type"
+is the same as irq's type (enum cm_event_types). The event message "msg" is
+optional and is effective only if the event type is "UNDESCRIBED" or "OTHERS".
+
+6. Other Considerations
 =======================
 
 At the charger/battery-related events such as battery-pulled-out,
index 9f16c5178b662b8f9ec67f3dd7eafd6f4c89e39a..211831d4095fef63eacb13a2dbde8d647b5499a6 100644 (file)
@@ -84,6 +84,8 @@ are already charged or discharging, 'n/a' can be displayed (or
 HEALTH - represents health of the battery, values corresponds to
 POWER_SUPPLY_HEALTH_*, defined in battery.h.
 
+VOLTAGE_OCV - open circuit voltage of the battery.
+
 VOLTAGE_MAX_DESIGN, VOLTAGE_MIN_DESIGN - design values for maximal and
 minimal power supply voltages. Maximal/minimal means values of voltages
 when battery considered "full"/"empty" at normal conditions. Yes, there is
index 88fd7f5c8dcd61307171b3af852541d06a984380..13d6166d7a2798fbd54b39a90b533ad5ddebe9eb 100644 (file)
@@ -225,6 +225,13 @@ a queue must be less or equal then msg_max.
 maximum  message size value (it is every  message queue's attribute set during
 its creation).
 
+/proc/sys/fs/mqueue/msg_default is  a read/write  file for setting/getting the
+default number of messages in a queue value if attr parameter of mq_open(2) is
+NULL. If it exceed msg_max, the default value is initialized msg_max.
+
+/proc/sys/fs/mqueue/msgsize_default is a read/write file for setting/getting
+the default message size value if attr parameter of mq_open(2) is NULL. If it
+exceed msgsize_max, the default value is initialized msgsize_max.
 
 4. /proc/sys/fs/epoll - Configuration options for the epoll interface
 --------------------------------------------------------
index 4600cbe3d6beabc7e9fb77c147951bcda70e6b46..7587493c67f11e809861b0e592c5d8458ab75030 100644 (file)
@@ -16,7 +16,7 @@ There are three components to pagemap:
     * Bits 0-4   swap type if swapped
     * Bits 5-54  swap offset if swapped
     * Bits 55-60 page shift (page size = 1<<page shift)
-    * Bit  61    reserved for future use
+    * Bit  61    page is file-page or shared-anon
     * Bit  62    page swapped
     * Bit  63    page present
 
index 29bdf62aac09bc9bfb06789c40fa18b55d7b6752..f734bb2a78dc797aa62a2d2b0e1e7cc3390fef87 100644 (file)
@@ -166,6 +166,68 @@ behavior. So to make them effective you need to restart any
 application that could have been using hugepages. This also applies to
 the regions registered in khugepaged.
 
+== Monitoring usage ==
+
+The number of transparent huge pages currently used by the system is
+available by reading the AnonHugePages field in /proc/meminfo. To
+identify what applications are using transparent huge pages, it is
+necessary to read /proc/PID/smaps and count the AnonHugePages fields
+for each mapping. Note that reading the smaps file is expensive and
+reading it frequently will incur overhead.
+
+There are a number of counters in /proc/vmstat that may be used to
+monitor how successfully the system is providing huge pages for use.
+
+thp_fault_alloc is incremented every time a huge page is successfully
+       allocated to handle a page fault. This applies to both the
+       first time a page is faulted and for COW faults.
+
+thp_collapse_alloc is incremented by khugepaged when it has found
+       a range of pages to collapse into one huge page and has
+       successfully allocated a new huge page to store the data.
+
+thp_fault_fallback is incremented if a page fault fails to allocate
+       a huge page and instead falls back to using small pages.
+
+thp_collapse_alloc_failed is incremented if khugepaged found a range
+       of pages that should be collapsed into one huge page but failed
+       the allocation.
+
+thp_split is incremented every time a huge page is split into base
+       pages. This can happen for a variety of reasons but a common
+       reason is that a huge page is old and is being reclaimed.
+
+As the system ages, allocating huge pages may be expensive as the
+system uses memory compaction to copy data around memory to free a
+huge page for use. There are some counters in /proc/vmstat to help
+monitor this overhead.
+
+compact_stall is incremented every time a process stalls to run
+       memory compaction so that a huge page is free for use.
+
+compact_success is incremented if the system compacted memory and
+       freed a huge page for use.
+
+compact_fail is incremented if the system tries to compact memory
+       but failed.
+
+compact_pages_moved is incremented each time a page is moved. If
+       this value is increasing rapidly, it implies that the system
+       is copying a lot of data to satisfy the huge page allocation.
+       It is possible that the cost of copying exceeds any savings
+       from reduced TLB misses.
+
+compact_pagemigrate_failed is incremented when the underlying mechanism
+       for moving a page failed.
+
+compact_blocks_moved is incremented each time memory compaction examines
+       a huge page aligned range of pages.
+
+It is possible to establish how long the stalls were using the function
+tracer to record how long was spent in __alloc_pages_nodemask and
+using the mm_page_alloc tracepoint to identify which allocations were
+for huge pages.
+
 == get_user_pages and follow_page ==
 
 get_user_pages and follow_page if run on a hugepage, will return the
index 25fe4304f2fcb7b21b4065de12cfa5af4ad9f00b..086638f6c82d2e37d92e2aefa94243c5b1b74b13 100644 (file)
@@ -1,6 +1,6 @@
 The Linux WatchDog Timer Driver Core kernel API.
 ===============================================
-Last reviewed: 16-Mar-2012
+Last reviewed: 22-May-2012
 
 Wim Van Sebroeck <wim@iguana.be>
 
@@ -39,6 +39,10 @@ watchdog_device structure.
 The watchdog device structure looks like this:
 
 struct watchdog_device {
+       int id;
+       struct cdev cdev;
+       struct device *dev;
+       struct device *parent;
        const struct watchdog_info *info;
        const struct watchdog_ops *ops;
        unsigned int bootstatus;
@@ -46,10 +50,20 @@ struct watchdog_device {
        unsigned int min_timeout;
        unsigned int max_timeout;
        void *driver_data;
+       struct mutex lock;
        unsigned long status;
 };
 
 It contains following fields:
+* id: set by watchdog_register_device, id 0 is special. It has both a
+  /dev/watchdog0 cdev (dynamic major, minor 0) as well as the old
+  /dev/watchdog miscdev. The id is set automatically when calling
+  watchdog_register_device.
+* cdev: cdev for the dynamic /dev/watchdog<id> device nodes. This
+  field is also populated by watchdog_register_device.
+* dev: device under the watchdog class (created by watchdog_register_device).
+* parent: set this to the parent device (or NULL) before calling
+  watchdog_register_device.
 * info: a pointer to a watchdog_info structure. This structure gives some
   additional information about the watchdog timer itself. (Like it's unique name)
 * ops: a pointer to the list of watchdog operations that the watchdog supports.
@@ -61,6 +75,7 @@ It contains following fields:
 * driver_data: a pointer to the drivers private data of a watchdog device.
   This data should only be accessed via the watchdog_set_drvdata and
   watchdog_get_drvdata routines.
+* lock: Mutex for WatchDog Timer Driver Core internal use only.
 * status: this field contains a number of status bits that give extra
   information about the status of the device (Like: is the watchdog timer
   running/active, is the nowayout bit set, is the device opened via
@@ -78,6 +93,8 @@ struct watchdog_ops {
        unsigned int (*status)(struct watchdog_device *);
        int (*set_timeout)(struct watchdog_device *, unsigned int);
        unsigned int (*get_timeleft)(struct watchdog_device *);
+       void (*ref)(struct watchdog_device *);
+       void (*unref)(struct watchdog_device *);
        long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long);
 };
 
@@ -85,6 +102,21 @@ It is important that you first define the module owner of the watchdog timer
 driver's operations. This module owner will be used to lock the module when
 the watchdog is active. (This to avoid a system crash when you unload the
 module and /dev/watchdog is still open).
+
+If the watchdog_device struct is dynamically allocated, just locking the module
+is not enough and a driver also needs to define the ref and unref operations to
+ensure the structure holding the watchdog_device does not go away.
+
+The simplest (and usually sufficient) implementation of this is to:
+1) Add a kref struct to the same structure which is holding the watchdog_device
+2) Define a release callback for the kref which frees the struct holding both
+3) Call kref_init on this kref *before* calling watchdog_register_device()
+4) Define a ref operation calling kref_get on this kref
+5) Define a unref operation calling kref_put on this kref
+6) When it is time to cleanup:
+ * Do not kfree() the struct holding both, the last kref_put will do this!
+ * *After* calling watchdog_unregister_device() call kref_put on the kref
+
 Some operations are mandatory and some are optional. The mandatory operations
 are:
 * start: this is a pointer to the routine that starts the watchdog timer
@@ -125,6 +157,10 @@ they are supported. These optional routines/operations are:
   (Note: the WDIOF_SETTIMEOUT needs to be set in the options field of the
   watchdog's info structure).
 * get_timeleft: this routines returns the time that's left before a reset.
+* ref: the operation that calls kref_get on the kref of a dynamically
+  allocated watchdog_device struct.
+* unref: the operation that calls kref_put on the kref of a dynamically
+  allocated watchdog_device struct.
 * ioctl: if this routine is present then it will be called first before we do
   our own internal ioctl call handling. This routine should return -ENOIOCTLCMD
   if a command is not supported. The parameters that are passed to the ioctl
@@ -144,6 +180,11 @@ bit-operations. The status bits that are defined are:
   (This bit should only be used by the WatchDog Timer Driver Core).
 * WDOG_NO_WAY_OUT: this bit stores the nowayout setting for the watchdog.
   If this bit is set then the watchdog timer will not be able to stop.
+* WDOG_UNREGISTERED: this bit gets set by the WatchDog Timer Driver Core
+  after calling watchdog_unregister_device, and then checked before calling
+  any watchdog_ops, so that you can be sure that no operations (other then
+  unref) will get called after unregister, even if userspace still holds a
+  reference to /dev/watchdog
 
   To set the WDOG_NO_WAY_OUT status bit (before registering your watchdog
   timer device) you can either:
index 17ddd822b4563c2cdf80ba80f422db5b4b35e002..04fddbacdbde74a03ae18ea6d91e5c0f452c0b44 100644 (file)
@@ -78,6 +78,11 @@ wd0_timeout: Default watchdog0 timeout in 1/10secs
 wd1_timeout: Default watchdog1 timeout in 1/10secs
 wd2_timeout: Default watchdog2 timeout in 1/10secs
 -------------------------------------------------
+da9052wdt:
+timeout: Watchdog timeout in seconds. 2<= timeout <=131, default=2.048s
+nowayout: Watchdog cannot be stopped once started
+       (default=kernel config parameter)
+-------------------------------------------------
 davinci_wdt:
 heartbeat: Watchdog heartbeat period in seconds from 1 to 600, default 60
 -------------------------------------------------
index 6f90c64a25399033da03b1c7565622565339634c..55f0fda602ecc69d5242ca5181c002ab2a8dd983 100644 (file)
@@ -2818,6 +2818,12 @@ F:       Documentation/firmware_class/
 F:     drivers/base/firmware*.c
 F:     include/linux/firmware.h
 
+FLOPPY DRIVER
+M:     Jiri Kosina <jkosina@suse.cz>
+T:     git git://git.kernel.org/pub/scm/linux/kernel/git/jikos/floppy.git
+S:     Odd fixes
+F:     drivers/block/floppy.c
+
 FPU EMULATOR
 M:     Bill Metzenthen <billm@melbpc.org.au>
 W:     http://floatingpoint.sourceforge.net/emulator/index.html
@@ -3232,10 +3238,8 @@ F:       include/linux/clockchips.h
 F:     include/linux/hrtimer.h
 
 HIGH-SPEED SCC DRIVER FOR AX.25
-M:     Klaus Kudielka <klaus.kudielka@ieee.org>
 L:     linux-hams@vger.kernel.org
-W:     http://www.nt.tuwien.ac.at/~kkudielk/Linux/
-S:     Maintained
+S:     Orphan
 F:     drivers/net/hamradio/dmascc.c
 F:     drivers/net/hamradio/scc.c
 
@@ -3382,6 +3386,12 @@ W:       http://www.developer.ibm.com/welcome/netfinity/serveraid.html
 S:     Supported
 F:     drivers/scsi/ips.*
 
+ICH LPC AND GPIO DRIVER
+M:     Peter Tyser <ptyser@xes-inc.com>
+S:     Maintained
+F:     drivers/mfd/lpc_ich.c
+F:     drivers/gpio/gpio-ich.c
+
 IDE SUBSYSTEM
 M:     "David S. Miller" <davem@davemloft.net>
 L:     linux-ide@vger.kernel.org
@@ -4505,12 +4515,6 @@ L:       linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
 S:     Maintained
 F:     drivers/mmc/host/imxmmc.*
 
-MOUSE AND MISC DEVICES [GENERAL]
-M:     Alessandro Rubini <rubini@ipvvis.unipv.it>
-S:     Maintained
-F:     drivers/input/mouse/
-F:     include/linux/gpio_mouse.h
-
 MOXA SMARTIO/INDUSTIO/INTELLIO SERIAL CARD
 M:     Jiri Slaby <jirislaby@gmail.com>
 S:     Maintained
@@ -5333,7 +5337,7 @@ M:        David Woodhouse <dwmw2@infradead.org>
 T:     git git://git.infradead.org/battery-2.6.git
 S:     Maintained
 F:     include/linux/power_supply.h
-F:     drivers/power/power_supply*
+F:     drivers/power/
 
 PNP SUPPORT
 M:     Adam Belay <abelay@mit.edu>
@@ -6653,7 +6657,7 @@ F:        include/linux/taskstats*
 F:     kernel/taskstats.c
 
 TC CLASSIFIER
-M:     Jamal Hadi Salim <hadi@cyberus.ca>
+M:     Jamal Hadi Salim <jhs@mojatatu.com>
 L:     netdev@vger.kernel.org
 S:     Maintained
 F:     include/linux/pkt_cls.h
index 24779fc95994efb5c4d69e4d507f3cba581570a4..5a8a48320efe9f5c577f9cc8363a4de2a6725559 100644 (file)
@@ -10,9 +10,6 @@
 typedef unsigned int   __kernel_ino_t;
 #define __kernel_ino_t __kernel_ino_t
 
-typedef unsigned int   __kernel_nlink_t;
-#define __kernel_nlink_t __kernel_nlink_t
-
 typedef unsigned long  __kernel_sigset_t;      /* at least 32 bits */
 
 #include <asm-generic/posix_types.h>
index 10ab2d74ecbbede2764c8bf8cbcca14f19f418a0..a8c97d42ec8eaef9215c32a40f743bd94505bc55 100644 (file)
@@ -226,7 +226,6 @@ do_sigreturn(struct sigcontext __user *sc, struct pt_regs *regs,
        if (__get_user(set.sig[0], &sc->sc_mask))
                goto give_sigsegv;
 
-       sigdelsetmask(&set, ~_BLOCKABLE);
        set_current_blocked(&set);
 
        if (restore_sigcontext(sc, regs, sw))
@@ -261,7 +260,6 @@ do_rt_sigreturn(struct rt_sigframe __user *frame, struct pt_regs *regs,
        if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
                goto give_sigsegv;
 
-       sigdelsetmask(&set, ~_BLOCKABLE);
        set_current_blocked(&set);
 
        if (restore_sigcontext(&frame->uc.uc_mcontext, regs, sw))
@@ -468,12 +466,9 @@ static inline void
 handle_signal(int sig, struct k_sigaction *ka, siginfo_t *info,
              struct pt_regs * regs, struct switch_stack *sw)
 {
-       sigset_t *oldset = &current->blocked;
+       sigset_t *oldset = sigmask_to_save();
        int ret;
 
-       if (test_thread_flag(TIF_RESTORE_SIGMASK))
-               oldset = &current->saved_sigmask;
-
        if (ka->sa.sa_flags & SA_SIGINFO)
                ret = setup_rt_frame(sig, ka, info, oldset, regs, sw);
        else
@@ -483,12 +478,7 @@ handle_signal(int sig, struct k_sigaction *ka, siginfo_t *info,
                force_sigsegv(sig, current);
                return;
        }
-       block_sigmask(ka, sig);
-       /* A signal was successfully delivered, and the
-          saved sigmask was stored on the signal frame,
-          and will be restored by sigreturn.  So we can
-          simply clear the restore sigmask flag.  */
-       clear_thread_flag(TIF_RESTORE_SIGMASK);
+       signal_delivered(sig, info, ka, regs, 0);
 }
 
 static inline void
@@ -572,9 +562,7 @@ do_signal(struct pt_regs * regs, struct switch_stack * sw,
        }
 
        /* If there's no signal to deliver, we just restore the saved mask.  */
-       if (test_and_clear_thread_flag(TIF_RESTORE_SIGMASK))
-               set_current_blocked(&current->saved_sigmask);
-
+       restore_saved_sigmask();
        if (single_stepping)
                ptrace_set_bpt(current);        /* re-set breakpoint */
 }
@@ -590,7 +578,5 @@ do_notify_resume(struct pt_regs *regs, struct switch_stack *sw,
        if (thread_info_flags & _TIF_NOTIFY_RESUME) {
                clear_thread_flag(TIF_NOTIFY_RESUME);
                tracehook_notify_resume(regs);
-               if (current->replacement_session_keyring)
-                       key_replace_session_keyring();
        }
 }
index 5e7601301b416c29fe3fa0905ee90a9ac4931e94..b649c5904a4ff3c993732067411e7723fc4d4d19 100644 (file)
@@ -525,7 +525,7 @@ config ARCH_IXP4XX
        select ARCH_HAS_DMA_SET_COHERENT_MASK
        select CLKSRC_MMIO
        select CPU_XSCALE
-       select GENERIC_GPIO
+       select ARCH_REQUIRE_GPIOLIB
        select GENERIC_CLOCKEVENTS
        select MIGHT_HAVE_PCI
        select NEED_MACH_IO_H
index 5ca0cdb76413e5e90f03787d1527930bafd81d73..4272b2949228ba2e4ea9d7ebb7bc2285fd9bb19b 100644 (file)
                reg = <0x10481000 0x1000>, <0x10482000 0x2000>;
        };
 
+       combiner:interrupt-controller@10440000 {
+               compatible = "samsung,exynos4210-combiner";
+               #interrupt-cells = <2>;
+               interrupt-controller;
+               samsung,combiner-nr = <32>;
+               reg = <0x10440000 0x1000>;
+               interrupts = <0 0 0>, <0 1 0>, <0 2 0>, <0 3 0>,
+                            <0 4 0>, <0 5 0>, <0 6 0>, <0 7 0>,
+                            <0 8 0>, <0 9 0>, <0 10 0>, <0 11 0>,
+                            <0 12 0>, <0 13 0>, <0 14 0>, <0 15 0>,
+                            <0 16 0>, <0 17 0>, <0 18 0>, <0 19 0>,
+                            <0 20 0>, <0 21 0>, <0 22 0>, <0 23 0>,
+                            <0 24 0>, <0 25 0>, <0 26 0>, <0 27 0>,
+                            <0 28 0>, <0 29 0>, <0 30 0>, <0 31 0>;
+       };
+
        watchdog {
                compatible = "samsung,s3c2410-wdt";
                reg = <0x101D0000 0x100>;
index 2d696866f71c4117973b8055d3ebfbc25b16786d..3f5dad801a9806ad3173a0d06dedc0c4a37e8388 100644 (file)
                        gpio: gpio@40028000 {
                                compatible = "nxp,lpc3220-gpio";
                                reg = <0x40028000 0x1000>;
-                               /* create a private address space for enumeration */
-                               #address-cells = <1>;
-                               #size-cells = <0>;
-
-                               gpio_p0: gpio-bank@0 {
-                                       gpio-controller;
-                                       #gpio-cells = <2>;
-                                       reg = <0>;
-                               };
-
-                               gpio_p1: gpio-bank@1 {
-                                       gpio-controller;
-                                       #gpio-cells = <2>;
-                                       reg = <1>;
-                               };
-
-                               gpio_p2: gpio-bank@2 {
-                                       gpio-controller;
-                                       #gpio-cells = <2>;
-                                       reg = <2>;
-                               };
-
-                               gpio_p3: gpio-bank@3 {
-                                       gpio-controller;
-                                       #gpio-cells = <2>;
-                                       reg = <3>;
-                               };
-
-                               gpi_p3: gpio-bank@4 {
-                                       gpio-controller;
-                                       #gpio-cells = <2>;
-                                       reg = <4>;
-                               };
-
-                               gpo_p3: gpio-bank@5 {
-                                       gpio-controller;
-                                       #gpio-cells = <2>;
-                                       reg = <5>;
-                               };
+                               gpio-controller;
+                               #gpio-cells = <3>; /* bank, pin, flags */
                        };
 
                        watchdog@4003C000 {
index 0167e86314c011bc8dc6141a4a3f2cbef6d4dd36..c4ff6d1a018bbee575fd99432c518d0ed530e769 100644 (file)
                compatible = "gpio-leds";
 
                led0 {
-                       gpios = <&gpo_p3 1 1>; /* GPO_P3 1, GPIO 80, active low */
+                       gpios = <&gpio 5 1 1>; /* GPO_P3 1, GPIO 80, active low */
                        linux,default-trigger = "heartbeat";
                        default-state = "off";
                };
 
                led1 {
-                       gpios = <&gpo_p3 14 1>; /* GPO_P3 14, GPIO 93, active low */
+                       gpios = <&gpio 5 14 1>; /* GPO_P3 14, GPIO 93, active low */
                        linux,default-trigger = "timer";
                        default-state = "off";
                };
index 941b161ab78ce32c219ec6c88a1ea2f955e3fa77..7e1091d91af8b9d999b795414ebf0110c42b65c0 100644 (file)
                #address-cells = <0>;
                interrupt-controller;
                reg = <0x2c001000 0x1000>,
-                     <0x2c002000 0x100>;
+                     <0x2c002000 0x1000>,
+                     <0x2c004000 0x2000>,
+                     <0x2c006000 0x2000>;
+               interrupts = <1 9 0xf04>;
        };
 
        memory-controller@7ffd0000 {
                             <0 91 4>;
        };
 
+       timer {
+               compatible = "arm,armv7-timer";
+               interrupts = <1 13 0xf08>,
+                            <1 14 0xf08>,
+                            <1 11 0xf08>,
+                            <1 10 0xf08>;
+       };
+
        pmu {
                compatible = "arm,cortex-a15-pmu", "arm,cortex-a9-pmu";
                interrupts = <0 68 4>,
index 6905e66d474808f7021121dbb5e065cb7e5d64a0..18917a0f86047a3a444919badb09fb47da244bc4 100644 (file)
 
        timer@2c000600 {
                compatible = "arm,cortex-a5-twd-timer";
-               reg = <0x2c000600 0x38>;
-               interrupts = <1 2 0x304>,
-                            <1 3 0x304>;
+               reg = <0x2c000600 0x20>;
+               interrupts = <1 13 0x304>;
+       };
+
+       watchdog@2c000620 {
+               compatible = "arm,cortex-a5-twd-wdt";
+               reg = <0x2c000620 0x20>;
+               interrupts = <1 14 0x304>;
        };
 
        gic: interrupt-controller@2c001000 {
-               compatible = "arm,corex-a5-gic", "arm,cortex-a9-gic";
+               compatible = "arm,cortex-a5-gic", "arm,cortex-a9-gic";
                #interrupt-cells = <3>;
                #address-cells = <0>;
                interrupt-controller;
index da778693be548fdc87c6cb479ff6eefe71a7bf82..3f0c736d31d6bca211d1c76550ed28ca841a3c56 100644 (file)
        timer@1e000600 {
                compatible = "arm,cortex-a9-twd-timer";
                reg = <0x1e000600 0x20>;
-               interrupts = <1 2 0xf04>,
-                            <1 3 0xf04>;
+               interrupts = <1 13 0xf04>;
+       };
+
+       watchdog@1e000620 {
+               compatible = "arm,cortex-a9-twd-wdt";
+               reg = <0x1e000620 0x20>;
+               interrupts = <1 14 0xf04>;
        };
 
        gic: interrupt-controller@1e001000 {
index ebda45bd576374a5efc995f86088c78a995f4d9c..e05a2f1665a78c1b6929271aed7b69a054247ef3 100644 (file)
@@ -173,7 +173,7 @@ CONFIG_LEDS_TRIGGER_DEFAULT_ON=y
 CONFIG_RTC_CLASS=y
 CONFIG_RTC_DRV_PCF8563=y
 CONFIG_RTC_DRV_IMXDI=y
-CONFIG_RTC_MXC=y
+CONFIG_RTC_DRV_MXC=y
 CONFIG_DMADEVICES=y
 CONFIG_IMX_SDMA=y
 CONFIG_IMX_DMA=y
index 12617f7296e6cbe13a11c54723feb32d3803b544..b1d3675df72cd2c8abb336e05cd01854b9fe7918 100644 (file)
@@ -178,7 +178,7 @@ CONFIG_NEW_LEDS=y
 CONFIG_LEDS_CLASS=y
 CONFIG_RTC_CLASS=y
 CONFIG_RTC_INTF_DEV_UIE_EMUL=y
-CONFIG_RTC_MXC=y
+CONFIG_RTC_DRV_MXC=y
 CONFIG_DMADEVICES=y
 CONFIG_IMX_SDMA=y
 CONFIG_EXT2_FS=y
index 9af5563dd3ebbc6be0f53448e107a05249cca859..815c669fec0a1f52665120604c774dbb6e9b2e41 100644 (file)
@@ -47,9 +47,9 @@ extern void __raw_readsb(const void __iomem *addr, void *data, int bytelen);
 extern void __raw_readsw(const void __iomem *addr, void *data, int wordlen);
 extern void __raw_readsl(const void __iomem *addr, void *data, int longlen);
 
-#define __raw_writeb(v,a)      (__chk_io_ptr(a), *(volatile unsigned char __force  *)(a) = (v))
-#define __raw_writew(v,a)      (__chk_io_ptr(a), *(volatile unsigned short __force *)(a) = (v))
-#define __raw_writel(v,a)      (__chk_io_ptr(a), *(volatile unsigned int __force   *)(a) = (v))
+#define __raw_writeb(v,a)      ((void)(__chk_io_ptr(a), *(volatile unsigned char __force  *)(a) = (v)))
+#define __raw_writew(v,a)      ((void)(__chk_io_ptr(a), *(volatile unsigned short __force *)(a) = (v)))
+#define __raw_writel(v,a)      ((void)(__chk_io_ptr(a), *(volatile unsigned int __force   *)(a) = (v)))
 
 #define __raw_readb(a)         (__chk_io_ptr(a), *(volatile unsigned char __force  *)(a))
 #define __raw_readw(a)         (__chk_io_ptr(a), *(volatile unsigned short __force *)(a))
@@ -229,11 +229,9 @@ extern void _memset_io(volatile void __iomem *, int, size_t);
 #define readl_relaxed(c) ({ u32 __r = le32_to_cpu((__force __le32) \
                                        __raw_readl(c)); __r; })
 
-#define writeb_relaxed(v,c)    ((void)__raw_writeb(v,c))
-#define writew_relaxed(v,c)    ((void)__raw_writew((__force u16) \
-                                       cpu_to_le16(v),c))
-#define writel_relaxed(v,c)    ((void)__raw_writel((__force u32) \
-                                       cpu_to_le32(v),c))
+#define writeb_relaxed(v,c)    __raw_writeb(v,c)
+#define writew_relaxed(v,c)    __raw_writew((__force u16) cpu_to_le16(v),c)
+#define writel_relaxed(v,c)    __raw_writel((__force u32) cpu_to_le32(v),c)
 
 #define readb(c)               ({ u8  __v = readb_relaxed(c); __iormb(); __v; })
 #define readw(c)               ({ u16 __v = readw_relaxed(c); __iormb(); __v; })
@@ -281,12 +279,12 @@ extern void _memset_io(volatile void __iomem *, int, size_t);
 #define ioread16be(p)  ({ unsigned int __v = be16_to_cpu((__force __be16)__raw_readw(p)); __iormb(); __v; })
 #define ioread32be(p)  ({ unsigned int __v = be32_to_cpu((__force __be32)__raw_readl(p)); __iormb(); __v; })
 
-#define iowrite8(v,p)  ({ __iowmb(); (void)__raw_writeb(v, p); })
-#define iowrite16(v,p) ({ __iowmb(); (void)__raw_writew((__force __u16)cpu_to_le16(v), p); })
-#define iowrite32(v,p) ({ __iowmb(); (void)__raw_writel((__force __u32)cpu_to_le32(v), p); })
+#define iowrite8(v,p)  ({ __iowmb(); __raw_writeb(v, p); })
+#define iowrite16(v,p) ({ __iowmb(); __raw_writew((__force __u16)cpu_to_le16(v), p); })
+#define iowrite32(v,p) ({ __iowmb(); __raw_writel((__force __u32)cpu_to_le32(v), p); })
 
-#define iowrite16be(v,p) ({ __iowmb(); (void)__raw_writew((__force __u16)cpu_to_be16(v), p); })
-#define iowrite32be(v,p) ({ __iowmb(); (void)__raw_writel((__force __u32)cpu_to_be32(v), p); })
+#define iowrite16be(v,p) ({ __iowmb(); __raw_writew((__force __u16)cpu_to_be16(v), p); })
+#define iowrite32be(v,p) ({ __iowmb(); __raw_writel((__force __u32)cpu_to_be32(v), p); })
 
 #define ioread8_rep(p,d,c)     __raw_readsb(p,d,c)
 #define ioread16_rep(p,d,c)    __raw_readsw(p,d,c)
index efdf99045d879e240b9bc41f1f0781efea6ac10f..d2de9cbbcd9bcaf6a9e5b76eefac1f8c8eb7b39d 100644 (file)
@@ -22,9 +22,6 @@
 typedef unsigned short         __kernel_mode_t;
 #define __kernel_mode_t __kernel_mode_t
 
-typedef unsigned short         __kernel_nlink_t;
-#define __kernel_nlink_t __kernel_nlink_t
-
 typedef unsigned short         __kernel_ipc_pid_t;
 #define __kernel_ipc_pid_t __kernel_ipc_pid_t
 
index 68388eb4946bda7a26a6c06868c51b890411a5a6..b79f8e97f7755f22d82ae20ee00442cd11f7af02 100644 (file)
@@ -148,6 +148,7 @@ extern int vfp_restore_user_hwstate(struct user_vfp __user *,
 #define TIF_NOTIFY_RESUME      2       /* callback before returning to user */
 #define TIF_SYSCALL_TRACE      8
 #define TIF_SYSCALL_AUDIT      9
+#define TIF_SYSCALL_RESTARTSYS 10
 #define TIF_POLLING_NRFLAG     16
 #define TIF_USING_IWMMXT       17
 #define TIF_MEMDIE             18      /* is terminating due to OOM killer */
@@ -162,16 +163,17 @@ extern int vfp_restore_user_hwstate(struct user_vfp __user *,
 #define _TIF_SYSCALL_AUDIT     (1 << TIF_SYSCALL_AUDIT)
 #define _TIF_POLLING_NRFLAG    (1 << TIF_POLLING_NRFLAG)
 #define _TIF_USING_IWMMXT      (1 << TIF_USING_IWMMXT)
-#define _TIF_RESTORE_SIGMASK   (1 << TIF_RESTORE_SIGMASK)
 #define _TIF_SECCOMP           (1 << TIF_SECCOMP)
+#define _TIF_SYSCALL_RESTARTSYS        (1 << TIF_SYSCALL_RESTARTSYS)
 
 /* Checks for any syscall work in entry-common.S */
-#define _TIF_SYSCALL_WORK (_TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT)
+#define _TIF_SYSCALL_WORK (_TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT | \
+                          _TIF_SYSCALL_RESTARTSYS)
 
 /*
  * Change these and you break ASM code in entry-common.S
  */
-#define _TIF_WORK_MASK         0x000000ff
+#define _TIF_WORK_MASK         (_TIF_NEED_RESCHED | _TIF_SIGPENDING | _TIF_NOTIFY_RESUME)
 
 #endif /* __KERNEL__ */
 #endif /* __ASM_ARM_THREAD_INFO_H */
index 7bd2d3cb8957d2aeb6ba85baa2a5f6be49c1d3b8..4afed88d250a6f6127a8a42a7a3462394b078bfc 100644 (file)
@@ -53,9 +53,13 @@ fast_work_pending:
 work_pending:
        tst     r1, #_TIF_NEED_RESCHED
        bne     work_resched
-       tst     r1, #_TIF_SIGPENDING|_TIF_NOTIFY_RESUME
-       beq     no_work_pending
+       /*
+        * TIF_SIGPENDING or TIF_NOTIFY_RESUME must've been set if we got here
+        */
+       ldr     r2, [sp, #S_PSR]
        mov     r0, sp                          @ 'regs'
+       tst     r2, #15                         @ are we returning to user mode?
+       bne     no_work_pending                 @ no?  just leave, then...
        mov     r2, why                         @ 'syscall'
        tst     r1, #_TIF_SIGPENDING            @ delivering a signal?
        movne   why, #0                         @ prevent further restarts
index 14e38261cd31db9d852db2eb0b8046251a04613d..5700a7ae7f0bc1511ae048d7a3f025c401e5729c 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/regset.h>
 #include <linux/audit.h>
 #include <linux/tracehook.h>
+#include <linux/unistd.h>
 
 #include <asm/pgtable.h>
 #include <asm/traps.h>
@@ -917,6 +918,8 @@ asmlinkage int syscall_trace(int why, struct pt_regs *regs, int scno)
                audit_syscall_entry(AUDIT_ARCH_ARM, scno, regs->ARM_r0,
                                    regs->ARM_r1, regs->ARM_r2, regs->ARM_r3);
 
+       if (why == 0 && test_and_clear_thread_flag(TIF_SYSCALL_RESTARTSYS))
+               scno = __NR_restart_syscall - __NR_SYSCALL_BASE;
        if (!test_thread_flag(TIF_SYSCALL_TRACE))
                return scno;
 
index 4e5fdd9bd9e39778b64c5354544b1c7f9fe89d77..fd2392a17ac1befb3464b5a0e77960a0f862b4ca 100644 (file)
 
 #include "signal.h"
 
-#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
-
 /*
  * For ARM syscalls, we encode the syscall number into the instruction.
  */
 #define SWI_SYS_SIGRETURN      (0xef000000|(__NR_sigreturn)|(__NR_OABI_SYSCALL_BASE))
 #define SWI_SYS_RT_SIGRETURN   (0xef000000|(__NR_rt_sigreturn)|(__NR_OABI_SYSCALL_BASE))
-#define SWI_SYS_RESTART                (0xef000000|__NR_restart_syscall|__NR_OABI_SYSCALL_BASE)
 
 /*
  * With EABI, the syscall number has to be loaded into r7.
@@ -49,18 +46,6 @@ const unsigned long sigreturn_codes[7] = {
        MOV_R7_NR_RT_SIGRETURN, SWI_SYS_RT_SIGRETURN, SWI_THUMB_RT_SIGRETURN,
 };
 
-/*
- * Either we support OABI only, or we have EABI with the OABI
- * compat layer enabled.  In the later case we don't know if
- * user space is EABI or not, and if not we must not clobber r7.
- * Always using the OABI syscall solves that issue and works for
- * all those cases.
- */
-const unsigned long syscall_restart_code[2] = {
-       SWI_SYS_RESTART,        /* swi  __NR_restart_syscall */
-       0xe49df004,             /* ldr  pc, [sp], #4 */
-};
-
 /*
  * atomically swap in the new signal mask, and wait for a signal.
  */
@@ -82,10 +67,10 @@ sys_sigaction(int sig, const struct old_sigaction __user *act,
                old_sigset_t mask;
                if (!access_ok(VERIFY_READ, act, sizeof(*act)) ||
                    __get_user(new_ka.sa.sa_handler, &act->sa_handler) ||
-                   __get_user(new_ka.sa.sa_restorer, &act->sa_restorer))
+                   __get_user(new_ka.sa.sa_restorer, &act->sa_restorer) ||
+                   __get_user(new_ka.sa.sa_flags, &act->sa_flags) ||
+                   __get_user(mask, &act->sa_mask))
                        return -EFAULT;
-               __get_user(new_ka.sa.sa_flags, &act->sa_flags);
-               __get_user(mask, &act->sa_mask);
                siginitset(&new_ka.sa.sa_mask, mask);
        }
 
@@ -94,10 +79,10 @@ sys_sigaction(int sig, const struct old_sigaction __user *act,
        if (!ret && oact) {
                if (!access_ok(VERIFY_WRITE, oact, sizeof(*oact)) ||
                    __put_user(old_ka.sa.sa_handler, &oact->sa_handler) ||
-                   __put_user(old_ka.sa.sa_restorer, &oact->sa_restorer))
+                   __put_user(old_ka.sa.sa_restorer, &oact->sa_restorer) ||
+                   __put_user(old_ka.sa.sa_flags, &oact->sa_flags) ||
+                   __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask))
                        return -EFAULT;
-               __put_user(old_ka.sa.sa_flags, &oact->sa_flags);
-               __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask);
        }
 
        return ret;
@@ -223,10 +208,8 @@ static int restore_sigframe(struct pt_regs *regs, struct sigframe __user *sf)
        int err;
 
        err = __copy_from_user(&set, &sf->uc.uc_sigmask, sizeof(set));
-       if (err == 0) {
-               sigdelsetmask(&set, ~_BLOCKABLE);
+       if (err == 0)
                set_current_blocked(&set);
-       }
 
        __get_user_error(regs->ARM_r0, &sf->uc.uc_mcontext.arm_r0, err);
        __get_user_error(regs->ARM_r1, &sf->uc.uc_mcontext.arm_r1, err);
@@ -541,13 +524,13 @@ setup_rt_frame(int usig, struct k_sigaction *ka, siginfo_t *info,
 /*
  * OK, we're invoking a handler
  */    
-static int
+static void
 handle_signal(unsigned long sig, struct k_sigaction *ka,
-             siginfo_t *info, sigset_t *oldset,
-             struct pt_regs * regs)
+             siginfo_t *info, struct pt_regs *regs)
 {
        struct thread_info *thread = current_thread_info();
        struct task_struct *tsk = current;
+       sigset_t *oldset = sigmask_to_save();
        int usig = sig;
        int ret;
 
@@ -572,17 +555,9 @@ handle_signal(unsigned long sig, struct k_sigaction *ka,
 
        if (ret != 0) {
                force_sigsegv(sig, tsk);
-               return ret;
+               return;
        }
-
-       /*
-        * Block the signal if we were successful.
-        */
-       block_sigmask(ka, sig);
-
-       tracehook_signal_handler(sig, info, ka, regs, 0);
-
-       return 0;
+       signal_delivered(sig, info, ka, regs, 0);
 }
 
 /*
@@ -601,15 +576,6 @@ static void do_signal(struct pt_regs *regs, int syscall)
        siginfo_t info;
        int signr;
 
-       /*
-        * We want the common case to go fast, which
-        * is why we may in certain cases get here from
-        * kernel mode. Just return without doing anything
-        * if so.
-        */
-       if (!user_mode(regs))
-               return;
-
        /*
         * If we were from a system call, check for system call restarting...
         */
@@ -626,58 +592,39 @@ static void do_signal(struct pt_regs *regs, int syscall)
                case -ERESTARTNOHAND:
                case -ERESTARTSYS:
                case -ERESTARTNOINTR:
+               case -ERESTART_RESTARTBLOCK:
                        regs->ARM_r0 = regs->ARM_ORIG_r0;
                        regs->ARM_pc = restart_addr;
                        break;
-               case -ERESTART_RESTARTBLOCK:
-                       regs->ARM_r0 = -EINTR;
-                       break;
                }
        }
 
-       if (try_to_freeze())
-               goto no_signal;
-
        /*
         * Get the signal to deliver.  When running under ptrace, at this
         * point the debugger may change all our registers ...
         */
        signr = get_signal_to_deliver(&info, &ka, regs, NULL);
        if (signr > 0) {
-               sigset_t *oldset;
-
                /*
                 * Depending on the signal settings we may need to revert the
                 * decision to restart the system call.  But skip this if a
                 * debugger has chosen to restart at a different PC.
                 */
                if (regs->ARM_pc == restart_addr) {
-                       if (retval == -ERESTARTNOHAND
+                       if (retval == -ERESTARTNOHAND ||
+                           retval == -ERESTART_RESTARTBLOCK
                            || (retval == -ERESTARTSYS
                                && !(ka.sa.sa_flags & SA_RESTART))) {
                                regs->ARM_r0 = -EINTR;
                                regs->ARM_pc = continue_addr;
                        }
+                       clear_thread_flag(TIF_SYSCALL_RESTARTSYS);
                }
 
-               if (test_thread_flag(TIF_RESTORE_SIGMASK))
-                       oldset = &current->saved_sigmask;
-               else
-                       oldset = &current->blocked;
-               if (handle_signal(signr, &ka, &info, oldset, regs) == 0) {
-                       /*
-                        * A signal was successfully delivered; the saved
-                        * sigmask will have been stored in the signal frame,
-                        * and will be restored by sigreturn, so we can simply
-                        * clear the TIF_RESTORE_SIGMASK flag.
-                        */
-                       if (test_thread_flag(TIF_RESTORE_SIGMASK))
-                               clear_thread_flag(TIF_RESTORE_SIGMASK);
-               }
+               handle_signal(signr, &ka, &info, regs);
                return;
        }
 
- no_signal:
        if (syscall) {
                /*
                 * Handle restarting a different system call.  As above,
@@ -685,38 +632,11 @@ static void do_signal(struct pt_regs *regs, int syscall)
                 * ignore the restart.
                 */
                if (retval == -ERESTART_RESTARTBLOCK
-                   && regs->ARM_pc == continue_addr) {
-                       if (thumb_mode(regs)) {
-                               regs->ARM_r7 = __NR_restart_syscall - __NR_SYSCALL_BASE;
-                               regs->ARM_pc -= 2;
-                       } else {
-#if defined(CONFIG_AEABI) && !defined(CONFIG_OABI_COMPAT)
-                               regs->ARM_r7 = __NR_restart_syscall;
-                               regs->ARM_pc -= 4;
-#else
-                               u32 __user *usp;
-
-                               regs->ARM_sp -= 4;
-                               usp = (u32 __user *)regs->ARM_sp;
-
-                               if (put_user(regs->ARM_pc, usp) == 0) {
-                                       regs->ARM_pc = KERN_RESTART_CODE;
-                               } else {
-                                       regs->ARM_sp += 4;
-                                       force_sigsegv(0, current);
-                               }
-#endif
-                       }
-               }
-
-               /* If there's no signal to deliver, we just put the saved sigmask
-                * back.
-                */
-               if (test_thread_flag(TIF_RESTORE_SIGMASK)) {
-                       clear_thread_flag(TIF_RESTORE_SIGMASK);
-                       sigprocmask(SIG_SETMASK, &current->saved_sigmask, NULL);
-               }
+                   && regs->ARM_pc == restart_addr)
+                       set_thread_flag(TIF_SYSCALL_RESTARTSYS);
        }
+
+       restore_saved_sigmask();
 }
 
 asmlinkage void
@@ -728,7 +648,5 @@ do_notify_resume(struct pt_regs *regs, unsigned int thread_flags, int syscall)
        if (thread_flags & _TIF_NOTIFY_RESUME) {
                clear_thread_flag(TIF_NOTIFY_RESUME);
                tracehook_notify_resume(regs);
-               if (current->replacement_session_keyring)
-                       key_replace_session_keyring();
        }
 }
index 6fcfe8398aa473051fbf72396986a84dc700978f..5ff067b7c7522f428b4343832ecb759b7ccf16de 100644 (file)
@@ -8,7 +8,5 @@
  * published by the Free Software Foundation.
  */
 #define KERN_SIGRETURN_CODE    (CONFIG_VECTORS_BASE + 0x00000500)
-#define KERN_RESTART_CODE      (KERN_SIGRETURN_CODE + sizeof(sigreturn_codes))
 
 extern const unsigned long sigreturn_codes[7];
-extern const unsigned long syscall_restart_code[2];
index b735521a4a5441f7764591bc06352d86b1ddae80..2c7217d971db0b42b9e1f5859459e18f9294f662 100644 (file)
@@ -109,7 +109,6 @@ static void percpu_timer_stop(void);
 int __cpu_disable(void)
 {
        unsigned int cpu = smp_processor_id();
-       struct task_struct *p;
        int ret;
 
        ret = platform_cpu_disable(cpu);
@@ -139,12 +138,7 @@ int __cpu_disable(void)
        flush_cache_all();
        local_flush_tlb_all();
 
-       read_lock(&tasklist_lock);
-       for_each_process(p) {
-               if (p->mm)
-                       cpumask_clear_cpu(cpu, mm_cpumask(p->mm));
-       }
-       read_unlock(&tasklist_lock);
+       clear_tasks_mm_cpumask(cpu);
 
        return 0;
 }
index 3647170e9a16ba3aa8838218ed99e74dbc5b7c59..4928d89758f4ce0dea767acdf9fcf2299dff7833 100644 (file)
@@ -820,8 +820,6 @@ void __init early_trap_init(void *vectors_base)
         */
        memcpy((void *)(vectors + KERN_SIGRETURN_CODE - CONFIG_VECTORS_BASE),
               sigreturn_codes, sizeof(sigreturn_codes));
-       memcpy((void *)(vectors + KERN_RESTART_CODE - CONFIG_VECTORS_BASE),
-              syscall_restart_code, sizeof(syscall_restart_code));
 
        flush_icache_range(vectors, vectors + PAGE_SIZE);
        modify_domain(DOMAIN_USER, DOMAIN_CLIENT);
index 43ebe909441108097a30f0c9c7fc605407105501..573be57d3d2805c70f24ab3b4dd29e1b3e6e3843 100644 (file)
@@ -62,6 +62,8 @@ config SOC_EXYNOS5250
        default y
        depends on ARCH_EXYNOS5
        select SAMSUNG_DMADEV
+       select S5P_PM if PM
+       select S5P_SLEEP if PM
        help
          Enable EXYNOS5250 SoC support
 
index 440a637c76f1933347050b82fb21e0275c1b327d..9b58024f7d43919fcc1c2c07ed6732d39bcd128d 100644 (file)
@@ -22,7 +22,7 @@ obj-$(CONFIG_PM)              += pm.o
 obj-$(CONFIG_PM_GENERIC_DOMAINS) += pm_domains.o
 obj-$(CONFIG_CPU_IDLE)         += cpuidle.o
 
-obj-$(CONFIG_ARCH_EXYNOS4)     += pmu.o
+obj-$(CONFIG_ARCH_EXYNOS     += pmu.o
 
 obj-$(CONFIG_SMP)              += platsmp.o headsmp.o
 
index 5aa460b01fdf41b7e476ed371b07de5fd747a88c..fefa336be2b4baf8d841a809766be3bdf9338f83 100644 (file)
 
 #ifdef CONFIG_PM_SLEEP
 static struct sleep_save exynos5_clock_save[] = {
-       /* will be implemented */
+       SAVE_ITEM(EXYNOS5_CLKSRC_MASK_TOP),
+       SAVE_ITEM(EXYNOS5_CLKSRC_MASK_GSCL),
+       SAVE_ITEM(EXYNOS5_CLKSRC_MASK_DISP1_0),
+       SAVE_ITEM(EXYNOS5_CLKSRC_MASK_FSYS),
+       SAVE_ITEM(EXYNOS5_CLKSRC_MASK_MAUDIO),
+       SAVE_ITEM(EXYNOS5_CLKSRC_MASK_PERIC0),
+       SAVE_ITEM(EXYNOS5_CLKSRC_MASK_PERIC1),
+       SAVE_ITEM(EXYNOS5_CLKGATE_IP_GSCL),
+       SAVE_ITEM(EXYNOS5_CLKGATE_IP_DISP1),
+       SAVE_ITEM(EXYNOS5_CLKGATE_IP_MFC),
+       SAVE_ITEM(EXYNOS5_CLKGATE_IP_G3D),
+       SAVE_ITEM(EXYNOS5_CLKGATE_IP_GEN),
+       SAVE_ITEM(EXYNOS5_CLKGATE_IP_FSYS),
+       SAVE_ITEM(EXYNOS5_CLKGATE_IP_PERIC),
+       SAVE_ITEM(EXYNOS5_CLKGATE_IP_PERIS),
+       SAVE_ITEM(EXYNOS5_CLKGATE_BLOCK),
+       SAVE_ITEM(EXYNOS5_CLKDIV_TOP0),
+       SAVE_ITEM(EXYNOS5_CLKDIV_TOP1),
+       SAVE_ITEM(EXYNOS5_CLKDIV_GSCL),
+       SAVE_ITEM(EXYNOS5_CLKDIV_DISP1_0),
+       SAVE_ITEM(EXYNOS5_CLKDIV_GEN),
+       SAVE_ITEM(EXYNOS5_CLKDIV_MAUDIO),
+       SAVE_ITEM(EXYNOS5_CLKDIV_FSYS0),
+       SAVE_ITEM(EXYNOS5_CLKDIV_FSYS1),
+       SAVE_ITEM(EXYNOS5_CLKDIV_FSYS2),
+       SAVE_ITEM(EXYNOS5_CLKDIV_FSYS3),
+       SAVE_ITEM(EXYNOS5_CLKDIV_PERIC0),
+       SAVE_ITEM(EXYNOS5_CLKDIV_PERIC1),
+       SAVE_ITEM(EXYNOS5_CLKDIV_PERIC2),
+       SAVE_ITEM(EXYNOS5_CLKDIV_PERIC3),
+       SAVE_ITEM(EXYNOS5_CLKDIV_PERIC4),
+       SAVE_ITEM(EXYNOS5_CLKDIV_PERIC5),
+       SAVE_ITEM(EXYNOS5_SCLK_DIV_ISP),
+       SAVE_ITEM(EXYNOS5_CLKSRC_TOP0),
+       SAVE_ITEM(EXYNOS5_CLKSRC_TOP1),
+       SAVE_ITEM(EXYNOS5_CLKSRC_TOP2),
+       SAVE_ITEM(EXYNOS5_CLKSRC_TOP3),
+       SAVE_ITEM(EXYNOS5_CLKSRC_GSCL),
+       SAVE_ITEM(EXYNOS5_CLKSRC_DISP1_0),
+       SAVE_ITEM(EXYNOS5_CLKSRC_MAUDIO),
+       SAVE_ITEM(EXYNOS5_CLKSRC_FSYS),
+       SAVE_ITEM(EXYNOS5_CLKSRC_PERIC0),
+       SAVE_ITEM(EXYNOS5_CLKSRC_PERIC1),
+       SAVE_ITEM(EXYNOS5_SCLK_SRC_ISP),
+       SAVE_ITEM(EXYNOS5_EPLL_CON0),
+       SAVE_ITEM(EXYNOS5_EPLL_CON1),
+       SAVE_ITEM(EXYNOS5_EPLL_CON2),
+       SAVE_ITEM(EXYNOS5_VPLL_CON0),
+       SAVE_ITEM(EXYNOS5_VPLL_CON1),
+       SAVE_ITEM(EXYNOS5_VPLL_CON2),
 };
 #endif
 
index 26dac2893b8e4f2a97bdaa0be50506b893083a50..cff0595d0d352c0d69cb9023b9b1cbf555fd16c4 100644 (file)
@@ -100,7 +100,7 @@ static int exynos4_enter_core0_aftr(struct cpuidle_device *dev,
        exynos4_set_wakeupmask();
 
        /* Set value of power down register for aftr mode */
-       exynos4_sys_powerdown_conf(SYS_AFTR);
+       exynos_sys_powerdown_conf(SYS_AFTR);
 
        __raw_writel(virt_to_phys(s3c_cpu_resume), REG_DIRECTGO_ADDR);
        __raw_writel(S5P_CHECK_AFTR, REG_DIRECTGO_FLAG);
index 9d8da51e35caa794ff8d45405c789fa00d2e009d..a67ecfaf12160646bc05680b299ca044f039bb6e 100644 (file)
@@ -33,7 +33,7 @@ static inline void s3c_pm_arch_prepare_irqs(void)
        __raw_writel(tmp, S5P_WAKEUP_MASK);
 
        __raw_writel(s3c_irqwake_intmask, S5P_WAKEUP_MASK);
-       __raw_writel(s3c_irqwake_eintmask, S5P_EINT_WAKEUP_MASK);
+       __raw_writel(s3c_irqwake_eintmask & 0xFFFFFFFE, S5P_EINT_WAKEUP_MASK);
 }
 
 static inline void s3c_pm_arch_stop_clocks(void)
index e76b7faba66b08da7f5da4c73816a15b924d7b3e..7c27c2d4bf44d2914c3c38a76c8b33b1f1ec611b 100644 (file)
@@ -23,12 +23,12 @@ enum sys_powerdown {
 };
 
 extern unsigned long l2x0_regs_phys;
-struct exynos4_pmu_conf {
+struct exynos_pmu_conf {
        void __iomem *reg;
        unsigned int val[NUM_SYS_POWERDOWN];
 };
 
-extern void exynos4_sys_powerdown_conf(enum sys_powerdown mode);
+extern void exynos_sys_powerdown_conf(enum sys_powerdown mode);
 extern void s3c_cpu_resume(void);
 
 #endif /* __ASM_ARCH_PMU_H */
index b78b5f3ad9c0d0168d8115e87d7fb2229b31c27c..8c9b38c9c5042d931ede0f3ab6f569cc3406dd86 100644 (file)
 
 #define EXYNOS5_CLKDIV_ACP                     EXYNOS_CLKREG(0x08500)
 
-#define EXYNOS5_CLKSRC_TOP2                    EXYNOS_CLKREG(0x10218)
 #define EXYNOS5_EPLL_CON0                      EXYNOS_CLKREG(0x10130)
 #define EXYNOS5_EPLL_CON1                      EXYNOS_CLKREG(0x10134)
+#define EXYNOS5_EPLL_CON2                      EXYNOS_CLKREG(0x10138)
 #define EXYNOS5_VPLL_CON0                      EXYNOS_CLKREG(0x10140)
 #define EXYNOS5_VPLL_CON1                      EXYNOS_CLKREG(0x10144)
+#define EXYNOS5_VPLL_CON2                      EXYNOS_CLKREG(0x10148)
 #define EXYNOS5_CPLL_CON0                      EXYNOS_CLKREG(0x10120)
 
 #define EXYNOS5_CLKSRC_TOP0                    EXYNOS_CLKREG(0x10210)
+#define EXYNOS5_CLKSRC_TOP1                    EXYNOS_CLKREG(0x10214)
+#define EXYNOS5_CLKSRC_TOP2                    EXYNOS_CLKREG(0x10218)
 #define EXYNOS5_CLKSRC_TOP3                    EXYNOS_CLKREG(0x1021C)
 #define EXYNOS5_CLKSRC_GSCL                    EXYNOS_CLKREG(0x10220)
 #define EXYNOS5_CLKSRC_DISP1_0                 EXYNOS_CLKREG(0x1022C)
+#define EXYNOS5_CLKSRC_MAUDIO                  EXYNOS_CLKREG(0x10240)
 #define EXYNOS5_CLKSRC_FSYS                    EXYNOS_CLKREG(0x10244)
 #define EXYNOS5_CLKSRC_PERIC0                  EXYNOS_CLKREG(0x10250)
+#define EXYNOS5_CLKSRC_PERIC1                  EXYNOS_CLKREG(0x10254)
+#define EXYNOS5_SCLK_SRC_ISP                   EXYNOS_CLKREG(0x10270)
 
 #define EXYNOS5_CLKSRC_MASK_TOP                        EXYNOS_CLKREG(0x10310)
 #define EXYNOS5_CLKSRC_MASK_GSCL               EXYNOS_CLKREG(0x10320)
 #define EXYNOS5_CLKSRC_MASK_DISP1_0            EXYNOS_CLKREG(0x1032C)
+#define EXYNOS5_CLKSRC_MASK_MAUDIO             EXYNOS_CLKREG(0x10334)
 #define EXYNOS5_CLKSRC_MASK_FSYS               EXYNOS_CLKREG(0x10340)
 #define EXYNOS5_CLKSRC_MASK_PERIC0             EXYNOS_CLKREG(0x10350)
+#define EXYNOS5_CLKSRC_MASK_PERIC1             EXYNOS_CLKREG(0x10354)
 
 #define EXYNOS5_CLKDIV_TOP0                    EXYNOS_CLKREG(0x10510)
 #define EXYNOS5_CLKDIV_TOP1                    EXYNOS_CLKREG(0x10514)
 #define EXYNOS5_CLKDIV_GSCL                    EXYNOS_CLKREG(0x10520)
 #define EXYNOS5_CLKDIV_DISP1_0                 EXYNOS_CLKREG(0x1052C)
 #define EXYNOS5_CLKDIV_GEN                     EXYNOS_CLKREG(0x1053C)
+#define EXYNOS5_CLKDIV_MAUDIO                  EXYNOS_CLKREG(0x10544)
 #define EXYNOS5_CLKDIV_FSYS0                   EXYNOS_CLKREG(0x10548)
 #define EXYNOS5_CLKDIV_FSYS1                   EXYNOS_CLKREG(0x1054C)
 #define EXYNOS5_CLKDIV_FSYS2                   EXYNOS_CLKREG(0x10550)
 #define EXYNOS5_CLKDIV_FSYS3                   EXYNOS_CLKREG(0x10554)
 #define EXYNOS5_CLKDIV_PERIC0                  EXYNOS_CLKREG(0x10558)
+#define EXYNOS5_CLKDIV_PERIC1                  EXYNOS_CLKREG(0x1055C)
+#define EXYNOS5_CLKDIV_PERIC2                  EXYNOS_CLKREG(0x10560)
+#define EXYNOS5_CLKDIV_PERIC3                  EXYNOS_CLKREG(0x10564)
+#define EXYNOS5_CLKDIV_PERIC4                  EXYNOS_CLKREG(0x10568)
+#define EXYNOS5_CLKDIV_PERIC5                  EXYNOS_CLKREG(0x1056C)
+#define EXYNOS5_SCLK_DIV_ISP                   EXYNOS_CLKREG(0x10580)
 
 #define EXYNOS5_CLKGATE_IP_ACP                 EXYNOS_CLKREG(0x08800)
 #define EXYNOS5_CLKGATE_IP_ISP0                        EXYNOS_CLKREG(0x0C800)
 #define EXYNOS5_CLKGATE_IP_GSCL                        EXYNOS_CLKREG(0x10920)
 #define EXYNOS5_CLKGATE_IP_DISP1               EXYNOS_CLKREG(0x10928)
 #define EXYNOS5_CLKGATE_IP_MFC                 EXYNOS_CLKREG(0x1092C)
+#define EXYNOS5_CLKGATE_IP_G3D                 EXYNOS_CLKREG(0x10930)
 #define EXYNOS5_CLKGATE_IP_GEN                 EXYNOS_CLKREG(0x10934)
 #define EXYNOS5_CLKGATE_IP_FSYS                        EXYNOS_CLKREG(0x10944)
 #define EXYNOS5_CLKGATE_IP_GPS                 EXYNOS_CLKREG(0x1094C)
index 4dbb8629b20025b091dd83016afa7f930accb776..43a99e6f56ab68e638621f8fb9e6b23c910005b3 100644 (file)
@@ -1,9 +1,8 @@
-/* linux/arch/arm/mach-exynos4/include/mach/regs-pmu.h
- *
- * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
+/*
+ * Copyright (c) 2010-2012 Samsung Electronics Co., Ltd.
  *             http://www.samsung.com
  *
- * EXYNOS4 - Power management unit definition
+ * EXYNOS - Power management unit definition
  *
  * 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
 #define S5P_DIS_IRQ_CORE3                      S5P_PMUREG(0x1034)
 #define S5P_DIS_IRQ_CENTRAL3                   S5P_PMUREG(0x1038)
 
+/* For EXYNOS5 */
+
+#define EXYNOS5_USB_CFG                                                S5P_PMUREG(0x0230)
+
+#define EXYNOS5_ARM_CORE0_SYS_PWR_REG                          S5P_PMUREG(0x1000)
+#define EXYNOS5_DIS_IRQ_ARM_CORE0_LOCAL_SYS_PWR_REG            S5P_PMUREG(0x1004)
+#define EXYNOS5_DIS_IRQ_ARM_CORE0_CENTRAL_SYS_PWR_REG          S5P_PMUREG(0x1008)
+#define EXYNOS5_ARM_CORE1_SYS_PWR_REG                          S5P_PMUREG(0x1010)
+#define EXYNOS5_DIS_IRQ_ARM_CORE1_LOCAL_SYS_PWR_REG            S5P_PMUREG(0x1014)
+#define EXYNOS5_DIS_IRQ_ARM_CORE1_CENTRAL_SYS_PWR_REG          S5P_PMUREG(0x1018)
+#define EXYNOS5_FSYS_ARM_SYS_PWR_REG                           S5P_PMUREG(0x1040)
+#define EXYNOS5_DIS_IRQ_FSYS_ARM_CENTRAL_SYS_PWR_REG           S5P_PMUREG(0x1048)
+#define EXYNOS5_ISP_ARM_SYS_PWR_REG                            S5P_PMUREG(0x1050)
+#define EXYNOS5_DIS_IRQ_ISP_ARM_LOCAL_SYS_PWR_REG              S5P_PMUREG(0x1054)
+#define EXYNOS5_DIS_IRQ_ISP_ARM_CENTRAL_SYS_PWR_REG            S5P_PMUREG(0x1058)
+#define EXYNOS5_ARM_COMMON_SYS_PWR_REG                         S5P_PMUREG(0x1080)
+#define EXYNOS5_ARM_L2_SYS_PWR_REG                             S5P_PMUREG(0x10C0)
+#define EXYNOS5_CMU_ACLKSTOP_SYS_PWR_REG                       S5P_PMUREG(0x1100)
+#define EXYNOS5_CMU_SCLKSTOP_SYS_PWR_REG                       S5P_PMUREG(0x1104)
+#define EXYNOS5_CMU_RESET_SYS_PWR_REG                          S5P_PMUREG(0x110C)
+#define EXYNOS5_CMU_ACLKSTOP_SYSMEM_SYS_PWR_REG                        S5P_PMUREG(0x1120)
+#define EXYNOS5_CMU_SCLKSTOP_SYSMEM_SYS_PWR_REG                        S5P_PMUREG(0x1124)
+#define EXYNOS5_CMU_RESET_SYSMEM_SYS_PWR_REG                   S5P_PMUREG(0x112C)
+#define EXYNOS5_DRAM_FREQ_DOWN_SYS_PWR_REG                     S5P_PMUREG(0x1130)
+#define EXYNOS5_DDRPHY_DLLOFF_SYS_PWR_REG                      S5P_PMUREG(0x1134)
+#define EXYNOS5_DDRPHY_DLLLOCK_SYS_PWR_REG                     S5P_PMUREG(0x1138)
+#define EXYNOS5_APLL_SYSCLK_SYS_PWR_REG                                S5P_PMUREG(0x1140)
+#define EXYNOS5_MPLL_SYSCLK_SYS_PWR_REG                                S5P_PMUREG(0x1144)
+#define EXYNOS5_VPLL_SYSCLK_SYS_PWR_REG                                S5P_PMUREG(0x1148)
+#define EXYNOS5_EPLL_SYSCLK_SYS_PWR_REG                                S5P_PMUREG(0x114C)
+#define EXYNOS5_BPLL_SYSCLK_SYS_PWR_REG                                S5P_PMUREG(0x1150)
+#define EXYNOS5_CPLL_SYSCLK_SYS_PWR_REG                                S5P_PMUREG(0x1154)
+#define EXYNOS5_MPLLUSER_SYSCLK_SYS_PWR_REG                    S5P_PMUREG(0x1164)
+#define EXYNOS5_BPLLUSER_SYSCLK_SYS_PWR_REG                    S5P_PMUREG(0x1170)
+#define EXYNOS5_TOP_BUS_SYS_PWR_REG                            S5P_PMUREG(0x1180)
+#define EXYNOS5_TOP_RETENTION_SYS_PWR_REG                      S5P_PMUREG(0x1184)
+#define EXYNOS5_TOP_PWR_SYS_PWR_REG                            S5P_PMUREG(0x1188)
+#define EXYNOS5_TOP_BUS_SYSMEM_SYS_PWR_REG                     S5P_PMUREG(0x1190)
+#define EXYNOS5_TOP_RETENTION_SYSMEM_SYS_PWR_REG               S5P_PMUREG(0x1194)
+#define EXYNOS5_TOP_PWR_SYSMEM_SYS_PWR_REG                     S5P_PMUREG(0x1198)
+#define EXYNOS5_LOGIC_RESET_SYS_PWR_REG                                S5P_PMUREG(0x11A0)
+#define EXYNOS5_OSCCLK_GATE_SYS_PWR_REG                                S5P_PMUREG(0x11A4)
+#define EXYNOS5_LOGIC_RESET_SYSMEM_SYS_PWR_REG                 S5P_PMUREG(0x11B0)
+#define EXYNOS5_OSCCLK_GATE_SYSMEM_SYS_PWR_REG                 S5P_PMUREG(0x11B4)
+#define EXYNOS5_USBOTG_MEM_SYS_PWR_REG                         S5P_PMUREG(0x11C0)
+#define EXYNOS5_G2D_MEM_SYS_PWR_REG                            S5P_PMUREG(0x11C8)
+#define EXYNOS5_USBDRD_MEM_SYS_PWR_REG                         S5P_PMUREG(0x11CC)
+#define EXYNOS5_SDMMC_MEM_SYS_PWR_REG                          S5P_PMUREG(0x11D0)
+#define EXYNOS5_CSSYS_MEM_SYS_PWR_REG                          S5P_PMUREG(0x11D4)
+#define EXYNOS5_SECSS_MEM_SYS_PWR_REG                          S5P_PMUREG(0x11D8)
+#define EXYNOS5_ROTATOR_MEM_SYS_PWR_REG                                S5P_PMUREG(0x11DC)
+#define EXYNOS5_INTRAM_MEM_SYS_PWR_REG                         S5P_PMUREG(0x11E0)
+#define EXYNOS5_INTROM_MEM_SYS_PWR_REG                         S5P_PMUREG(0x11E4)
+#define EXYNOS5_JPEG_MEM_SYS_PWR_REG                           S5P_PMUREG(0x11E8)
+#define EXYNOS5_HSI_MEM_SYS_PWR_REG                            S5P_PMUREG(0x11EC)
+#define EXYNOS5_MCUIOP_MEM_SYS_PWR_REG                         S5P_PMUREG(0x11F4)
+#define EXYNOS5_SATA_MEM_SYS_PWR_REG                           S5P_PMUREG(0x11FC)
+#define EXYNOS5_PAD_RETENTION_DRAM_SYS_PWR_REG                 S5P_PMUREG(0x1200)
+#define EXYNOS5_PAD_RETENTION_MAU_SYS_PWR_REG                  S5P_PMUREG(0x1204)
+#define EXYNOS5_PAD_RETENTION_EFNAND_SYS_PWR_REG               S5P_PMUREG(0x1208)
+#define EXYNOS5_PAD_RETENTION_GPIO_SYS_PWR_REG                 S5P_PMUREG(0x1220)
+#define EXYNOS5_PAD_RETENTION_UART_SYS_PWR_REG                 S5P_PMUREG(0x1224)
+#define EXYNOS5_PAD_RETENTION_MMCA_SYS_PWR_REG                 S5P_PMUREG(0x1228)
+#define EXYNOS5_PAD_RETENTION_MMCB_SYS_PWR_REG                 S5P_PMUREG(0x122C)
+#define EXYNOS5_PAD_RETENTION_EBIA_SYS_PWR_REG                 S5P_PMUREG(0x1230)
+#define EXYNOS5_PAD_RETENTION_EBIB_SYS_PWR_REG                 S5P_PMUREG(0x1234)
+#define EXYNOS5_PAD_RETENTION_SPI_SYS_PWR_REG                  S5P_PMUREG(0x1238)
+#define EXYNOS5_PAD_RETENTION_GPIO_SYSMEM_SYS_PWR_REG          S5P_PMUREG(0x123C)
+#define EXYNOS5_PAD_ISOLATION_SYS_PWR_REG                      S5P_PMUREG(0x1240)
+#define EXYNOS5_PAD_ISOLATION_SYSMEM_SYS_PWR_REG               S5P_PMUREG(0x1250)
+#define EXYNOS5_PAD_ALV_SEL_SYS_PWR_REG                                S5P_PMUREG(0x1260)
+#define EXYNOS5_XUSBXTI_SYS_PWR_REG                            S5P_PMUREG(0x1280)
+#define EXYNOS5_XXTI_SYS_PWR_REG                               S5P_PMUREG(0x1284)
+#define EXYNOS5_EXT_REGULATOR_SYS_PWR_REG                      S5P_PMUREG(0x12C0)
+#define EXYNOS5_GPIO_MODE_SYS_PWR_REG                          S5P_PMUREG(0x1300)
+#define EXYNOS5_GPIO_MODE_SYSMEM_SYS_PWR_REG                   S5P_PMUREG(0x1320)
+#define EXYNOS5_GPIO_MODE_MAU_SYS_PWR_REG                      S5P_PMUREG(0x1340)
+#define EXYNOS5_TOP_ASB_RESET_SYS_PWR_REG                      S5P_PMUREG(0x1344)
+#define EXYNOS5_TOP_ASB_ISOLATION_SYS_PWR_REG                  S5P_PMUREG(0x1348)
+#define EXYNOS5_GSCL_SYS_PWR_REG                               S5P_PMUREG(0x1400)
+#define EXYNOS5_ISP_SYS_PWR_REG                                        S5P_PMUREG(0x1404)
+#define EXYNOS5_MFC_SYS_PWR_REG                                        S5P_PMUREG(0x1408)
+#define EXYNOS5_G3D_SYS_PWR_REG                                        S5P_PMUREG(0x140C)
+#define EXYNOS5_DISP1_SYS_PWR_REG                              S5P_PMUREG(0x1414)
+#define EXYNOS5_MAU_SYS_PWR_REG                                        S5P_PMUREG(0x1418)
+#define EXYNOS5_CMU_CLKSTOP_GSCL_SYS_PWR_REG                   S5P_PMUREG(0x1480)
+#define EXYNOS5_CMU_CLKSTOP_ISP_SYS_PWR_REG                    S5P_PMUREG(0x1484)
+#define EXYNOS5_CMU_CLKSTOP_MFC_SYS_PWR_REG                    S5P_PMUREG(0x1488)
+#define EXYNOS5_CMU_CLKSTOP_G3D_SYS_PWR_REG                    S5P_PMUREG(0x148C)
+#define EXYNOS5_CMU_CLKSTOP_DISP1_SYS_PWR_REG                  S5P_PMUREG(0x1494)
+#define EXYNOS5_CMU_CLKSTOP_MAU_SYS_PWR_REG                    S5P_PMUREG(0x1498)
+#define EXYNOS5_CMU_SYSCLK_GSCL_SYS_PWR_REG                    S5P_PMUREG(0x14C0)
+#define EXYNOS5_CMU_SYSCLK_ISP_SYS_PWR_REG                     S5P_PMUREG(0x14C4)
+#define EXYNOS5_CMU_SYSCLK_MFC_SYS_PWR_REG                     S5P_PMUREG(0x14C8)
+#define EXYNOS5_CMU_SYSCLK_G3D_SYS_PWR_REG                     S5P_PMUREG(0x14CC)
+#define EXYNOS5_CMU_SYSCLK_DISP1_SYS_PWR_REG                   S5P_PMUREG(0x14D4)
+#define EXYNOS5_CMU_SYSCLK_MAU_SYS_PWR_REG                     S5P_PMUREG(0x14D8)
+#define EXYNOS5_CMU_RESET_GSCL_SYS_PWR_REG                     S5P_PMUREG(0x1580)
+#define EXYNOS5_CMU_RESET_ISP_SYS_PWR_REG                      S5P_PMUREG(0x1584)
+#define EXYNOS5_CMU_RESET_MFC_SYS_PWR_REG                      S5P_PMUREG(0x1588)
+#define EXYNOS5_CMU_RESET_G3D_SYS_PWR_REG                      S5P_PMUREG(0x158C)
+#define EXYNOS5_CMU_RESET_DISP1_SYS_PWR_REG                    S5P_PMUREG(0x1594)
+#define EXYNOS5_CMU_RESET_MAU_SYS_PWR_REG                      S5P_PMUREG(0x1598)
+
+#define EXYNOS5_ARM_CORE0_OPTION                               S5P_PMUREG(0x2008)
+#define EXYNOS5_ARM_CORE1_OPTION                               S5P_PMUREG(0x2088)
+#define EXYNOS5_FSYS_ARM_OPTION                                        S5P_PMUREG(0x2208)
+#define EXYNOS5_ISP_ARM_OPTION                                 S5P_PMUREG(0x2288)
+#define EXYNOS5_ARM_COMMON_OPTION                              S5P_PMUREG(0x2408)
+#define EXYNOS5_TOP_PWR_OPTION                                 S5P_PMUREG(0x2C48)
+#define EXYNOS5_TOP_PWR_SYSMEM_OPTION                          S5P_PMUREG(0x2CC8)
+#define EXYNOS5_JPEG_MEM_OPTION                                        S5P_PMUREG(0x2F48)
+#define EXYNOS5_GSCL_STATUS                                    S5P_PMUREG(0x4004)
+#define EXYNOS5_ISP_STATUS                                     S5P_PMUREG(0x4024)
+#define EXYNOS5_GSCL_OPTION                                    S5P_PMUREG(0x4008)
+#define EXYNOS5_ISP_OPTION                                     S5P_PMUREG(0x4028)
+#define EXYNOS5_MFC_OPTION                                     S5P_PMUREG(0x4048)
+#define EXYNOS5_G3D_CONFIGURATION                              S5P_PMUREG(0x4060)
+#define EXYNOS5_G3D_STATUS                                     S5P_PMUREG(0x4064)
+#define EXYNOS5_G3D_OPTION                                     S5P_PMUREG(0x4068)
+#define EXYNOS5_DISP1_OPTION                                   S5P_PMUREG(0x40A8)
+#define EXYNOS5_MAU_OPTION                                     S5P_PMUREG(0x40C8)
+
+#define EXYNOS5_USE_SC_FEEDBACK                                        (1 << 1)
+#define EXYNOS5_USE_SC_COUNTER                                 (1 << 0)
+
+#define EXYNOS5_MANUAL_L2RSTDISABLE_CONTROL                    (1 << 2)
+#define EXYNOS5_SKIP_DEACTIVATE_ACEACP_IN_PWDN                 (1 << 7)
+
+#define EXYNOS5_OPTION_USE_STANDBYWFE                          (1 << 24)
+#define EXYNOS5_OPTION_USE_STANDBYWFI                          (1 << 16)
+
+#define EXYNOS5_OPTION_USE_RETENTION                           (1 << 4)
+
 #endif /* __ASM_ARCH_REGS_PMU_H */
index 563dea9a6dbb224ab850e8bb3490da95e52eb791..c06c992943a139bc3017854b19c4eba1cb09d4c5 100644 (file)
@@ -1,9 +1,8 @@
-/* linux/arch/arm/mach-exynos4/pm.c
- *
- * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+/*
+ * Copyright (c) 2011-2012 Samsung Electronics Co., Ltd.
  *             http://www.samsung.com
  *
- * EXYNOS4210 - Power Management support
+ * EXYNOS - Power Management support
  *
  * Based on arch/arm/mach-s3c2410/pm.c
  * Copyright (c) 2006 Simtec Electronics
@@ -63,90 +62,7 @@ static struct sleep_save exynos4_vpll_save[] = {
        SAVE_ITEM(EXYNOS4_VPLL_CON1),
 };
 
-static struct sleep_save exynos4_core_save[] = {
-       /* GIC side */
-       SAVE_ITEM(S5P_VA_GIC_CPU + 0x000),
-       SAVE_ITEM(S5P_VA_GIC_CPU + 0x004),
-       SAVE_ITEM(S5P_VA_GIC_CPU + 0x008),
-       SAVE_ITEM(S5P_VA_GIC_CPU + 0x00C),
-       SAVE_ITEM(S5P_VA_GIC_CPU + 0x014),
-       SAVE_ITEM(S5P_VA_GIC_CPU + 0x018),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x000),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x004),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x100),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x104),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x108),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x300),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x304),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x308),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x400),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x404),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x408),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x40C),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x410),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x414),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x418),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x41C),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x420),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x424),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x428),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x42C),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x430),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x434),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x438),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x43C),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x440),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x444),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x448),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x44C),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x450),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x454),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x458),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x45C),
-
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x800),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x804),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x808),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x80C),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x810),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x814),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x818),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x81C),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x820),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x824),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x828),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x82C),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x830),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x834),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x838),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x83C),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x840),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x844),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x848),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x84C),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x850),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x854),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x858),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0x85C),
-
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0xC00),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0xC04),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0xC08),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0xC0C),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0xC10),
-       SAVE_ITEM(S5P_VA_GIC_DIST + 0xC14),
-
-       SAVE_ITEM(S5P_VA_COMBINER_BASE + 0x000),
-       SAVE_ITEM(S5P_VA_COMBINER_BASE + 0x010),
-       SAVE_ITEM(S5P_VA_COMBINER_BASE + 0x020),
-       SAVE_ITEM(S5P_VA_COMBINER_BASE + 0x030),
-       SAVE_ITEM(S5P_VA_COMBINER_BASE + 0x040),
-       SAVE_ITEM(S5P_VA_COMBINER_BASE + 0x050),
-       SAVE_ITEM(S5P_VA_COMBINER_BASE + 0x060),
-       SAVE_ITEM(S5P_VA_COMBINER_BASE + 0x070),
-       SAVE_ITEM(S5P_VA_COMBINER_BASE + 0x080),
-       SAVE_ITEM(S5P_VA_COMBINER_BASE + 0x090),
-
+static struct sleep_save exynos_core_save[] = {
        /* SROM side */
        SAVE_ITEM(S5P_SROM_BW),
        SAVE_ITEM(S5P_SROM_BC0),
@@ -159,9 +75,11 @@ static struct sleep_save exynos4_core_save[] = {
 /* For Cortex-A9 Diagnostic and Power control register */
 static unsigned int save_arm_register[2];
 
-static int exynos4_cpu_suspend(unsigned long arg)
+static int exynos_cpu_suspend(unsigned long arg)
 {
+#ifdef CONFIG_CACHE_L2X0
        outer_flush_all();
+#endif
 
        /* issue the standby signal into the pm unit. */
        cpu_do_idle();
@@ -170,19 +88,25 @@ static int exynos4_cpu_suspend(unsigned long arg)
        panic("sleep resumed to originator?");
 }
 
-static void exynos4_pm_prepare(void)
+static void exynos_pm_prepare(void)
 {
-       u32 tmp;
+       unsigned int tmp;
 
-       s3c_pm_do_save(exynos4_core_save, ARRAY_SIZE(exynos4_core_save));
-       s3c_pm_do_save(exynos4_epll_save, ARRAY_SIZE(exynos4_epll_save));
-       s3c_pm_do_save(exynos4_vpll_save, ARRAY_SIZE(exynos4_vpll_save));
+       s3c_pm_do_save(exynos_core_save, ARRAY_SIZE(exynos_core_save));
 
-       tmp = __raw_readl(S5P_INFORM1);
+       if (!soc_is_exynos5250()) {
+               s3c_pm_do_save(exynos4_epll_save, ARRAY_SIZE(exynos4_epll_save));
+               s3c_pm_do_save(exynos4_vpll_save, ARRAY_SIZE(exynos4_vpll_save));
+       } else {
+               /* Disable USE_RETENTION of JPEG_MEM_OPTION */
+               tmp = __raw_readl(EXYNOS5_JPEG_MEM_OPTION);
+               tmp &= ~EXYNOS5_OPTION_USE_RETENTION;
+               __raw_writel(tmp, EXYNOS5_JPEG_MEM_OPTION);
+       }
 
        /* Set value of power down register for sleep mode */
 
-       exynos4_sys_powerdown_conf(SYS_SLEEP);
+       exynos_sys_powerdown_conf(SYS_SLEEP);
        __raw_writel(S5P_CHECK_SLEEP, S5P_INFORM1);
 
        /* ensure at least INFORM0 has the resume address */
@@ -191,17 +115,18 @@ static void exynos4_pm_prepare(void)
 
        /* Before enter central sequence mode, clock src register have to set */
 
-       s3c_pm_do_restore_core(exynos4_set_clksrc, ARRAY_SIZE(exynos4_set_clksrc));
+       if (!soc_is_exynos5250())
+               s3c_pm_do_restore_core(exynos4_set_clksrc, ARRAY_SIZE(exynos4_set_clksrc));
 
        if (soc_is_exynos4210())
                s3c_pm_do_restore_core(exynos4210_set_clksrc, ARRAY_SIZE(exynos4210_set_clksrc));
 
 }
 
-static int exynos4_pm_add(struct device *dev, struct subsys_interface *sif)
+static int exynos_pm_add(struct device *dev, struct subsys_interface *sif)
 {
-       pm_cpu_prep = exynos4_pm_prepare;
-       pm_cpu_sleep = exynos4_cpu_suspend;
+       pm_cpu_prep = exynos_pm_prepare;
+       pm_cpu_sleep = exynos_cpu_suspend;
 
        return 0;
 }
@@ -273,13 +198,13 @@ static void exynos4_restore_pll(void)
        } while (epll_wait || vpll_wait);
 }
 
-static struct subsys_interface exynos4_pm_interface = {
-       .name           = "exynos4_pm",
+static struct subsys_interface exynos_pm_interface = {
+       .name           = "exynos_pm",
        .subsys         = &exynos_subsys,
-       .add_dev        = exynos4_pm_add,
+       .add_dev        = exynos_pm_add,
 };
 
-static __init int exynos4_pm_drvinit(void)
+static __init int exynos_pm_drvinit(void)
 {
        struct clk *pll_base;
        unsigned int tmp;
@@ -292,18 +217,20 @@ static __init int exynos4_pm_drvinit(void)
        tmp |= ((0xFF << 8) | (0x1F << 1));
        __raw_writel(tmp, S5P_WAKEUP_MASK);
 
-       pll_base = clk_get(NULL, "xtal");
+       if (!soc_is_exynos5250()) {
+               pll_base = clk_get(NULL, "xtal");
 
-       if (!IS_ERR(pll_base)) {
-               pll_base_rate = clk_get_rate(pll_base);
-               clk_put(pll_base);
+               if (!IS_ERR(pll_base)) {
+                       pll_base_rate = clk_get_rate(pll_base);
+                       clk_put(pll_base);
+               }
        }
 
-       return subsys_interface_register(&exynos4_pm_interface);
+       return subsys_interface_register(&exynos_pm_interface);
 }
-arch_initcall(exynos4_pm_drvinit);
+arch_initcall(exynos_pm_drvinit);
 
-static int exynos4_pm_suspend(void)
+static int exynos_pm_suspend(void)
 {
        unsigned long tmp;
 
@@ -313,27 +240,27 @@ static int exynos4_pm_suspend(void)
        tmp &= ~S5P_CENTRAL_LOWPWR_CFG;
        __raw_writel(tmp, S5P_CENTRAL_SEQ_CONFIGURATION);
 
-       if (soc_is_exynos4212() || soc_is_exynos4412()) {
-               tmp = __raw_readl(S5P_CENTRAL_SEQ_OPTION);
-               tmp &= ~(S5P_USE_STANDBYWFI_ISP_ARM |
-                        S5P_USE_STANDBYWFE_ISP_ARM);
-               __raw_writel(tmp, S5P_CENTRAL_SEQ_OPTION);
-       }
+       /* Setting SEQ_OPTION register */
+
+       tmp = (S5P_USE_STANDBY_WFI0 | S5P_USE_STANDBY_WFE0);
+       __raw_writel(tmp, S5P_CENTRAL_SEQ_OPTION);
 
-       /* Save Power control register */
-       asm ("mrc p15, 0, %0, c15, c0, 0"
-            : "=r" (tmp) : : "cc");
-       save_arm_register[0] = tmp;
+       if (!soc_is_exynos5250()) {
+               /* Save Power control register */
+               asm ("mrc p15, 0, %0, c15, c0, 0"
+                    : "=r" (tmp) : : "cc");
+               save_arm_register[0] = tmp;
 
-       /* Save Diagnostic register */
-       asm ("mrc p15, 0, %0, c15, c0, 1"
-            : "=r" (tmp) : : "cc");
-       save_arm_register[1] = tmp;
+               /* Save Diagnostic register */
+               asm ("mrc p15, 0, %0, c15, c0, 1"
+                    : "=r" (tmp) : : "cc");
+               save_arm_register[1] = tmp;
+       }
 
        return 0;
 }
 
-static void exynos4_pm_resume(void)
+static void exynos_pm_resume(void)
 {
        unsigned long tmp;
 
@@ -350,17 +277,19 @@ static void exynos4_pm_resume(void)
                /* No need to perform below restore code */
                goto early_wakeup;
        }
-       /* Restore Power control register */
-       tmp = save_arm_register[0];
-       asm volatile ("mcr p15, 0, %0, c15, c0, 0"
-                     : : "r" (tmp)
-                     : "cc");
-
-       /* Restore Diagnostic register */
-       tmp = save_arm_register[1];
-       asm volatile ("mcr p15, 0, %0, c15, c0, 1"
-                     : : "r" (tmp)
-                     : "cc");
+       if (!soc_is_exynos5250()) {
+               /* Restore Power control register */
+               tmp = save_arm_register[0];
+               asm volatile ("mcr p15, 0, %0, c15, c0, 0"
+                             : : "r" (tmp)
+                             : "cc");
+
+               /* Restore Diagnostic register */
+               tmp = save_arm_register[1];
+               asm volatile ("mcr p15, 0, %0, c15, c0, 1"
+                             : : "r" (tmp)
+                             : "cc");
+       }
 
        /* For release retention */
 
@@ -372,26 +301,28 @@ static void exynos4_pm_resume(void)
        __raw_writel((1 << 28), S5P_PAD_RET_EBIA_OPTION);
        __raw_writel((1 << 28), S5P_PAD_RET_EBIB_OPTION);
 
-       s3c_pm_do_restore_core(exynos4_core_save, ARRAY_SIZE(exynos4_core_save));
+       s3c_pm_do_restore_core(exynos_core_save, ARRAY_SIZE(exynos_core_save));
 
-       exynos4_restore_pll();
+       if (!soc_is_exynos5250()) {
+               exynos4_restore_pll();
 
 #ifdef CONFIG_SMP
-       scu_enable(S5P_VA_SCU);
+               scu_enable(S5P_VA_SCU);
 #endif
+       }
 
 early_wakeup:
        return;
 }
 
-static struct syscore_ops exynos4_pm_syscore_ops = {
-       .suspend        = exynos4_pm_suspend,
-       .resume         = exynos4_pm_resume,
+static struct syscore_ops exynos_pm_syscore_ops = {
+       .suspend        = exynos_pm_suspend,
+       .resume         = exynos_pm_resume,
 };
 
-static __init int exynos4_pm_syscore_init(void)
+static __init int exynos_pm_syscore_init(void)
 {
-       register_syscore_ops(&exynos4_pm_syscore_ops);
+       register_syscore_ops(&exynos_pm_syscore_ops);
        return 0;
 }
-arch_initcall(exynos4_pm_syscore_init);
+arch_initcall(exynos_pm_syscore_init);
index 77c6815eebeea9c6070ccab7474f5282368b037e..4aacb66f71618da5d865a04a868878ef380aebea 100644 (file)
@@ -1,9 +1,8 @@
-/* linux/arch/arm/mach-exynos4/pmu.c
- *
- * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+/*
+ * Copyright (c) 2011-2012 Samsung Electronics Co., Ltd.
  *             http://www.samsung.com/
  *
- * EXYNOS4210 - CPU PMU(Power Management Unit) support
+ * EXYNOS - CPU PMU(Power Management Unit) support
  *
  * 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
 
 #include <linux/io.h>
 #include <linux/kernel.h>
+#include <linux/bug.h>
 
 #include <mach/regs-clock.h>
 #include <mach/pmu.h>
 
-static struct exynos4_pmu_conf *exynos4_pmu_config;
+static struct exynos_pmu_conf *exynos_pmu_config;
 
-static struct exynos4_pmu_conf exynos4210_pmu_config[] = {
+static struct exynos_pmu_conf exynos4210_pmu_config[] = {
        /* { .reg = address, .val = { AFTR, LPA, SLEEP } */
        { S5P_ARM_CORE0_LOWPWR,                 { 0x0, 0x0, 0x2 } },
        { S5P_DIS_IRQ_CORE0,                    { 0x0, 0x0, 0x0 } },
@@ -94,7 +94,7 @@ static struct exynos4_pmu_conf exynos4210_pmu_config[] = {
        { PMU_TABLE_END,},
 };
 
-static struct exynos4_pmu_conf exynos4x12_pmu_config[] = {
+static struct exynos_pmu_conf exynos4x12_pmu_config[] = {
        { S5P_ARM_CORE0_LOWPWR,                 { 0x0, 0x0, 0x2 } },
        { S5P_DIS_IRQ_CORE0,                    { 0x0, 0x0, 0x0 } },
        { S5P_DIS_IRQ_CENTRAL0,                 { 0x0, 0x0, 0x0 } },
@@ -202,7 +202,7 @@ static struct exynos4_pmu_conf exynos4x12_pmu_config[] = {
        { PMU_TABLE_END,},
 };
 
-static struct exynos4_pmu_conf exynos4412_pmu_config[] = {
+static struct exynos_pmu_conf exynos4412_pmu_config[] = {
        { S5P_ARM_CORE2_LOWPWR,                 { 0x0, 0x0, 0x2 } },
        { S5P_DIS_IRQ_CORE2,                    { 0x0, 0x0, 0x0 } },
        { S5P_DIS_IRQ_CENTRAL2,                 { 0x0, 0x0, 0x0 } },
@@ -212,13 +212,174 @@ static struct exynos4_pmu_conf exynos4412_pmu_config[] = {
        { PMU_TABLE_END,},
 };
 
-void exynos4_sys_powerdown_conf(enum sys_powerdown mode)
+static struct exynos_pmu_conf exynos5250_pmu_config[] = {
+       /* { .reg = address, .val = { AFTR, LPA, SLEEP } */
+       { EXYNOS5_ARM_CORE0_SYS_PWR_REG,                { 0x0, 0x0, 0x2} },
+       { EXYNOS5_DIS_IRQ_ARM_CORE0_LOCAL_SYS_PWR_REG,  { 0x0, 0x0, 0x0} },
+       { EXYNOS5_DIS_IRQ_ARM_CORE0_CENTRAL_SYS_PWR_REG,        { 0x0, 0x0, 0x0} },
+       { EXYNOS5_ARM_CORE1_SYS_PWR_REG,                { 0x0, 0x0, 0x2} },
+       { EXYNOS5_DIS_IRQ_ARM_CORE1_LOCAL_SYS_PWR_REG,  { 0x0, 0x0, 0x0} },
+       { EXYNOS5_DIS_IRQ_ARM_CORE1_CENTRAL_SYS_PWR_REG,        { 0x0, 0x0, 0x0} },
+       { EXYNOS5_FSYS_ARM_SYS_PWR_REG,                 { 0x1, 0x0, 0x0} },
+       { EXYNOS5_DIS_IRQ_FSYS_ARM_CENTRAL_SYS_PWR_REG, { 0x1, 0x1, 0x1} },
+       { EXYNOS5_ISP_ARM_SYS_PWR_REG,                  { 0x1, 0x0, 0x0} },
+       { EXYNOS5_DIS_IRQ_ISP_ARM_LOCAL_SYS_PWR_REG,    { 0x0, 0x0, 0x0} },
+       { EXYNOS5_DIS_IRQ_ISP_ARM_CENTRAL_SYS_PWR_REG,  { 0x0, 0x0, 0x0} },
+       { EXYNOS5_ARM_COMMON_SYS_PWR_REG,               { 0x0, 0x0, 0x2} },
+       { EXYNOS5_ARM_L2_SYS_PWR_REG,                   { 0x3, 0x3, 0x3} },
+       { EXYNOS5_CMU_ACLKSTOP_SYS_PWR_REG,             { 0x1, 0x0, 0x1} },
+       { EXYNOS5_CMU_SCLKSTOP_SYS_PWR_REG,             { 0x1, 0x0, 0x1} },
+       { EXYNOS5_CMU_RESET_SYS_PWR_REG,                { 0x1, 0x1, 0x0} },
+       { EXYNOS5_CMU_ACLKSTOP_SYSMEM_SYS_PWR_REG,      { 0x1, 0x0, 0x1} },
+       { EXYNOS5_CMU_SCLKSTOP_SYSMEM_SYS_PWR_REG,      { 0x1, 0x0, 0x1} },
+       { EXYNOS5_CMU_RESET_SYSMEM_SYS_PWR_REG,         { 0x1, 0x1, 0x0} },
+       { EXYNOS5_DRAM_FREQ_DOWN_SYS_PWR_REG,           { 0x1, 0x1, 0x1} },
+       { EXYNOS5_DDRPHY_DLLOFF_SYS_PWR_REG,            { 0x1, 0x1, 0x1} },
+       { EXYNOS5_DDRPHY_DLLLOCK_SYS_PWR_REG,           { 0x1, 0x1, 0x1} },
+       { EXYNOS5_APLL_SYSCLK_SYS_PWR_REG,              { 0x1, 0x0, 0x0} },
+       { EXYNOS5_MPLL_SYSCLK_SYS_PWR_REG,              { 0x1, 0x0, 0x0} },
+       { EXYNOS5_VPLL_SYSCLK_SYS_PWR_REG,              { 0x1, 0x0, 0x0} },
+       { EXYNOS5_EPLL_SYSCLK_SYS_PWR_REG,              { 0x1, 0x1, 0x0} },
+       { EXYNOS5_BPLL_SYSCLK_SYS_PWR_REG,              { 0x1, 0x0, 0x0} },
+       { EXYNOS5_CPLL_SYSCLK_SYS_PWR_REG,              { 0x1, 0x0, 0x0} },
+       { EXYNOS5_MPLLUSER_SYSCLK_SYS_PWR_REG,          { 0x1, 0x0, 0x0} },
+       { EXYNOS5_BPLLUSER_SYSCLK_SYS_PWR_REG,          { 0x1, 0x0, 0x0} },
+       { EXYNOS5_TOP_BUS_SYS_PWR_REG,                  { 0x3, 0x0, 0x0} },
+       { EXYNOS5_TOP_RETENTION_SYS_PWR_REG,            { 0x1, 0x0, 0x1} },
+       { EXYNOS5_TOP_PWR_SYS_PWR_REG,                  { 0x3, 0x0, 0x3} },
+       { EXYNOS5_TOP_BUS_SYSMEM_SYS_PWR_REG,           { 0x3, 0x0, 0x0} },
+       { EXYNOS5_TOP_RETENTION_SYSMEM_SYS_PWR_REG,     { 0x1, 0x0, 0x1} },
+       { EXYNOS5_TOP_PWR_SYSMEM_SYS_PWR_REG,           { 0x3, 0x0, 0x3} },
+       { EXYNOS5_LOGIC_RESET_SYS_PWR_REG,              { 0x1, 0x1, 0x0} },
+       { EXYNOS5_OSCCLK_GATE_SYS_PWR_REG,              { 0x1, 0x0, 0x1} },
+       { EXYNOS5_LOGIC_RESET_SYSMEM_SYS_PWR_REG,       { 0x1, 0x1, 0x0} },
+       { EXYNOS5_OSCCLK_GATE_SYSMEM_SYS_PWR_REG,       { 0x1, 0x0, 0x1} },
+       { EXYNOS5_USBOTG_MEM_SYS_PWR_REG,               { 0x3, 0x0, 0x0} },
+       { EXYNOS5_G2D_MEM_SYS_PWR_REG,                  { 0x3, 0x0, 0x0} },
+       { EXYNOS5_USBDRD_MEM_SYS_PWR_REG,               { 0x3, 0x0, 0x0} },
+       { EXYNOS5_SDMMC_MEM_SYS_PWR_REG,                { 0x3, 0x0, 0x0} },
+       { EXYNOS5_CSSYS_MEM_SYS_PWR_REG,                { 0x3, 0x0, 0x0} },
+       { EXYNOS5_SECSS_MEM_SYS_PWR_REG,                { 0x3, 0x0, 0x0} },
+       { EXYNOS5_ROTATOR_MEM_SYS_PWR_REG,              { 0x3, 0x0, 0x0} },
+       { EXYNOS5_INTRAM_MEM_SYS_PWR_REG,               { 0x3, 0x0, 0x0} },
+       { EXYNOS5_INTROM_MEM_SYS_PWR_REG,               { 0x3, 0x0, 0x0} },
+       { EXYNOS5_JPEG_MEM_SYS_PWR_REG,                 { 0x3, 0x0, 0x0} },
+       { EXYNOS5_HSI_MEM_SYS_PWR_REG,                  { 0x3, 0x0, 0x0} },
+       { EXYNOS5_MCUIOP_MEM_SYS_PWR_REG,               { 0x3, 0x0, 0x0} },
+       { EXYNOS5_SATA_MEM_SYS_PWR_REG,                 { 0x3, 0x0, 0x0} },
+       { EXYNOS5_PAD_RETENTION_DRAM_SYS_PWR_REG,       { 0x1, 0x0, 0x0} },
+       { EXYNOS5_PAD_RETENTION_MAU_SYS_PWR_REG,        { 0x1, 0x1, 0x0} },
+       { EXYNOS5_PAD_RETENTION_GPIO_SYS_PWR_REG,       { 0x1, 0x0, 0x0} },
+       { EXYNOS5_PAD_RETENTION_UART_SYS_PWR_REG,       { 0x1, 0x0, 0x0} },
+       { EXYNOS5_PAD_RETENTION_MMCA_SYS_PWR_REG,       { 0x1, 0x0, 0x0} },
+       { EXYNOS5_PAD_RETENTION_MMCB_SYS_PWR_REG,       { 0x1, 0x0, 0x0} },
+       { EXYNOS5_PAD_RETENTION_EBIA_SYS_PWR_REG,       { 0x1, 0x0, 0x0} },
+       { EXYNOS5_PAD_RETENTION_EBIB_SYS_PWR_REG,       { 0x1, 0x0, 0x0} },
+       { EXYNOS5_PAD_RETENTION_SPI_SYS_PWR_REG,        { 0x1, 0x0, 0x0} },
+       { EXYNOS5_PAD_RETENTION_GPIO_SYSMEM_SYS_PWR_REG,        { 0x1, 0x0, 0x0} },
+       { EXYNOS5_PAD_ISOLATION_SYS_PWR_REG,            { 0x1, 0x0, 0x0} },
+       { EXYNOS5_PAD_ISOLATION_SYSMEM_SYS_PWR_REG,     { 0x1, 0x0, 0x0} },
+       { EXYNOS5_PAD_ALV_SEL_SYS_PWR_REG,              { 0x1, 0x0, 0x0} },
+       { EXYNOS5_XUSBXTI_SYS_PWR_REG,                  { 0x1, 0x1, 0x1} },
+       { EXYNOS5_XXTI_SYS_PWR_REG,                     { 0x1, 0x1, 0x0} },
+       { EXYNOS5_EXT_REGULATOR_SYS_PWR_REG,            { 0x1, 0x1, 0x0} },
+       { EXYNOS5_GPIO_MODE_SYS_PWR_REG,                { 0x1, 0x0, 0x0} },
+       { EXYNOS5_GPIO_MODE_SYSMEM_SYS_PWR_REG,         { 0x1, 0x0, 0x0} },
+       { EXYNOS5_GPIO_MODE_MAU_SYS_PWR_REG,            { 0x1, 0x1, 0x0} },
+       { EXYNOS5_TOP_ASB_RESET_SYS_PWR_REG,            { 0x1, 0x1, 0x1} },
+       { EXYNOS5_TOP_ASB_ISOLATION_SYS_PWR_REG,        { 0x1, 0x0, 0x1} },
+       { EXYNOS5_GSCL_SYS_PWR_REG,                     { 0x7, 0x0, 0x0} },
+       { EXYNOS5_ISP_SYS_PWR_REG,                      { 0x7, 0x0, 0x0} },
+       { EXYNOS5_MFC_SYS_PWR_REG,                      { 0x7, 0x0, 0x0} },
+       { EXYNOS5_G3D_SYS_PWR_REG,                      { 0x7, 0x0, 0x0} },
+       { EXYNOS5_DISP1_SYS_PWR_REG,                    { 0x7, 0x0, 0x0} },
+       { EXYNOS5_MAU_SYS_PWR_REG,                      { 0x7, 0x7, 0x0} },
+       { EXYNOS5_CMU_CLKSTOP_GSCL_SYS_PWR_REG,         { 0x1, 0x0, 0x0} },
+       { EXYNOS5_CMU_CLKSTOP_ISP_SYS_PWR_REG,          { 0x1, 0x0, 0x0} },
+       { EXYNOS5_CMU_CLKSTOP_MFC_SYS_PWR_REG,          { 0x1, 0x0, 0x0} },
+       { EXYNOS5_CMU_CLKSTOP_G3D_SYS_PWR_REG,          { 0x1, 0x0, 0x0} },
+       { EXYNOS5_CMU_CLKSTOP_DISP1_SYS_PWR_REG,        { 0x1, 0x0, 0x0} },
+       { EXYNOS5_CMU_CLKSTOP_MAU_SYS_PWR_REG,          { 0x1, 0x1, 0x0} },
+       { EXYNOS5_CMU_SYSCLK_GSCL_SYS_PWR_REG,          { 0x1, 0x0, 0x0} },
+       { EXYNOS5_CMU_SYSCLK_ISP_SYS_PWR_REG,           { 0x1, 0x0, 0x0} },
+       { EXYNOS5_CMU_SYSCLK_MFC_SYS_PWR_REG,           { 0x1, 0x0, 0x0} },
+       { EXYNOS5_CMU_SYSCLK_G3D_SYS_PWR_REG,           { 0x1, 0x0, 0x0} },
+       { EXYNOS5_CMU_SYSCLK_DISP1_SYS_PWR_REG,         { 0x1, 0x0, 0x0} },
+       { EXYNOS5_CMU_SYSCLK_MAU_SYS_PWR_REG,           { 0x1, 0x1, 0x0} },
+       { EXYNOS5_CMU_RESET_GSCL_SYS_PWR_REG,           { 0x1, 0x0, 0x0} },
+       { EXYNOS5_CMU_RESET_ISP_SYS_PWR_REG,            { 0x1, 0x0, 0x0} },
+       { EXYNOS5_CMU_RESET_MFC_SYS_PWR_REG,            { 0x1, 0x0, 0x0} },
+       { EXYNOS5_CMU_RESET_G3D_SYS_PWR_REG,            { 0x1, 0x0, 0x0} },
+       { EXYNOS5_CMU_RESET_DISP1_SYS_PWR_REG,          { 0x1, 0x0, 0x0} },
+       { EXYNOS5_CMU_RESET_MAU_SYS_PWR_REG,            { 0x1, 0x1, 0x0} },
+       { PMU_TABLE_END,},
+};
+
+void __iomem *exynos5_list_both_cnt_feed[] = {
+       EXYNOS5_ARM_CORE0_OPTION,
+       EXYNOS5_ARM_CORE1_OPTION,
+       EXYNOS5_ARM_COMMON_OPTION,
+       EXYNOS5_GSCL_OPTION,
+       EXYNOS5_ISP_OPTION,
+       EXYNOS5_MFC_OPTION,
+       EXYNOS5_G3D_OPTION,
+       EXYNOS5_DISP1_OPTION,
+       EXYNOS5_MAU_OPTION,
+       EXYNOS5_TOP_PWR_OPTION,
+       EXYNOS5_TOP_PWR_SYSMEM_OPTION,
+};
+
+void __iomem *exynos5_list_diable_wfi_wfe[] = {
+       EXYNOS5_ARM_CORE1_OPTION,
+       EXYNOS5_FSYS_ARM_OPTION,
+       EXYNOS5_ISP_ARM_OPTION,
+};
+
+static void exynos5_init_pmu(void)
 {
        unsigned int i;
+       unsigned int tmp;
+
+       /*
+        * Enable both SC_FEEDBACK and SC_COUNTER
+        */
+       for (i = 0 ; i < ARRAY_SIZE(exynos5_list_both_cnt_feed) ; i++) {
+               tmp = __raw_readl(exynos5_list_both_cnt_feed[i]);
+               tmp |= (EXYNOS5_USE_SC_FEEDBACK |
+                       EXYNOS5_USE_SC_COUNTER);
+               __raw_writel(tmp, exynos5_list_both_cnt_feed[i]);
+       }
+
+       /*
+        * SKIP_DEACTIVATE_ACEACP_IN_PWDN_BITFIELD Enable
+        * MANUAL_L2RSTDISABLE_CONTROL_BITFIELD Enable
+        */
+       tmp = __raw_readl(EXYNOS5_ARM_COMMON_OPTION);
+       tmp |= (EXYNOS5_MANUAL_L2RSTDISABLE_CONTROL |
+               EXYNOS5_SKIP_DEACTIVATE_ACEACP_IN_PWDN);
+       __raw_writel(tmp, EXYNOS5_ARM_COMMON_OPTION);
+
+       /*
+        * Disable WFI/WFE on XXX_OPTION
+        */
+       for (i = 0 ; i < ARRAY_SIZE(exynos5_list_diable_wfi_wfe) ; i++) {
+               tmp = __raw_readl(exynos5_list_diable_wfi_wfe[i]);
+               tmp &= ~(EXYNOS5_OPTION_USE_STANDBYWFE |
+                        EXYNOS5_OPTION_USE_STANDBYWFI);
+               __raw_writel(tmp, exynos5_list_diable_wfi_wfe[i]);
+       }
+}
+
+void exynos_sys_powerdown_conf(enum sys_powerdown mode)
+{
+       unsigned int i;
+
+       if (soc_is_exynos5250())
+               exynos5_init_pmu();
 
-       for (i = 0; (exynos4_pmu_config[i].reg != PMU_TABLE_END) ; i++)
-               __raw_writel(exynos4_pmu_config[i].val[mode],
-                               exynos4_pmu_config[i].reg);
+       for (i = 0; (exynos_pmu_config[i].reg != PMU_TABLE_END) ; i++)
+               __raw_writel(exynos_pmu_config[i].val[mode],
+                               exynos_pmu_config[i].reg);
 
        if (soc_is_exynos4412()) {
                for (i = 0; exynos4412_pmu_config[i].reg != PMU_TABLE_END ; i++)
@@ -227,20 +388,23 @@ void exynos4_sys_powerdown_conf(enum sys_powerdown mode)
        }
 }
 
-static int __init exynos4_pmu_init(void)
+static int __init exynos_pmu_init(void)
 {
-       exynos4_pmu_config = exynos4210_pmu_config;
+       exynos_pmu_config = exynos4210_pmu_config;
 
        if (soc_is_exynos4210()) {
-               exynos4_pmu_config = exynos4210_pmu_config;
+               exynos_pmu_config = exynos4210_pmu_config;
                pr_info("EXYNOS4210 PMU Initialize\n");
        } else if (soc_is_exynos4212() || soc_is_exynos4412()) {
-               exynos4_pmu_config = exynos4x12_pmu_config;
+               exynos_pmu_config = exynos4x12_pmu_config;
                pr_info("EXYNOS4x12 PMU Initialize\n");
+       } else if (soc_is_exynos5250()) {
+               exynos_pmu_config = exynos5250_pmu_config;
+               pr_info("EXYNOS5250 PMU Initialize\n");
        } else {
-               pr_info("EXYNOS4: PMU not supported\n");
+               pr_info("EXYNOS: PMU not supported\n");
        }
 
        return 0;
 }
-arch_initcall(exynos4_pmu_init);
+arch_initcall(exynos_pmu_init);
index ebbd7fc90eb47488b320de65feee82ce578ff288..a9f80943d01fe8b468a0cba2a78cf8f05a51cac7 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/clockchips.h>
 #include <linux/io.h>
 #include <linux/export.h>
+#include <linux/gpio.h>
 
 #include <mach/udc.h>
 #include <mach/hardware.h>
@@ -107,7 +108,7 @@ static signed char irq2gpio[32] = {
         7,  8,  9, 10, 11, 12, -1, -1,
 };
 
-int gpio_to_irq(int gpio)
+static int ixp4xx_gpio_to_irq(struct gpio_chip *chip, unsigned gpio)
 {
        int irq;
 
@@ -117,7 +118,6 @@ int gpio_to_irq(int gpio)
        }
        return -EINVAL;
 }
-EXPORT_SYMBOL(gpio_to_irq);
 
 int irq_to_gpio(unsigned int irq)
 {
@@ -383,12 +383,56 @@ static struct platform_device *ixp46x_devices[] __initdata = {
 unsigned long ixp4xx_exp_bus_size;
 EXPORT_SYMBOL(ixp4xx_exp_bus_size);
 
+static int ixp4xx_gpio_direction_input(struct gpio_chip *chip, unsigned gpio)
+{
+       gpio_line_config(gpio, IXP4XX_GPIO_IN);
+
+       return 0;
+}
+
+static int ixp4xx_gpio_direction_output(struct gpio_chip *chip, unsigned gpio,
+                                       int level)
+{
+       gpio_line_set(gpio, level);
+       gpio_line_config(gpio, IXP4XX_GPIO_OUT);
+
+       return 0;
+}
+
+static int ixp4xx_gpio_get_value(struct gpio_chip *chip, unsigned gpio)
+{
+       int value;
+
+       gpio_line_get(gpio, &value);
+
+       return value;
+}
+
+static void ixp4xx_gpio_set_value(struct gpio_chip *chip, unsigned gpio,
+                                 int value)
+{
+       gpio_line_set(gpio, value);
+}
+
+static struct gpio_chip ixp4xx_gpio_chip = {
+       .label                  = "IXP4XX_GPIO_CHIP",
+       .direction_input        = ixp4xx_gpio_direction_input,
+       .direction_output       = ixp4xx_gpio_direction_output,
+       .get                    = ixp4xx_gpio_get_value,
+       .set                    = ixp4xx_gpio_set_value,
+       .to_irq                 = ixp4xx_gpio_to_irq,
+       .base                   = 0,
+       .ngpio                  = 16,
+};
+
 void __init ixp4xx_sys_init(void)
 {
        ixp4xx_exp_bus_size = SZ_16M;
 
        platform_add_devices(ixp4xx_devices, ARRAY_SIZE(ixp4xx_devices));
 
+       gpiochip_add(&ixp4xx_gpio_chip);
+
        if (cpu_is_ixp46x()) {
                int region;
 
index 83d6b4ed60bbd42f3e912d68611205d720ad98a1..ef37f2635b0e4a5812ab30ce065525e3853a44d8 100644 (file)
@@ -1,79 +1,2 @@
-/*
- * arch/arm/mach-ixp4xx/include/mach/gpio.h
- *
- * IXP4XX GPIO wrappers for arch-neutral GPIO calls
- *
- * Written by Milan Svoboda <msvoboda@ra.rockwell.com>
- * Based on PXA implementation by Philipp Zabel <philipp.zabel@gmail.com>
- *
- * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- */
-
-#ifndef __ASM_ARCH_IXP4XX_GPIO_H
-#define __ASM_ARCH_IXP4XX_GPIO_H
-
-#include <linux/kernel.h>
-#include <mach/hardware.h>
-
-#define __ARM_GPIOLIB_COMPLEX
-
-static inline int gpio_request(unsigned gpio, const char *label)
-{
-       return 0;
-}
-
-static inline void gpio_free(unsigned gpio)
-{
-       might_sleep();
-
-       return;
-}
-
-static inline int gpio_direction_input(unsigned gpio)
-{
-       gpio_line_config(gpio, IXP4XX_GPIO_IN);
-       return 0;
-}
-
-static inline int gpio_direction_output(unsigned gpio, int level)
-{
-       gpio_line_set(gpio, level);
-       gpio_line_config(gpio, IXP4XX_GPIO_OUT);
-       return 0;
-}
-
-static inline int gpio_get_value(unsigned gpio)
-{
-       int value;
-
-       gpio_line_get(gpio, &value);
-
-       return value;
-}
-
-static inline void gpio_set_value(unsigned gpio, int value)
-{
-       gpio_line_set(gpio, value);
-}
-
-#include <asm-generic/gpio.h>                  /* cansleep wrappers */
-
-extern int gpio_to_irq(int gpio);
-#define gpio_to_irq gpio_to_irq
-extern int irq_to_gpio(unsigned int irq);
-
-#endif
+/* empty */
 
index e53b2177319e701cdb44e8f479e2de62282e826d..b7a9f4d469e816bf6ca1f389f31d9280d06ecc46 100644 (file)
 #define IRQ_S32416_WDT         S3C2410_IRQSUB(27)
 #define IRQ_S32416_AC97                S3C2410_IRQSUB(28)
 
+/* second interrupt-register of s3c2416/s3c2450 */
+
+#define S3C2416_IRQ(x)         S3C2410_IRQ((x) + 54 + 29)
+#define IRQ_S3C2416_2D         S3C2416_IRQ(0)
+#define IRQ_S3C2416_IIC1       S3C2416_IRQ(1)
+#define IRQ_S3C2416_RESERVED2  S3C2416_IRQ(2)
+#define IRQ_S3C2416_RESERVED3  S3C2416_IRQ(3)
+#define IRQ_S3C2416_PCM0       S3C2416_IRQ(4)
+#define IRQ_S3C2416_PCM1       S3C2416_IRQ(5)
+#define IRQ_S3C2416_I2S0       S3C2416_IRQ(6)
+#define IRQ_S3C2416_I2S1       S3C2416_IRQ(7)
 
 /* extra irqs for s3c2440 */
 
 #define IRQ_S3C2443_WDT                S3C2410_IRQSUB(27)
 #define IRQ_S3C2443_AC97       S3C2410_IRQSUB(28)
 
-#if defined(CONFIG_CPU_S3C2443) || defined(CONFIG_CPU_S3C2416)
+#if defined(CONFIG_CPU_S3C2416)
+#define NR_IRQS (IRQ_S3C2416_I2S1 + 1)
+#elif defined(CONFIG_CPU_S3C2443)
 #define NR_IRQS (IRQ_S3C2443_AC97+1)
 #else
 #define NR_IRQS (IRQ_S3C2440_AC97+1)
index fd49f35e448ec7090098a4293493df7ee8a32f86..23ec97370f3272ea21ad6f745b859c5105ff04e2 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/ioport.h>
 #include <linux/device.h>
 #include <linux/io.h>
+#include <linux/syscore_ops.h>
 
 #include <mach/hardware.h>
 #include <asm/irq.h>
@@ -192,6 +193,43 @@ static struct irq_chip s3c2416_irq_uart3 = {
        .irq_ack        = s3c2416_irq_uart3_ack,
 };
 
+/* second interrupt register */
+
+static inline void s3c2416_irq_ack_second(struct irq_data *data)
+{
+       unsigned long bitval = 1UL << (data->irq - IRQ_S3C2416_2D);
+
+       __raw_writel(bitval, S3C2416_SRCPND2);
+       __raw_writel(bitval, S3C2416_INTPND2);
+}
+
+static void s3c2416_irq_mask_second(struct irq_data *data)
+{
+       unsigned long bitval = 1UL << (data->irq - IRQ_S3C2416_2D);
+       unsigned long mask;
+
+       mask = __raw_readl(S3C2416_INTMSK2);
+       mask |= bitval;
+       __raw_writel(mask, S3C2416_INTMSK2);
+}
+
+static void s3c2416_irq_unmask_second(struct irq_data *data)
+{
+       unsigned long bitval = 1UL << (data->irq - IRQ_S3C2416_2D);
+       unsigned long mask;
+
+       mask = __raw_readl(S3C2416_INTMSK2);
+       mask &= ~bitval;
+       __raw_writel(mask, S3C2416_INTMSK2);
+}
+
+struct irq_chip s3c2416_irq_second = {
+       .irq_ack        = s3c2416_irq_ack_second,
+       .irq_mask       = s3c2416_irq_mask_second,
+       .irq_unmask     = s3c2416_irq_unmask_second,
+};
+
+
 /* IRQ initialisation code */
 
 static int __init s3c2416_add_sub(unsigned int base,
@@ -213,6 +251,42 @@ static int __init s3c2416_add_sub(unsigned int base,
        return 0;
 }
 
+static void __init s3c2416_irq_add_second(void)
+{
+       unsigned long pend;
+       unsigned long last;
+       int irqno;
+       int i;
+
+       /* first, clear all interrupts pending... */
+       last = 0;
+       for (i = 0; i < 4; i++) {
+               pend = __raw_readl(S3C2416_INTPND2);
+
+               if (pend == 0 || pend == last)
+                       break;
+
+               __raw_writel(pend, S3C2416_SRCPND2);
+               __raw_writel(pend, S3C2416_INTPND2);
+               printk(KERN_INFO "irq: clearing pending status %08x\n",
+                      (int)pend);
+               last = pend;
+       }
+
+       for (irqno = IRQ_S3C2416_2D; irqno <= IRQ_S3C2416_I2S1; irqno++) {
+               switch (irqno) {
+               case IRQ_S3C2416_RESERVED2:
+               case IRQ_S3C2416_RESERVED3:
+                       /* no IRQ here */
+                       break;
+               default:
+                       irq_set_chip_and_handler(irqno, &s3c2416_irq_second,
+                                                handle_edge_irq);
+                       set_irq_flags(irqno, IRQF_VALID);
+               }
+       }
+}
+
 static int __init s3c2416_irq_add(struct device *dev,
                                  struct subsys_interface *sif)
 {
@@ -232,6 +306,8 @@ static int __init s3c2416_irq_add(struct device *dev,
                        &s3c2416_irq_wdtac97,
                        IRQ_S3C2443_WDT, IRQ_S3C2443_AC97);
 
+       s3c2416_irq_add_second();
+
        return 0;
 }
 
@@ -248,3 +324,25 @@ static int __init s3c2416_irq_init(void)
 
 arch_initcall(s3c2416_irq_init);
 
+#ifdef CONFIG_PM
+static struct sleep_save irq_save[] = {
+       SAVE_ITEM(S3C2416_INTMSK2),
+};
+
+int s3c2416_irq_suspend(void)
+{
+       s3c_pm_do_save(irq_save, ARRAY_SIZE(irq_save));
+
+       return 0;
+}
+
+void s3c2416_irq_resume(void)
+{
+       s3c_pm_do_restore(irq_save, ARRAY_SIZE(irq_save));
+}
+
+struct syscore_ops s3c2416_irq_syscore_ops = {
+       .suspend        = s3c2416_irq_suspend,
+       .resume         = s3c2416_irq_resume,
+};
+#endif
index 7743fade50dfa59e1c3d669d5aa55478adb993e1..ed5a95ece9eb2d365b7152cdb4bd5cfe508e672b 100644 (file)
@@ -106,6 +106,7 @@ int __init s3c2416_init(void)
        register_syscore_ops(&s3c2416_pm_syscore_ops);
 #endif
        register_syscore_ops(&s3c24xx_irq_syscore_ops);
+       register_syscore_ops(&s3c2416_irq_syscore_ops);
 
        return device_register(&s3c2416_dev);
 }
index 179460f38db7587c94321ca2c401173cf174da77..acb197ccf3f7e7635ccbba7a3426303701e0fbf8 100644 (file)
@@ -27,12 +27,7 @@ static int s3c64xx_enter_idle(struct cpuidle_device *dev,
                              struct cpuidle_driver *drv,
                              int index)
 {
-       struct timeval before, after;
        unsigned long tmp;
-       int idle_time;
-
-       local_irq_disable();
-       do_gettimeofday(&before);
 
        /* Setup PWRCFG to enter idle mode */
        tmp = __raw_readl(S3C64XX_PWR_CFG);
@@ -42,42 +37,32 @@ static int s3c64xx_enter_idle(struct cpuidle_device *dev,
 
        cpu_do_idle();
 
-       do_gettimeofday(&after);
-       local_irq_enable();
-       idle_time = (after.tv_sec - before.tv_sec) * USEC_PER_SEC +
-                   (after.tv_usec - before.tv_usec);
-
-       dev->last_residency = idle_time;
        return index;
 }
 
-static struct cpuidle_state s3c64xx_cpuidle_set[] = {
-       [0] = {
-               .enter                  = s3c64xx_enter_idle,
-               .exit_latency           = 1,
-               .target_residency       = 1,
-               .flags                  = CPUIDLE_FLAG_TIME_VALID,
-               .name                   = "IDLE",
-               .desc                   = "System active, ARM gated",
-       },
-};
+static DEFINE_PER_CPU(struct cpuidle_device, s3c64xx_cpuidle_device);
 
 static struct cpuidle_driver s3c64xx_cpuidle_driver = {
-       .name           = "s3c64xx_cpuidle",
-       .owner          = THIS_MODULE,
-       .state_count    = ARRAY_SIZE(s3c64xx_cpuidle_set),
-};
-
-static struct cpuidle_device s3c64xx_cpuidle_device = {
-       .state_count    = ARRAY_SIZE(s3c64xx_cpuidle_set),
+       .name   = "s3c64xx_cpuidle",
+       .owner  = THIS_MODULE,
+       .en_core_tk_irqen = 1,
+       .states = {
+               {
+                       .enter            = s3c64xx_enter_idle,
+                       .exit_latency     = 1,
+                       .target_residency = 1,
+                       .flags            = CPUIDLE_FLAG_TIME_VALID,
+                       .name             = "IDLE",
+                       .desc             = "System active, ARM gated",
+               },
+       },
+       .state_count = 1,
 };
 
 static int __init s3c64xx_init_cpuidle(void)
 {
        int ret;
 
-       memcpy(s3c64xx_cpuidle_driver.states, s3c64xx_cpuidle_set,
-              sizeof(s3c64xx_cpuidle_set));
        cpuidle_register_driver(&s3c64xx_cpuidle_driver);
 
        ret = cpuidle_register_device(&s3c64xx_cpuidle_device);
index 0ace108c3e3d710264f4c88dfa5676702723fbfc..7a27f5603c7405fca49409c33ee9af79413de701 100644 (file)
@@ -182,6 +182,11 @@ static const struct i2c_board_info wm1277_devs[] = {
        },
 };
 
+static const struct i2c_board_info wm6230_i2c_devs[] = {
+       { I2C_BOARD_INFO("wm9081", 0x6c),
+         .platform_data = &wm9081_pdata, },
+};
+
 static __devinitdata const struct {
        u8 id;
        const char *name;
@@ -195,7 +200,9 @@ static __devinitdata const struct {
        { .id = 0x03, .name = "1252-EV1 Glenlivet" },
        { .id = 0x11, .name = "6249-EV2 Glenfarclas", },
        { .id = 0x14, .name = "6271-EV1 Lochnagar" },
-       { .id = 0x15, .name = "XXXX-EV1 Bells" },
+       { .id = 0x15, .name = "6320-EV1 Bells",
+         .i2c_devs = wm6230_i2c_devs,
+         .num_i2c_devs = ARRAY_SIZE(wm6230_i2c_devs) },
        { .id = 0x21, .name = "1275-EV1 Mortlach" },
        { .id = 0x25, .name = "1274-EV1 Glencadam" },
        { .id = 0x31, .name = "1253-EV1 Tomatin",
index eda5e027b109aa6a9af206c782cbfba6cd58bb9f..6b20a71d7dbfd81b9db2c73966e747d30079b7a1 100644 (file)
@@ -671,6 +671,7 @@ static struct i2c_board_info i2c_devs1[] __initdata = {
          .irq = S3C_EINT(0),
          .platform_data = &glenfarclas_pmic_pdata },
 
+       { I2C_BOARD_INFO("wlf-gf-module", 0x22) },
        { I2C_BOARD_INFO("wlf-gf-module", 0x24) },
        { I2C_BOARD_INFO("wlf-gf-module", 0x25) },
        { I2C_BOARD_INFO("wlf-gf-module", 0x26) },
index 6c58f01b358a1064db6a869ee2cbac52b4b8ba0a..266db873a4e4c6c328a19b038eea18d29cab8997 100644 (file)
@@ -89,6 +89,7 @@ void neponset_ncr_frob(unsigned int mask, unsigned int val)
                WARN(1, "nep_base unset\n");
        }
 }
+EXPORT_SYMBOL(neponset_ncr_frob);
 
 static void neponset_set_mctrl(struct uart_port *port, u_int mctrl)
 {
index b23a643f03f4ce62eaddfd9b29b7722c06c5afd8..fba8adea421e399272336b9ff8d7b6bfb8b645e9 100644 (file)
@@ -206,7 +206,7 @@ static struct resource ab8500_resources[] = {
 };
 
 struct platform_device ab8500_device = {
-       .name = "ab8500-i2c",
+       .name = "ab8500-core",
        .id = 0,
        .dev = {
                .platform_data = &ab8500_platdata,
index 04dd092211b893271fc65df344ef42adb1e3f754..fde26adaef32d964a539a12a97f4596af27b1fe1 100644 (file)
@@ -14,7 +14,6 @@
 #include <linux/ata_platform.h>
 #include <linux/smsc911x.h>
 #include <linux/spinlock.h>
-#include <linux/device.h>
 #include <linux/usb/isp1760.h>
 #include <linux/clkdev.h>
 #include <linux/mtd/physmap.h>
@@ -31,7 +30,6 @@
 #include <asm/hardware/gic.h>
 #include <asm/hardware/timer-sp.h>
 #include <asm/hardware/sp810.h>
-#include <asm/hardware/gic.h>
 
 #include <mach/ct-ca9x4.h>
 #include <mach/motherboard.h>
index de2b5bdc5ebd860a3e65020c6c63dfc75fb97dbb..7178e338e25ed8e7981e9b134dd7508aa8f4f128 100644 (file)
@@ -24,6 +24,9 @@ extern void s3c2416_init_clocks(int xtal);
 extern  int s3c2416_baseclk_add(void);
 
 extern void s3c2416_restart(char mode, const char *cmd);
+
+extern struct syscore_ops s3c2416_irq_syscore_ops;
+
 #else
 #define s3c2416_init_clocks NULL
 #define s3c2416_init_uarts NULL
index 74667bfc88cc7676e4c43332b40705908c747021..9ba9e749b3f34d7c2760d1d9784ff9ede9528ef3 100644 (file)
@@ -17,9 +17,6 @@
 typedef unsigned short  __kernel_mode_t;
 #define __kernel_mode_t __kernel_mode_t
 
-typedef unsigned short  __kernel_nlink_t;
-#define __kernel_nlink_t __kernel_nlink_t
-
 typedef unsigned short  __kernel_ipc_pid_t;
 #define __kernel_ipc_pid_t __kernel_ipc_pid_t
 
index 169268c40ae2552df9430128e4ae6e6964249989..df28841813139f658a3511daaacb5aa14012dd80 100644 (file)
@@ -281,7 +281,7 @@ syscall_exit_work:
        ld.w    r1, r0[TI_flags]
        rjmp    1b
 
-2:     mov     r2, _TIF_SIGPENDING | _TIF_RESTORE_SIGMASK | _TIF_NOTIFY_RESUME
+2:     mov     r2, _TIF_SIGPENDING | _TIF_NOTIFY_RESUME
        tst     r1, r2
        breq    3f
        unmask_interrupts
@@ -587,7 +587,7 @@ fault_exit_work:
        ld.w    r1, r0[TI_flags]
        rjmp    fault_exit_work
 
-1:     mov     r2, _TIF_SIGPENDING | _TIF_RESTORE_SIGMASK
+1:     mov     r2, _TIF_SIGPENDING | _TIF_NOTIFY_RESUME
        tst     r1, r2
        breq    2f
        unmask_interrupts
index ae386c304beefe153fb162e2b6909edc84dd5ef4..c140f9b41dce48fed05240ff531dc22e42f29095 100644 (file)
@@ -22,8 +22,6 @@
 #include <asm/ucontext.h>
 #include <asm/syscalls.h>
 
-#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
-
 asmlinkage int sys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss,
                               struct pt_regs *regs)
 {
@@ -89,7 +87,6 @@ asmlinkage int sys_rt_sigreturn(struct pt_regs *regs)
        if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
                goto badframe;
 
-       sigdelsetmask(&set, ~_BLOCKABLE);
        set_current_blocked(&set);
 
        if (restore_sigcontext(regs, &frame->uc.uc_mcontext))
@@ -224,30 +221,27 @@ static inline void setup_syscall_restart(struct pt_regs *regs)
 
 static inline void
 handle_signal(unsigned long sig, struct k_sigaction *ka, siginfo_t *info,
-             sigset_t *oldset, struct pt_regs *regs, int syscall)
+             struct pt_regs *regs, int syscall)
 {
        int ret;
 
        /*
         * Set up the stack frame
         */
-       ret = setup_rt_frame(sig, ka, info, oldset, regs);
+       ret = setup_rt_frame(sig, ka, info, sigmask_to_save(), regs);
 
        /*
         * Check that the resulting registers are sane
         */
        ret |= !valid_user_regs(regs);
 
-       if (ret != 0) {
-               force_sigsegv(sig, current);
-               return;
-       }
-
        /*
         * Block the signal if we were successful.
         */
-       block_sigmask(ka, sig);
-       clear_thread_flag(TIF_RESTORE_SIGMASK);
+       if (ret != 0)
+               force_sigsegv(sig, current);
+       else
+               signal_delivered(sig, info, ka, regs, 0);
 }
 
 /*
@@ -255,7 +249,7 @@ handle_signal(unsigned long sig, struct k_sigaction *ka, siginfo_t *info,
  * doesn't want to handle. Thus you cannot kill init even with a
  * SIGKILL even by mistake.
  */
-int do_signal(struct pt_regs *regs, sigset_t *oldset, int syscall)
+static void do_signal(struct pt_regs *regs, int syscall)
 {
        siginfo_t info;
        int signr;
@@ -267,12 +261,7 @@ int do_signal(struct pt_regs *regs, sigset_t *oldset, int syscall)
         * without doing anything if so.
         */
        if (!user_mode(regs))
-               return 0;
-
-       if (test_thread_flag(TIF_RESTORE_SIGMASK))
-               oldset = &current->saved_sigmask;
-       else if (!oldset)
-               oldset = &current->blocked;
+               return;
 
        signr = get_signal_to_deliver(&info, &ka, regs, NULL);
        if (syscall) {
@@ -297,15 +286,11 @@ int do_signal(struct pt_regs *regs, sigset_t *oldset, int syscall)
 
        if (signr == 0) {
                /* No signal to deliver -- put the saved sigmask back */
-               if (test_thread_flag(TIF_RESTORE_SIGMASK)) {
-                       clear_thread_flag(TIF_RESTORE_SIGMASK);
-                       sigprocmask(SIG_SETMASK, &current->saved_sigmask, NULL);
-               }
-               return 0;
+               restore_saved_sigmask();
+               return;
        }
 
-       handle_signal(signr, &ka, &info, oldset, regs, syscall);
-       return 1;
+       handle_signal(signr, &ka, &info, regs, syscall);
 }
 
 asmlinkage void do_notify_resume(struct pt_regs *regs, struct thread_info *ti)
@@ -315,13 +300,11 @@ asmlinkage void do_notify_resume(struct pt_regs *regs, struct thread_info *ti)
        if ((sysreg_read(SR) & MODE_MASK) == MODE_SUPERVISOR)
                syscall = 1;
 
-       if (ti->flags & (_TIF_SIGPENDING | _TIF_RESTORE_SIGMASK))
-               do_signal(regs, &current->blocked, syscall);
+       if (ti->flags & _TIF_SIGPENDING))
+               do_signal(regs, syscall);
 
        if (ti->flags & _TIF_NOTIFY_RESUME) {
                clear_thread_flag(TIF_NOTIFY_RESUME);
                tracehook_notify_resume(regs);
-               if (current->replacement_session_keyring)
-                       key_replace_session_keyring();
        }
 }
index 41bc1875c4d7fd367bbbea5432b9332a2821557f..1bd3436db6a7b7d080bdf4bcb1a09db621fde1b0 100644 (file)
@@ -10,9 +10,6 @@
 typedef unsigned short __kernel_mode_t;
 #define __kernel_mode_t __kernel_mode_t
 
-typedef unsigned short __kernel_nlink_t;
-#define __kernel_nlink_t __kernel_nlink_t
-
 typedef unsigned int __kernel_ipc_pid_t;
 #define __kernel_ipc_pid_t __kernel_ipc_pid_t
 
index 02560fd8a12182f019802e2f7b4c392110c2eb66..53ad10005ae37db4fbf6c3d6cb21eee6485b8db6 100644 (file)
@@ -100,7 +100,6 @@ static inline struct thread_info *current_thread_info(void)
                                           TIF_NEED_RESCHED */
 #define TIF_MEMDIE             4       /* is terminating due to OOM killer */
 #define TIF_RESTORE_SIGMASK    5       /* restore signal mask in do_signal() */
-#define TIF_FREEZE             6       /* is freezing for suspend */
 #define TIF_IRQ_SYNC           7       /* sync pipeline stage */
 #define TIF_NOTIFY_RESUME      8       /* callback before returning to user */
 #define TIF_SINGLESTEP         9
@@ -111,7 +110,6 @@ static inline struct thread_info *current_thread_info(void)
 #define _TIF_NEED_RESCHED      (1<<TIF_NEED_RESCHED)
 #define _TIF_POLLING_NRFLAG    (1<<TIF_POLLING_NRFLAG)
 #define _TIF_RESTORE_SIGMASK   (1<<TIF_RESTORE_SIGMASK)
-#define _TIF_FREEZE            (1<<TIF_FREEZE)
 #define _TIF_IRQ_SYNC          (1<<TIF_IRQ_SYNC)
 #define _TIF_NOTIFY_RESUME     (1<<TIF_NOTIFY_RESUME)
 #define _TIF_SINGLESTEP                (1<<TIF_SINGLESTEP)
index e5bbc1a5edc242dae7221168f771bbad704872d3..6682b73a8523f789bf2e3fa722f068f1ef4ebf79 100644 (file)
@@ -19,8 +19,6 @@
 #include <asm/fixed_code.h>
 #include <asm/syscall.h>
 
-#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
-
 /* Location of the trace bit in SYSCFG. */
 #define TRACE_BITS 0x0001
 
@@ -98,7 +96,6 @@ asmlinkage int do_rt_sigreturn(unsigned long __unused)
        if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
                goto badframe;
 
-       sigdelsetmask(&set, ~_BLOCKABLE);
        set_current_blocked(&set);
 
        if (rt_restore_sigcontext(regs, &frame->uc.uc_mcontext, &r0))
@@ -190,17 +187,22 @@ setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t * info,
        err |= copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
 
        if (err)
-               goto give_sigsegv;
+               return -EFAULT;
 
        /* Set up registers for signal handler */
-       wrusp((unsigned long)frame);
        if (current->personality & FDPIC_FUNCPTRS) {
                struct fdpic_func_descriptor __user *funcptr =
                        (struct fdpic_func_descriptor *) ka->sa.sa_handler;
-               __get_user(regs->pc, &funcptr->text);
-               __get_user(regs->p3, &funcptr->GOT);
+               u32 pc, p3;
+               err |= __get_user(pc, &funcptr->text);
+               err |= __get_user(p3, &funcptr->GOT);
+               if (err)
+                       return -EFAULT;
+               regs->pc = pc;
+               regs->p3 = p3;
        } else
                regs->pc = (unsigned long)ka->sa.sa_handler;
+       wrusp((unsigned long)frame);
        regs->rets = SIGRETURN_STUB;
 
        regs->r0 = frame->sig;
@@ -208,10 +210,6 @@ setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t * info,
        regs->r2 = (unsigned long)(&frame->uc);
 
        return 0;
-
- give_sigsegv:
-       force_sigsegv(sig, current);
-       return -EFAULT;
 }
 
 static inline void
@@ -247,24 +245,21 @@ handle_restart(struct pt_regs *regs, struct k_sigaction *ka, int has_handler)
 /*
  * OK, we're invoking a handler
  */
-static int
+static void
 handle_signal(int sig, siginfo_t *info, struct k_sigaction *ka,
-             sigset_t *oldset, struct pt_regs *regs)
+             struct pt_regs *regs)
 {
-       int ret;
-
        /* are we from a system call? to see pt_regs->orig_p0 */
        if (regs->orig_p0 >= 0)
                /* If so, check system call restarting.. */
                handle_restart(regs, ka, 1);
 
        /* set up the stack frame */
-       ret = setup_rt_frame(sig, ka, info, oldset, regs);
-
-       if (ret == 0)
-               block_sigmask(ka, sig);
-
-       return ret;
+       if (setup_rt_frame(sig, ka, info, sigmask_to_save(), regs) < 0)
+               force_sigsegv(sig, current);
+       else 
+               signal_delivered(sig, info, ka, regs,
+                               test_thread_flag(TIF_SINGLESTEP));
 }
 
 /*
@@ -281,37 +276,16 @@ asmlinkage void do_signal(struct pt_regs *regs)
        siginfo_t info;
        int signr;
        struct k_sigaction ka;
-       sigset_t *oldset;
 
        current->thread.esp0 = (unsigned long)regs;
 
-       if (try_to_freeze())
-               goto no_signal;
-
-       if (test_thread_flag(TIF_RESTORE_SIGMASK))
-               oldset = &current->saved_sigmask;
-       else
-               oldset = &current->blocked;
-
        signr = get_signal_to_deliver(&info, &ka, regs, NULL);
        if (signr > 0) {
                /* Whee!  Actually deliver the signal.  */
-               if (handle_signal(signr, &info, &ka, oldset, regs) == 0) {
-                       /* a signal was successfully delivered; the saved
-                        * sigmask will have been stored in the signal frame,
-                        * and will be restored by sigreturn, so we can simply
-                        * clear the TIF_RESTORE_SIGMASK flag */
-                       if (test_thread_flag(TIF_RESTORE_SIGMASK))
-                               clear_thread_flag(TIF_RESTORE_SIGMASK);
-
-                       tracehook_signal_handler(signr, &info, &ka, regs,
-                               test_thread_flag(TIF_SINGLESTEP));
-               }
-
+               handle_signal(signr, &info, &ka, regs);
                return;
        }
 
- no_signal:
        /* Did we come from a system call? */
        if (regs->orig_p0 >= 0)
                /* Restart the system call - no handlers present */
@@ -319,10 +293,7 @@ asmlinkage void do_signal(struct pt_regs *regs)
 
        /* if there's no signal to deliver, we just put the saved sigmask
         * back */
-       if (test_thread_flag(TIF_RESTORE_SIGMASK)) {
-               clear_thread_flag(TIF_RESTORE_SIGMASK);
-               sigprocmask(SIG_SETMASK, &current->saved_sigmask, NULL);
-       }
+       restore_saved_sigmask();
 }
 
 /*
@@ -330,14 +301,12 @@ asmlinkage void do_signal(struct pt_regs *regs)
  */
 asmlinkage void do_notify_resume(struct pt_regs *regs)
 {
-       if (test_thread_flag(TIF_SIGPENDING) || test_thread_flag(TIF_RESTORE_SIGMASK))
+       if (test_thread_flag(TIF_SIGPENDING))
                do_signal(regs);
 
        if (test_thread_flag(TIF_NOTIFY_RESUME)) {
                clear_thread_flag(TIF_NOTIFY_RESUME);
                tracehook_notify_resume(regs);
-               if (current->replacement_session_keyring)
-                       key_replace_session_keyring();
        }
 }
 
index 44bbf2f564cb7953d076a63b3940b400a2b330d8..f7f7a18abca915773ad291bd74a8ef192ffde333 100644 (file)
@@ -10,6 +10,8 @@
 #include <linux/hardirq.h>
 #include <linux/thread_info.h>
 #include <linux/mm.h>
+#include <linux/oom.h>
+#include <linux/sched.h>
 #include <linux/uaccess.h>
 #include <linux/module.h>
 #include <linux/kallsyms.h>
@@ -27,8 +29,7 @@ void decode_address(char *buf, unsigned long address)
 {
        struct task_struct *p;
        struct mm_struct *mm;
-       unsigned long flags, offset;
-       unsigned char in_atomic = (bfin_read_IPEND() & 0x10) || in_atomic();
+       unsigned long offset;
        struct rb_node *n;
 
 #ifdef CONFIG_KALLSYMS
@@ -112,17 +113,17 @@ void decode_address(char *buf, unsigned long address)
         * mappings of all our processes and see if we can't be a whee
         * bit more specific
         */
-       write_lock_irqsave(&tasklist_lock, flags);
+       read_lock(&tasklist_lock);
        for_each_process(p) {
-               mm = (in_atomic ? p->mm : get_task_mm(p));
-               if (!mm)
-                       continue;
+               struct task_struct *t;
 
-               if (!down_read_trylock(&mm->mmap_sem)) {
-                       if (!in_atomic)
-                               mmput(mm);
+               t = find_lock_task_mm(p);
+               if (!t)
                        continue;
-               }
+
+               mm = t->mm;
+               if (!down_read_trylock(&mm->mmap_sem))
+                       goto __continue;
 
                for (n = rb_first(&mm->mm_rb); n; n = rb_next(n)) {
                        struct vm_area_struct *vma;
@@ -131,7 +132,7 @@ void decode_address(char *buf, unsigned long address)
 
                        if (address >= vma->vm_start && address < vma->vm_end) {
                                char _tmpbuf[256];
-                               char *name = p->comm;
+                               char *name = t->comm;
                                struct file *file = vma->vm_file;
 
                                if (file) {
@@ -164,8 +165,7 @@ void decode_address(char *buf, unsigned long address)
                                                name, vma->vm_start, vma->vm_end);
 
                                up_read(&mm->mmap_sem);
-                               if (!in_atomic)
-                                       mmput(mm);
+                               task_unlock(t);
 
                                if (buf[0] == '\0')
                                        sprintf(buf, "[ %s ] dynamic memory", name);
@@ -175,8 +175,8 @@ void decode_address(char *buf, unsigned long address)
                }
 
                up_read(&mm->mmap_sem);
-               if (!in_atomic)
-                       mmput(mm);
+__continue:
+               task_unlock(t);
        }
 
        /*
@@ -186,7 +186,7 @@ void decode_address(char *buf, unsigned long address)
        sprintf(buf, "/* kernel dynamic memory */");
 
 done:
-       write_unlock_irqrestore(&tasklist_lock, flags);
+       read_unlock(&tasklist_lock);
 }
 
 #define EXPAND_LEN ((1 << CONFIG_DEBUG_BFIN_HWTRACE_EXPAND_LEN) * 256 - 1)
index 80aa2535e2c9f2be80c6f78d6a49c832f7e8be3d..04c2fbe41a7ff3e7193532b1e809b8dbcb7474ee 100644 (file)
@@ -711,8 +711,6 @@ ENTRY(_system_call)
        jump .Lresume_userspace_1;
 
 .Lsyscall_sigpending:
-       cc = BITTST(r7, TIF_RESTORE_SIGMASK);
-       if cc jump .Lsyscall_do_signals;
        cc = BITTST(r7, TIF_SIGPENDING);
        if cc jump .Lsyscall_do_signals;
        cc = BITTST(r7, TIF_NOTIFY_RESUME);
index cf37478c1169c59411bc6926142c020a6348ec26..3d8f3c22a94fa0adf7e66fe2a793a65326759990 100644 (file)
@@ -20,8 +20,6 @@
 #include <asm/cacheflush.h>
 
 
-#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
-
 /*
  * Do a signal return, undo the signal stack.
  */
@@ -87,7 +85,6 @@ asmlinkage int do_rt_sigreturn(struct pt_regs *regs)
        if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
                goto badframe;
 
-       sigdelsetmask(&set, ~_BLOCKABLE);
        set_current_blocked(&set);
 
        if (restore_sigcontext(regs, &frame->uc.uc_mcontext))
@@ -248,10 +245,9 @@ do_restart:
 /*
  * handle the actual delivery of a signal to userspace
  */
-static int handle_signal(int sig,
+static void handle_signal(int sig,
                         siginfo_t *info, struct k_sigaction *ka,
-                        sigset_t *oldset, struct pt_regs *regs,
-                        int syscall)
+                        struct pt_regs *regs, int syscall)
 {
        int ret;
 
@@ -278,11 +274,9 @@ static int handle_signal(int sig,
        }
 
        /* Set up the stack frame */
-       ret = setup_rt_frame(sig, ka, info, oldset, regs);
-       if (ret == 0)
-               block_sigmask(ka, sig);
-
-       return ret;
+       if (setup_rt_frame(sig, ka, info, sigmask_to_save(), regs) < 0)
+               return;
+       signal_delivered(sig, info, ka, regs, 0);
 }
 
 /*
@@ -292,7 +286,6 @@ static void do_signal(struct pt_regs *regs, int syscall)
 {
        struct k_sigaction ka;
        siginfo_t info;
-       sigset_t *oldset;
        int signr;
 
        /* we want the common case to go fast, which is why we may in certain
@@ -300,25 +293,9 @@ static void do_signal(struct pt_regs *regs, int syscall)
        if (!user_mode(regs))
                return;
 
-       if (test_thread_flag(TIF_RESTORE_SIGMASK))
-               oldset = &current->saved_sigmask;
-       else
-               oldset = &current->blocked;
-
        signr = get_signal_to_deliver(&info, &ka, regs, NULL);
        if (signr > 0) {
-               if (handle_signal(signr, &info, &ka, oldset,
-                                 regs, syscall) == 0) {
-                       /* a signal was successfully delivered; the saved
-                        * sigmask will have been stored in the signal frame,
-                        * and will be restored by sigreturn, so we can simply
-                        * clear the TIF_RESTORE_SIGMASK flag */
-                       if (test_thread_flag(TIF_RESTORE_SIGMASK))
-                               clear_thread_flag(TIF_RESTORE_SIGMASK);
-
-                       tracehook_signal_handler(signr, &info, &ka, regs, 0);
-               }
-
+               handle_signal(signr, &info, &ka, regs, syscall);
                return;
        }
 
@@ -343,10 +320,7 @@ static void do_signal(struct pt_regs *regs, int syscall)
 
        /* if there's no signal to deliver, we just put the saved sigmask
         * back */
-       if (test_thread_flag(TIF_RESTORE_SIGMASK)) {
-               clear_thread_flag(TIF_RESTORE_SIGMASK);
-               sigprocmask(SIG_SETMASK, &current->saved_sigmask, NULL);
-       }
+       restore_saved_sigmask();
 }
 
 /*
@@ -357,14 +331,11 @@ asmlinkage void do_notify_resume(struct pt_regs *regs, u32 thread_info_flags,
                                 int syscall)
 {
        /* deal with pending signal delivery */
-       if (thread_info_flags & ((1 << TIF_SIGPENDING) |
-                                (1 << TIF_RESTORE_SIGMASK)))
+       if (thread_info_flags & (1 << TIF_SIGPENDING))
                do_signal(regs, syscall);
 
        if (thread_info_flags & (1 << TIF_NOTIFY_RESUME)) {
                clear_thread_flag(TIF_NOTIFY_RESUME);
                tracehook_notify_resume(regs);
-               if (current->replacement_session_keyring)
-                       key_replace_session_keyring();
        }
 }
index 22d34d64cc81d014ef39e793f14dc1b28d105655..bb344650a14f255517487a3a06342992801e8237 100644 (file)
@@ -40,6 +40,7 @@ config CRIS
        bool
        default y
        select HAVE_IDE
+       select GENERIC_ATOMIC64
        select HAVE_GENERIC_HARDIRQS
        select GENERIC_IRQ_SHOW
        select GENERIC_IOMAP
index e16f8f297f61a0376f8b6b4497675a742ee9bf87..0bb477c13a4e08b6bd09ceee6e22e9a11ac2a6b0 100644 (file)
@@ -31,8 +31,6 @@
 
 #define DEBUG_SIG 0
 
-#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
-
 /* a syscall in Linux/CRIS is a break 13 instruction which is 2 bytes */
 /* manipulate regs so that upon return, it will be re-executed */
 
@@ -176,7 +174,6 @@ asmlinkage int sys_sigreturn(long r10, long r11, long r12, long r13, long mof,
                                    sizeof(frame->extramask))))
                goto badframe;
 
-       sigdelsetmask(&set, ~_BLOCKABLE);
        set_current_blocked(&set);
 
        if (restore_sigcontext(regs, &frame->sc))
@@ -212,7 +209,6 @@ asmlinkage int sys_rt_sigreturn(long r10, long r11, long r12, long r13,
        if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
                goto badframe;
 
-       sigdelsetmask(&set, ~_BLOCKABLE);
        set_current_blocked(&set);
 
        if (restore_sigcontext(regs, &frame->uc.uc_mcontext))
@@ -415,10 +411,11 @@ give_sigsegv:
  * OK, we're invoking a handler
  */
 
-static inline int handle_signal(int canrestart, unsigned long sig,
+static inline void handle_signal(int canrestart, unsigned long sig,
        siginfo_t *info, struct k_sigaction *ka,
-       sigset_t *oldset, struct pt_regs *regs)
+       struct pt_regs *regs)
 {
+       sigset_t *oldset = sigmask_to_save();
        int ret;
 
        /* Are we from a system call? */
@@ -456,9 +453,7 @@ static inline int handle_signal(int canrestart, unsigned long sig,
                ret = setup_frame(sig, ka, oldset, regs);
 
        if (ret == 0)
-               block_sigmask(ka, sig);
-
-       return ret;
+               signal_delivered(sig, info, ka, regs, 0);
 }
 
 /*
@@ -478,7 +473,6 @@ void do_signal(int canrestart, struct pt_regs *regs)
        siginfo_t info;
        int signr;
         struct k_sigaction ka;
-       sigset_t *oldset;
 
        /*
         * We want the common case to go fast, which
@@ -489,23 +483,10 @@ void do_signal(int canrestart, struct pt_regs *regs)
        if (!user_mode(regs))
                return;
 
-       if (test_thread_flag(TIF_RESTORE_SIGMASK))
-               oldset = &current->saved_sigmask;
-       else
-               oldset = &current->blocked;
-
        signr = get_signal_to_deliver(&info, &ka, regs, NULL);
        if (signr > 0) {
                /* Whee!  Actually deliver the signal.  */
-               if (handle_signal(canrestart, signr, &info, &ka,
-                               oldset, regs)) {
-                       /* a signal was successfully delivered; the saved
-                        * sigmask will have been stored in the signal frame,
-                        * and will be restored by sigreturn, so we can simply
-                        * clear the TIF_RESTORE_SIGMASK flag */
-                       if (test_thread_flag(TIF_RESTORE_SIGMASK))
-                               clear_thread_flag(TIF_RESTORE_SIGMASK);
-               }
+               handle_signal(canrestart, signr, &info, &ka, regs);
                return;
        }
 
@@ -525,8 +506,5 @@ void do_signal(int canrestart, struct pt_regs *regs)
 
        /* if there's no signal to deliver, we just put the saved sigmask
         * back */
-       if (test_thread_flag(TIF_RESTORE_SIGMASK)) {
-               clear_thread_flag(TIF_RESTORE_SIGMASK);
-               sigprocmask(SIG_SETMASK, &current->saved_sigmask, NULL);
-       }
+       restore_saved_sigmask();
 }
index b338d8fc0c1241c12aa66495e474d16cd5d49f4a..b60d1b65a4267ef5cbdb14758058b5c9ca6707a0 100644 (file)
@@ -24,9 +24,6 @@
 
 extern unsigned long cris_signal_return_page;
 
-/* Flag to check if a signal is blockable. */
-#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
-
 /*
  * A syscall in CRIS is really a "break 13" instruction, which is 2
  * bytes. The registers is manipulated so upon return the instruction
@@ -167,7 +164,6 @@ sys_sigreturn(long r10, long r11, long r12, long r13, long mof, long srp,
                                                 sizeof(frame->extramask))))
                goto badframe;
 
-       sigdelsetmask(&set, ~_BLOCKABLE);
        set_current_blocked(&set);
 
        if (restore_sigcontext(regs, &frame->sc))
@@ -208,7 +204,6 @@ sys_rt_sigreturn(long r10, long r11, long r12, long r13, long mof, long srp,
        if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
                goto badframe;
 
-       sigdelsetmask(&set, ~_BLOCKABLE);
        set_current_blocked(&set);
 
        if (restore_sigcontext(regs, &frame->uc.uc_mcontext))
@@ -434,11 +429,12 @@ give_sigsegv:
 }
 
 /* Invoke a signal handler to, well, handle the signal. */
-static inline int
+static inline void
 handle_signal(int canrestart, unsigned long sig,
              siginfo_t *info, struct k_sigaction *ka,
-              sigset_t *oldset, struct pt_regs * regs)
+              struct pt_regs * regs)
 {
+       sigset_t *oldset = sigmask_to_save();
        int ret;
 
        /* Check if this got called from a system call. */
@@ -489,9 +485,7 @@ handle_signal(int canrestart, unsigned long sig,
                ret = setup_frame(sig, ka, oldset, regs);
 
        if (ret == 0)
-               block_sigmask(ka, sig);
-
-       return ret;
+               signal_delivered(sig, info, ka, regs, 0);
 }
 
 /*
@@ -511,7 +505,6 @@ do_signal(int canrestart, struct pt_regs *regs)
        int signr;
        siginfo_t info;
         struct k_sigaction ka;
-       sigset_t *oldset;
 
        /*
         * The common case should go fast, which is why this point is
@@ -521,25 +514,11 @@ do_signal(int canrestart, struct pt_regs *regs)
        if (!user_mode(regs))
                return;
 
-       if (test_thread_flag(TIF_RESTORE_SIGMASK))
-               oldset = &current->saved_sigmask;
-       else
-               oldset = &current->blocked;
-
        signr = get_signal_to_deliver(&info, &ka, regs, NULL);
 
        if (signr > 0) {
                /* Whee!  Actually deliver the signal.  */
-               if (handle_signal(canrestart, signr, &info, &ka,
-                               oldset, regs)) {
-                       /* a signal was successfully delivered; the saved
-                        * sigmask will have been stored in the signal frame,
-                        * and will be restored by sigreturn, so we can simply
-                        * clear the TIF_RESTORE_SIGMASK flag */
-                       if (test_thread_flag(TIF_RESTORE_SIGMASK))
-                               clear_thread_flag(TIF_RESTORE_SIGMASK);
-               }
-
+               handle_signal(canrestart, signr, &info, &ka, regs);
                return;
        }
 
@@ -560,10 +539,7 @@ do_signal(int canrestart, struct pt_regs *regs)
 
        /* if there's no signal to deliver, we just put the saved sigmask
         * back */
-       if (test_thread_flag(TIF_RESTORE_SIGMASK)) {
-               clear_thread_flag(TIF_RESTORE_SIGMASK);
-               sigprocmask(SIG_SETMASK, &current->saved_sigmask, NULL);
-       }
+       restore_saved_sigmask();
 }
 
 asmlinkage void
index 234891c74e2bbe21d0aed4301a7552200829c101..ce4e517931514fb0f6d52a2ef1af35b39aa19ea0 100644 (file)
@@ -15,9 +15,6 @@
 typedef unsigned short __kernel_mode_t;
 #define __kernel_mode_t __kernel_mode_t
 
-typedef unsigned short __kernel_nlink_t;
-#define __kernel_nlink_t __kernel_nlink_t
-
 typedef unsigned short  __kernel_ipc_pid_t;
 #define __kernel_ipc_pid_t __kernel_ipc_pid_t
 
index d114ad3da9b15f95ccc5a68b736d5e80bae75af7..58d44ee1a71f70885aef328fdf6a6af32d6c7b58 100644 (file)
@@ -40,7 +40,5 @@ void do_notify_resume(int canrestart, struct pt_regs *regs,
        if (thread_info_flags & _TIF_NOTIFY_RESUME) {
                clear_thread_flag(TIF_NOTIFY_RESUME);
                tracehook_notify_resume(regs);
-               if (current->replacement_session_keyring)
-                       key_replace_session_keyring();
        }
 }
index 3f34cb45fbb3fafd24edff901ca8b52d9941532d..fe512af74a5afbb57dbc1490214155cdbbeb220d 100644 (file)
@@ -10,9 +10,6 @@
 typedef unsigned short __kernel_mode_t;
 #define __kernel_mode_t __kernel_mode_t
 
-typedef unsigned short __kernel_nlink_t;
-#define __kernel_nlink_t __kernel_nlink_t
-
 typedef unsigned short __kernel_ipc_pid_t;
 #define __kernel_ipc_pid_t __kernel_ipc_pid_t
 
index 54ab13a0de415c1c76d2449626ad5d3ef92c3565..0ff03a33c81e8b193da81f690ce94c0f0c9ad82b 100644 (file)
@@ -94,8 +94,8 @@ register struct thread_info *__current_thread_info asm("gr15");
 #define TIF_NEED_RESCHED       3       /* rescheduling necessary */
 #define TIF_SINGLESTEP         4       /* restore singlestep on return to user mode */
 #define TIF_RESTORE_SIGMASK    5       /* restore signal mask in do_signal() */
-#define TIF_POLLING_NRFLAG     16      /* true if poll_idle() is polling TIF_NEED_RESCHED */
-#define TIF_MEMDIE             17      /* is terminating due to OOM killer */
+#define TIF_POLLING_NRFLAG           /* true if poll_idle() is polling TIF_NEED_RESCHED */
+#define TIF_MEMDIE                   /* is terminating due to OOM killer */
 
 #define _TIF_SYSCALL_TRACE     (1 << TIF_SYSCALL_TRACE)
 #define _TIF_NOTIFY_RESUME     (1 << TIF_NOTIFY_RESUME)
@@ -105,8 +105,16 @@ register struct thread_info *__current_thread_info asm("gr15");
 #define _TIF_RESTORE_SIGMASK   (1 << TIF_RESTORE_SIGMASK)
 #define _TIF_POLLING_NRFLAG    (1 << TIF_POLLING_NRFLAG)
 
-#define _TIF_WORK_MASK         0x0000FFFE      /* work to do on interrupt/exception return */
-#define _TIF_ALLWORK_MASK      0x0000FFFF      /* work to do on any return to u-space */
+/* work to do on interrupt/exception return */
+#define _TIF_WORK_MASK         \
+       (_TIF_NOTIFY_RESUME | _TIF_SIGPENDING | _TIF_NEED_RESCHED | _TIF_SINGLESTEP)
+
+/* work to do on any return to u-space */
+#define _TIF_ALLWORK_MASK      (_TIF_WORK_MASK | _TIF_SYSCALL_TRACE)
+
+#if _TIF_ALLWORK_MASK >= 0x2000
+#error "_TIF_ALLWORK_MASK won't fit in an ANDI now (see entry.S)"
+#endif
 
 /*
  * Thread-synchronous status.
index 5ba23f715ea5e7f0f3dfcc0cab6a860519e41ec6..7d5e000fd32e0e4d40c78c2fcec1806f17bb1286 100644 (file)
@@ -905,18 +905,19 @@ __syscall_call:
 __syscall_exit:
        LEDS            0x6300
 
-       sti             gr8,@(gr28,#REG_GR(8))  ; save return value
+       # keep current PSR in GR23
+       movsg           psr,gr23
 
-       # rebuild saved psr - execve will change it for init/main.c
        ldi             @(gr28,#REG_PSR),gr22
+
+       sti.p           gr8,@(gr28,#REG_GR(8))  ; save return value
+
+       # rebuild saved psr - execve will change it for init/main.c
        srli            gr22,#1,gr5
        andi.p          gr22,#~PSR_PS,gr22
        andi            gr5,#PSR_PS,gr5
        or              gr5,gr22,gr22
-       ori             gr22,#PSR_S,gr22
-
-       # keep current PSR in GR23
-       movsg           psr,gr23
+       ori.p           gr22,#PSR_S,gr22
 
        # make sure we don't miss an interrupt setting need_resched or sigpending between
        # sampling and the RETT
@@ -924,9 +925,7 @@ __syscall_exit:
        movgs           gr23,psr
 
        ldi             @(gr15,#TI_FLAGS),gr4
-       sethi.p         %hi(_TIF_ALLWORK_MASK),gr5
-       setlo           %lo(_TIF_ALLWORK_MASK),gr5
-       andcc           gr4,gr5,gr0,icc0
+       andicc          gr4,#_TIF_ALLWORK_MASK,gr0,icc0
        bne             icc0,#0,__syscall_exit_work
 
        # restore all registers and return
@@ -1111,9 +1110,7 @@ __entry_resume_userspace:
 __entry_return_from_user_interrupt:
        LEDS            0x6402
        ldi             @(gr15,#TI_FLAGS),gr4
-       sethi.p         %hi(_TIF_WORK_MASK),gr5
-       setlo           %lo(_TIF_WORK_MASK),gr5
-       andcc           gr4,gr5,gr0,icc0
+       andicc          gr4,#_TIF_WORK_MASK,gr0,icc0
        beq             icc0,#1,__entry_return_direct
 
 __entry_work_pending:
@@ -1133,9 +1130,7 @@ __entry_work_resched:
 
        LEDS            0x6401
        ldi             @(gr15,#TI_FLAGS),gr4
-       sethi.p         %hi(_TIF_WORK_MASK),gr5
-       setlo           %lo(_TIF_WORK_MASK),gr5
-       andcc           gr4,gr5,gr0,icc0
+       andicc          gr4,#_TIF_WORK_MASK,gr0,icc0
        beq             icc0,#1,__entry_return_direct
        andicc          gr4,#_TIF_NEED_RESCHED,gr0,icc0
        bne             icc0,#1,__entry_work_resched
@@ -1163,7 +1158,9 @@ __syscall_trace_entry:
        # perform syscall exit tracing
 __syscall_exit_work:
        LEDS            0x6340
-       andicc          gr4,#_TIF_SYSCALL_TRACE,gr0,icc0
+       andicc          gr22,#PSR_PS,gr0,icc1   ; don't handle on return to kernel mode
+       andicc.p        gr4,#_TIF_SYSCALL_TRACE,gr0,icc0
+       bne             icc1,#0,__entry_return_direct
        beq             icc0,#1,__entry_work_pending
 
        movsg           psr,gr23
index 8cf5dca01758f75e63041d2c81edf1d8542bbb0c..f3b9064c548c1e3b3870c5a450b9f333d64ff082 100644 (file)
@@ -28,8 +28,6 @@
 
 #define DEBUG_SIG 0
 
-#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
-
 struct fdpic_func_descriptor {
        unsigned long   text;
        unsigned long   GOT;
@@ -149,7 +147,6 @@ asmlinkage int sys_sigreturn(void)
            __copy_from_user(&set.sig[1], &frame->extramask, sizeof(frame->extramask)))
                goto badframe;
 
-       sigdelsetmask(&set, ~_BLOCKABLE);
        set_current_blocked(&set);
 
        if (restore_sigcontext(&frame->sc, &gr8))
@@ -172,7 +169,6 @@ asmlinkage int sys_rt_sigreturn(void)
        if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
                goto badframe;
 
-       sigdelsetmask(&set, ~_BLOCKABLE);
        set_current_blocked(&set);
 
        if (restore_sigcontext(&frame->uc.uc_mcontext, &gr8))
@@ -426,9 +422,10 @@ give_sigsegv:
 /*
  * OK, we're invoking a handler
  */
-static int handle_signal(unsigned long sig, siginfo_t *info,
-                        struct k_sigaction *ka, sigset_t *oldset)
+static void handle_signal(unsigned long sig, siginfo_t *info,
+                        struct k_sigaction *ka)
 {
+       sigset_t *oldset = sigmask_to_save();
        int ret;
 
        /* Are we from a system call? */
@@ -460,11 +457,11 @@ static int handle_signal(unsigned long sig, siginfo_t *info,
        else
                ret = setup_frame(sig, ka, oldset);
 
-       if (ret == 0)
-               block_sigmask(ka, sig);
-
-       return ret;
+       if (ret)
+               return;
 
+       signal_delivered(sig, info, ka, __frame,
+                                test_thread_flag(TIF_SINGLESTEP));
 } /* end handle_signal() */
 
 /*****************************************************************************/
@@ -477,44 +474,14 @@ static void do_signal(void)
 {
        struct k_sigaction ka;
        siginfo_t info;
-       sigset_t *oldset;
        int signr;
 
-       /*
-        * We want the common case to go fast, which
-        * is why we may in certain cases get here from
-        * kernel mode. Just return without doing anything
-        * if so.
-        */
-       if (!user_mode(__frame))
-               return;
-
-       if (try_to_freeze())
-               goto no_signal;
-
-       if (test_thread_flag(TIF_RESTORE_SIGMASK))
-               oldset = &current->saved_sigmask;
-       else
-               oldset = &current->blocked;
-
        signr = get_signal_to_deliver(&info, &ka, __frame, NULL);
        if (signr > 0) {
-               if (handle_signal(signr, &info, &ka, oldset) == 0) {
-                       /* a signal was successfully delivered; the saved
-                        * sigmask will have been stored in the signal frame,
-                        * and will be restored by sigreturn, so we can simply
-                        * clear the TIF_RESTORE_SIGMASK flag */
-                       if (test_thread_flag(TIF_RESTORE_SIGMASK))
-                               clear_thread_flag(TIF_RESTORE_SIGMASK);
-
-                       tracehook_signal_handler(signr, &info, &ka, __frame,
-                                                test_thread_flag(TIF_SINGLESTEP));
-               }
-
+               handle_signal(signr, &info, &ka);
                return;
        }
 
-no_signal:
        /* Did we come from a system call? */
        if (__frame->syscallno != -1) {
                /* Restart the system call - no handlers present */
@@ -536,11 +503,7 @@ no_signal:
 
        /* if there's no signal to deliver, we just put the saved sigmask
         * back */
-       if (test_thread_flag(TIF_RESTORE_SIGMASK)) {
-               clear_thread_flag(TIF_RESTORE_SIGMASK);
-               sigprocmask(SIG_SETMASK, &current->saved_sigmask, NULL);
-       }
-
+       restore_saved_sigmask();
 } /* end do_signal() */
 
 /*****************************************************************************/
@@ -555,15 +518,13 @@ asmlinkage void do_notify_resume(__u32 thread_info_flags)
                clear_thread_flag(TIF_SINGLESTEP);
 
        /* deal with pending signal delivery */
-       if (thread_info_flags & (_TIF_SIGPENDING | _TIF_RESTORE_SIGMASK))
+       if (thread_info_flags & _TIF_SIGPENDING))
                do_signal();
 
        /* deal with notification on about to resume userspace execution */
        if (thread_info_flags & _TIF_NOTIFY_RESUME) {
                clear_thread_flag(TIF_NOTIFY_RESUME);
                tracehook_notify_resume(__frame);
-               if (current->replacement_session_keyring)
-                       key_replace_session_keyring();
        }
 
 } /* end do_notify_resume() */
index bc4c34efb1ad167ccafa90ee8796d605058572a0..91e62ba4c7b02e99cf73893ef04e6a8cf259025a 100644 (file)
@@ -10,9 +10,6 @@
 typedef unsigned short __kernel_mode_t;
 #define __kernel_mode_t __kernel_mode_t
 
-typedef unsigned short __kernel_nlink_t;
-#define __kernel_nlink_t __kernel_nlink_t
-
 typedef unsigned short __kernel_ipc_pid_t;
 #define __kernel_ipc_pid_t __kernel_ipc_pid_t
 
index d4b0555d29047851d9a80d9ae020f30eecc1a370..fca10378701bb9c818b8c123fb4d3c05522cd0c6 100644 (file)
@@ -47,8 +47,6 @@
 #include <asm/traps.h>
 #include <asm/ucontext.h>
 
-#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
-
 /*
  * Atomically swap in the new signal mask, and wait for a signal.
  */
@@ -186,7 +184,6 @@ asmlinkage int do_sigreturn(unsigned long __unused,...)
                              sizeof(frame->extramask))))
                goto badframe;
 
-       sigdelsetmask(&set, ~_BLOCKABLE);
        set_current_blocked(&set);
        
        if (restore_sigcontext(regs, &frame->sc, &er0))
@@ -211,7 +208,6 @@ asmlinkage int do_rt_sigreturn(unsigned long __unused,...)
        if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
                goto badframe;
 
-       sigdelsetmask(&set, ~_BLOCKABLE);
        set_current_blocked(&set);
        
        if (restore_sigcontext(regs, &frame->uc.uc_mcontext, &er0))
@@ -412,8 +408,9 @@ give_sigsegv:
  */
 static void
 handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka,
-             sigset_t *oldset, struct pt_regs * regs)
+             struct pt_regs * regs)
 {
+       sigset_t *oldset = sigmask_to_save();
        int ret;
        /* are we from a system call? */
        if (regs->orig_er0 >= 0) {
@@ -441,10 +438,8 @@ handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka,
        else
                ret = setup_frame(sig, ka, oldset, regs);
 
-       if (!ret) {
-               block_sigmask(ka, sig);
-               clear_thread_flag(TIF_RESTORE_SIGMASK);
-       }
+       if (!ret)
+               signal_delivered(sig, info, ka, regs, 0);
 }
 
 /*
@@ -457,7 +452,6 @@ statis void do_signal(struct pt_regs *regs)
        siginfo_t info;
        int signr;
        struct k_sigaction ka;
-       sigset_t *oldset;
 
        /*
         * We want the common case to go fast, which
@@ -468,23 +462,14 @@ statis void do_signal(struct pt_regs *regs)
        if ((regs->ccr & 0x10))
                return;
 
-       if (try_to_freeze())
-               goto no_signal;
-
        current->thread.esp0 = (unsigned long) regs;
 
-       if (test_thread_flag(TIF_RESTORE_SIGMASK))
-               oldset = &current->saved_sigmask;
-       else
-               oldset = &current->blocked;
-
        signr = get_signal_to_deliver(&info, &ka, regs, NULL);
        if (signr > 0) {
                /* Whee!  Actually deliver the signal.  */
-               handle_signal(signr, &info, &ka, oldset, regs);
+               handle_signal(signr, &info, &ka, regs);
                return;
        }
- no_signal:
        /* Did we come from a system call? */
        if (regs->orig_er0 >= 0) {
                /* Restart the system call - no handlers present */
@@ -501,8 +486,7 @@ statis void do_signal(struct pt_regs *regs)
        }
 
        /* If there's no signal to deliver, we just restore the saved mask.  */
-       if (test_and_clear_thread_flag(TIF_RESTORE_SIGMASK))
-               set_current_blocked(&current->saved_sigmask);
+       restore_saved_sigmask();
 }
 
 asmlinkage void do_notify_resume(struct pt_regs *regs, u32 thread_info_flags)
@@ -513,7 +497,5 @@ asmlinkage void do_notify_resume(struct pt_regs *regs, u32 thread_info_flags)
        if (thread_info_flags & _TIF_NOTIFY_RESUME) {
                clear_thread_flag(TIF_NOTIFY_RESUME);
                tracehook_notify_resume(regs);
-               if (current->replacement_session_keyring)
-                       key_replace_session_keyring();
        }
 }
index 434866eb0f1cf36d639c5e14367458bf272522df..304b0808d07213f0853ee256d781a114f561a947 100644 (file)
@@ -31,8 +31,6 @@
 #include <asm/signal.h>
 #include <asm/vdso.h>
 
-#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
-
 struct rt_sigframe {
        unsigned long tramp[2];
        struct siginfo info;
@@ -149,11 +147,9 @@ sigsegv:
 /*
  * Setup invocation of signal handler
  */
-static int handle_signal(int sig, siginfo_t *info, struct k_sigaction *ka,
-                        sigset_t *oldset, struct pt_regs *regs)
+static void handle_signal(int sig, siginfo_t *info, struct k_sigaction *ka,
+                        struct pt_regs *regs)
 {
-       int rc;
-
        /*
         * If we're handling a signal that aborted a system call,
         * set up the error return value before adding the signal
@@ -186,15 +182,12 @@ static int handle_signal(int sig, siginfo_t *info, struct k_sigaction *ka,
         * Set up the stack frame; not doing the SA_SIGINFO thing.  We
         * only set up the rt_frame flavor.
         */
-       rc = setup_rt_frame(sig, ka, info, oldset, regs);
-
        /* If there was an error on setup, no signal was delivered. */
-       if (rc)
-               return rc;
-
-       block_sigmask(ka, sig);
+       if (setup_rt_frame(sig, ka, info, sigmask_to_save(), regs) < 0)
+               return;
 
-       return 0;
+       signal_delivered(sig, info, ka, regs,
+                       test_thread_flag(TIF_SINGLESTEP));
 }
 
 /*
@@ -209,34 +202,13 @@ static void do_signal(struct pt_regs *regs)
        if (!user_mode(regs))
                return;
 
-       if (try_to_freeze())
-               goto no_signal;
-
        signo = get_signal_to_deliver(&info, &sigact, regs, NULL);
 
        if (signo > 0) {
-               sigset_t *oldset;
-
-               if (test_thread_flag(TIF_RESTORE_SIGMASK))
-                       oldset = &current->saved_sigmask;
-               else
-                       oldset = &current->blocked;
-
-               if (handle_signal(signo, &info, &sigact, oldset, regs) == 0) {
-                       /*
-                        * Successful delivery case.  The saved sigmask is
-                        * stored in the signal frame, and will be restored
-                        * by sigreturn.  We can clear the TIF flag.
-                        */
-                       clear_thread_flag(TIF_RESTORE_SIGMASK);
-
-                       tracehook_signal_handler(signo, &info, &sigact, regs,
-                               test_thread_flag(TIF_SINGLESTEP));
-               }
+               handle_signal(signo, &info, &sigact, regs);
                return;
        }
 
-no_signal:
        /*
         * If we came from a system call, handle the restart.
         */
@@ -259,10 +231,7 @@ no_signal:
 
 no_restart:
        /* If there's no signal to deliver, put the saved sigmask back */
-       if (test_thread_flag(TIF_RESTORE_SIGMASK)) {
-               clear_thread_flag(TIF_RESTORE_SIGMASK);
-               sigprocmask(SIG_SETMASK, &current->saved_sigmask, NULL);
-       }
+       restore_saved_sigmask();
 }
 
 void do_notify_resume(struct pt_regs *regs, unsigned long thread_info_flags)
@@ -273,8 +242,6 @@ void do_notify_resume(struct pt_regs *regs, unsigned long thread_info_flags)
        if (thread_info_flags & _TIF_NOTIFY_RESUME) {
                clear_thread_flag(TIF_NOTIFY_RESUME);
                tracehook_notify_resume(regs);
-               if (current->replacement_session_keyring)
-                       key_replace_session_keyring();
        }
 }
 
@@ -303,7 +270,6 @@ asmlinkage int sys_rt_sigreturn(void)
        if (__copy_from_user(&blocked, &frame->uc.uc_sigmask, sizeof(blocked)))
                goto badframe;
 
-       sigdelsetmask(&blocked, ~_BLOCKABLE);
        set_current_blocked(&blocked);
 
        if (restore_sigcontext(regs, &frame->uc.uc_mcontext))
index 7323ab9467ebae726473512588d2c8f41d0ceb40..99ee1d6510cfc98a7dc66128fca841af022b133f 100644 (file)
@@ -1,9 +1,6 @@
 #ifndef _ASM_IA64_POSIX_TYPES_H
 #define _ASM_IA64_POSIX_TYPES_H
 
-typedef unsigned int   __kernel_nlink_t;
-#define __kernel_nlink_t __kernel_nlink_t
-
 typedef unsigned long  __kernel_sigset_t;      /* at least 32 bits */
 
 #include <asm-generic/posix_types.h>
index 310d9734f02d11a2537311b6860a736ff5fa63a7..f7ee85378311a0f2d241a458640f697b4940a2a1 100644 (file)
@@ -141,7 +141,23 @@ static inline void set_restore_sigmask(void)
 {
        struct thread_info *ti = current_thread_info();
        ti->status |= TS_RESTORE_SIGMASK;
-       set_bit(TIF_SIGPENDING, &ti->flags);
+       WARN_ON(!test_bit(TIF_SIGPENDING, &ti->flags));
+}
+static inline void clear_restore_sigmask(void)
+{
+       current_thread_info()->status &= ~TS_RESTORE_SIGMASK;
+}
+static inline bool test_restore_sigmask(void)
+{
+       return current_thread_info()->status & TS_RESTORE_SIGMASK;
+}
+static inline bool test_and_clear_restore_sigmask(void)
+{
+       struct thread_info *ti = current_thread_info();
+       if (!(ti->status & TS_RESTORE_SIGMASK))
+               return false;
+       ti->status &= ~TS_RESTORE_SIGMASK;
+       return true;
 }
 #endif /* !__ASSEMBLY__ */
 
index f00ba025375d5696d0070bfe640b6f26f554eebd..d7f558c1e7117bfff75a056d4fee9213c6a4b7fb 100644 (file)
@@ -604,12 +604,6 @@ pfm_unprotect_ctx_ctxsw(pfm_context_t *x, unsigned long f)
        spin_unlock(&(x)->ctx_lock);
 }
 
-static inline unsigned long 
-pfm_get_unmapped_area(struct file *file, unsigned long addr, unsigned long len, unsigned long pgoff, unsigned long flags, unsigned long exec)
-{
-       return get_unmapped_area(file, addr, len, pgoff, flags);
-}
-
 /* forward declaration */
 static const struct dentry_operations pfmfs_dentry_operations;
 
@@ -2333,8 +2327,8 @@ pfm_smpl_buffer_alloc(struct task_struct *task, struct file *filp, pfm_context_t
        down_write(&task->mm->mmap_sem);
 
        /* find some free area in address space, must have mmap sem held */
-       vma->vm_start = pfm_get_unmapped_area(NULL, 0, size, 0, MAP_PRIVATE|MAP_ANONYMOUS, 0);
-       if (vma->vm_start == 0UL) {
+       vma->vm_start = get_unmapped_area(NULL, 0, size, 0, MAP_PRIVATE|MAP_ANONYMOUS);
+       if (IS_ERR_VALUE(vma->vm_start)) {
                DPRINT(("Cannot find unmapped area for size %ld\n", size));
                up_write(&task->mm->mmap_sem);
                goto error;
index 5e0e86ddb12f7801ec0a1f7e5579dae48168d5a4..dd6fc14497419dca929be82a44b55ce638bc4705 100644 (file)
@@ -199,8 +199,6 @@ do_notify_resume_user(sigset_t *unused, struct sigscratch *scr, long in_syscall)
        if (test_thread_flag(TIF_NOTIFY_RESUME)) {
                clear_thread_flag(TIF_NOTIFY_RESUME);
                tracehook_notify_resume(&scr->pt);
-               if (current->replacement_session_keyring)
-                       key_replace_session_keyring();
        }
 
        /* copy user rbs to kernel rbs */
index 7523501d3bc087bbb39a22573776ba365d131aeb..a199be1fe619bc12d00a9e87198b6f919a9f2c74 100644 (file)
@@ -30,7 +30,6 @@
 
 #define DEBUG_SIG      0
 #define STACK_ALIGN    16              /* minimal alignment for stack pointer */
-#define _BLOCKABLE     (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
 
 #if _NSIG_WORDS > 1
 # define PUT_SIGSET(k,u)       __copy_to_user((u)->sig, (k)->sig, sizeof(sigset_t))
@@ -200,7 +199,6 @@ ia64_rt_sigreturn (struct sigscratch *scr)
        if (GET_SIGSET(&set, &sc->sc_mask))
                goto give_sigsegv;
 
-       sigdelsetmask(&set, ~_BLOCKABLE);
        set_current_blocked(&set);
 
        if (restore_sigcontext(sc, scr))
@@ -415,18 +413,13 @@ setup_frame (int sig, struct k_sigaction *ka, siginfo_t *info, sigset_t *set,
 }
 
 static long
-handle_signal (unsigned long sig, struct k_sigaction *ka, siginfo_t *info, sigset_t *oldset,
+handle_signal (unsigned long sig, struct k_sigaction *ka, siginfo_t *info,
               struct sigscratch *scr)
 {
-       if (!setup_frame(sig, ka, info, oldset, scr))
+       if (!setup_frame(sig, ka, info, sigmask_to_save(), scr))
                return 0;
 
-       block_sigmask(ka, sig);
-
-       /*
-        * Let tracing know that we've done the handler setup.
-        */
-       tracehook_signal_handler(sig, info, ka, &scr->pt,
+       signal_delivered(sig, info, ka, &scr->pt,
                                 test_thread_flag(TIF_SINGLESTEP));
 
        return 1;
@@ -440,7 +433,6 @@ void
 ia64_do_signal (struct sigscratch *scr, long in_syscall)
 {
        struct k_sigaction ka;
-       sigset_t *oldset;
        siginfo_t info;
        long restart = in_syscall;
        long errno = scr->pt.r8;
@@ -453,11 +445,6 @@ ia64_do_signal (struct sigscratch *scr, long in_syscall)
        if (!user_mode(&scr->pt))
                return;
 
-       if (current_thread_info()->status & TS_RESTORE_SIGMASK)
-               oldset = &current->saved_sigmask;
-       else
-               oldset = &current->blocked;
-
        /*
         * This only loops in the rare cases of handle_signal() failing, in which case we
         * need to push through a forced SIGSEGV.
@@ -507,16 +494,8 @@ ia64_do_signal (struct sigscratch *scr, long in_syscall)
                 * Whee!  Actually deliver the signal.  If the delivery failed, we need to
                 * continue to iterate in this loop so we can deliver the SIGSEGV...
                 */
-               if (handle_signal(signr, &ka, &info, oldset, scr)) {
-                       /*
-                        * A signal was successfully delivered; the saved
-                        * sigmask will have been stored in the signal frame,
-                        * and will be restored by sigreturn, so we can simply
-                        * clear the TS_RESTORE_SIGMASK flag.
-                        */
-                       current_thread_info()->status &= ~TS_RESTORE_SIGMASK;
+               if (handle_signal(signr, &ka, &info, scr))
                        return;
-               }
        }
 
        /* Did we come from a system call? */
@@ -538,8 +517,5 @@ ia64_do_signal (struct sigscratch *scr, long in_syscall)
 
        /* if there's no signal to deliver, we just put the saved sigmask
         * back */
-       if (current_thread_info()->status & TS_RESTORE_SIGMASK) {
-               current_thread_info()->status &= ~TS_RESTORE_SIGMASK;
-               sigprocmask(SIG_SETMASK, &current->saved_sigmask, NULL);
-       }
+       restore_saved_sigmask();
 }
index 609d50056a6c7bd9fba2d46b757960b3e8893388..d9439ef2f66187d9e864f91778b8a9022c41577c 100644 (file)
@@ -171,22 +171,9 @@ asmlinkage unsigned long
 ia64_mremap (unsigned long addr, unsigned long old_len, unsigned long new_len, unsigned long flags,
             unsigned long new_addr)
 {
-       extern unsigned long do_mremap (unsigned long addr,
-                                       unsigned long old_len,
-                                       unsigned long new_len,
-                                       unsigned long flags,
-                                       unsigned long new_addr);
-
-       down_write(&current->mm->mmap_sem);
-       {
-               addr = do_mremap(addr, old_len, new_len, flags, new_addr);
-       }
-       up_write(&current->mm->mmap_sem);
-
-       if (IS_ERR((void *) addr))
-               return addr;
-
-       force_successful_syscall_return();
+       addr = sys_mremap(addr, old_len, new_len, flags, new_addr);
+       if (!IS_ERR((void *) addr))
+               force_successful_syscall_return();
        return addr;
 }
 
index 0195850e1f88698b7a6c29ffd8b807b9e38a5b53..236de26a409b3f9a3d85df67129d88a488d025d1 100644 (file)
@@ -10,9 +10,6 @@
 typedef unsigned short __kernel_mode_t;
 #define __kernel_mode_t __kernel_mode_t
 
-typedef unsigned short __kernel_nlink_t;
-#define __kernel_nlink_t __kernel_nlink_t
-
 typedef unsigned short __kernel_ipc_pid_t;
 #define __kernel_ipc_pid_t __kernel_ipc_pid_t
 
index f54d96993ea187686f7264fdab3867d5ffc821ca..f3fb2c029cfcab061fbbfb5697273067e57c38e8 100644 (file)
@@ -28,8 +28,6 @@
 
 #define DEBUG_SIG 0
 
-#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
-
 asmlinkage int
 sys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss,
                unsigned long r2, unsigned long r3, unsigned long r4,
@@ -111,7 +109,6 @@ sys_rt_sigreturn(unsigned long r0, unsigned long r1,
        if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
                goto badframe;
 
-       sigdelsetmask(&set, ~_BLOCKABLE);
        set_current_blocked(&set);
 
        if (restore_sigcontext(regs, &frame->uc.uc_mcontext, &result))
@@ -267,9 +264,9 @@ static int prev_insn(struct pt_regs *regs)
  * OK, we're invoking a handler
  */
 
-static int
+static void
 handle_signal(unsigned long sig, struct k_sigaction *ka, siginfo_t *info,
-             sigset_t *oldset, struct pt_regs *regs)
+             struct pt_regs *regs)
 {
        /* Are we from a system call? */
        if (regs->syscall_nr >= 0) {
@@ -294,11 +291,10 @@ handle_signal(unsigned long sig, struct k_sigaction *ka, siginfo_t *info,
        }
 
        /* Set up the stack frame */
-       if (setup_rt_frame(sig, ka, info, oldset, regs))
-               return -EFAULT;
+       if (setup_rt_frame(sig, ka, info, sigmask_to_save(), regs))
+               return;
 
-       block_sigmask(ka, sig);
-       return 0;
+       signal_delivered(sig, info, ka, regs, 0);
 }
 
 /*
@@ -311,7 +307,6 @@ static void do_signal(struct pt_regs *regs)
        siginfo_t info;
        int signr;
        struct k_sigaction ka;
-       sigset_t *oldset;
 
        /*
         * We want the common case to go fast, which
@@ -322,14 +317,6 @@ static void do_signal(struct pt_regs *regs)
        if (!user_mode(regs))
                return;
 
-       if (try_to_freeze()) 
-               goto no_signal;
-
-       if (test_thread_flag(TIF_RESTORE_SIGMASK))
-               oldset = &current->saved_sigmask;
-       else
-               oldset = &current->blocked;
-
        signr = get_signal_to_deliver(&info, &ka, regs, NULL);
        if (signr > 0) {
                /* Re-enable any watchpoints before delivering the
@@ -339,13 +326,11 @@ static void do_signal(struct pt_regs *regs)
                 */
 
                /* Whee!  Actually deliver the signal.  */
-               if (handle_signal(signr, &ka, &info, oldset, regs) == 0)
-                       clear_thread_flag(TIF_RESTORE_SIGMASK);
+               handle_signal(signr, &ka, &info, regs);
 
                return;
        }
 
- no_signal:
        /* Did we come from a system call? */
        if (regs->syscall_nr >= 0) {
                /* Restart the system call - no handlers present */
@@ -360,10 +345,7 @@ static void do_signal(struct pt_regs *regs)
                        prev_insn(regs);
                }
        }
-       if (test_thread_flag(TIF_RESTORE_SIGMASK)) {
-               clear_thread_flag(TIF_RESTORE_SIGMASK);
-               sigprocmask(SIG_SETMASK, &current->saved_sigmask, NULL);
-       }
+       restore_saved_sigmask();
 }
 
 /*
@@ -383,8 +365,6 @@ void do_notify_resume(struct pt_regs *regs, __u32 thread_info_flags)
        if (thread_info_flags & _TIF_NOTIFY_RESUME) {
                clear_thread_flag(TIF_NOTIFY_RESUME);
                tracehook_notify_resume(regs);
-               if (current->replacement_session_keyring)
-                       key_replace_session_keyring();
        }
 
        clear_thread_flag(TIF_IRET);
index 6373093be72bb049f37f071468c3b6c73d6daafe..cf4dbf70fdc73f116f95a83c698511fd7b5f4a62 100644 (file)
@@ -10,9 +10,6 @@
 typedef unsigned short __kernel_mode_t;
 #define __kernel_mode_t __kernel_mode_t
 
-typedef unsigned short __kernel_nlink_t;
-#define __kernel_nlink_t __kernel_nlink_t
-
 typedef unsigned short __kernel_ipc_pid_t;
 #define __kernel_ipc_pid_t __kernel_ipc_pid_t
 
index d9f3d1900eed029a5044fb05845dceeccb261844..710a528b928b8580ac4281449586ee4507966db9 100644 (file)
@@ -51,8 +51,6 @@
 #include <asm/traps.h>
 #include <asm/ucontext.h>
 
-#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
-
 #ifdef CONFIG_MMU
 
 /*
@@ -795,7 +793,6 @@ asmlinkage int do_sigreturn(unsigned long __unused)
                              sizeof(frame->extramask))))
                goto badframe;
 
-       sigdelsetmask(&set, ~_BLOCKABLE);
        set_current_blocked(&set);
 
        if (restore_sigcontext(regs, &frame->sc, frame + 1))
@@ -820,7 +817,6 @@ asmlinkage int do_rt_sigreturn(unsigned long __unused)
        if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
                goto badframe;
 
-       sigdelsetmask(&set, ~_BLOCKABLE);
        set_current_blocked(&set);
 
        if (rt_restore_ucontext(regs, sw, &frame->uc))
@@ -1123,8 +1119,9 @@ handle_restart(struct pt_regs *regs, struct k_sigaction *ka, int has_handler)
  */
 static void
 handle_signal(int sig, struct k_sigaction *ka, siginfo_t *info,
-             sigset_t *oldset, struct pt_regs *regs)
+             struct pt_regs *regs)
 {
+       sigset_t *oldset = sigmask_to_save();
        int err;
        /* are we from a system call? */
        if (regs->orig_d0 >= 0)
@@ -1140,14 +1137,12 @@ handle_signal(int sig, struct k_sigaction *ka, siginfo_t *info,
        if (err)
                return;
 
-       block_sigmask(ka, sig);
+       signal_delivered(sig, info, ka, regs, 0);
 
        if (test_thread_flag(TIF_DELAYED_TRACE)) {
                regs->sr &= ~0x8000;
                send_sig(SIGTRAP, current, 1);
        }
-
-       clear_thread_flag(TIF_RESTORE_SIGMASK);
 }
 
 /*
@@ -1160,19 +1155,13 @@ static void do_signal(struct pt_regs *regs)
        siginfo_t info;
        struct k_sigaction ka;
        int signr;
-       sigset_t *oldset;
 
        current->thread.esp0 = (unsigned long) regs;
 
-       if (test_thread_flag(TIF_RESTORE_SIGMASK))
-               oldset = &current->saved_sigmask;
-       else
-               oldset = &current->blocked;
-
        signr = get_signal_to_deliver(&info, &ka, regs, NULL);
        if (signr > 0) {
                /* Whee!  Actually deliver the signal.  */
-               handle_signal(signr, &ka, &info, oldset, regs);
+               handle_signal(signr, &ka, &info, regs);
                return;
        }
 
@@ -1182,10 +1171,7 @@ static void do_signal(struct pt_regs *regs)
                handle_restart(regs, NULL, 0);
 
        /* If there's no signal to deliver, we just restore the saved mask.  */
-       if (test_thread_flag(TIF_RESTORE_SIGMASK)) {
-               clear_thread_flag(TIF_RESTORE_SIGMASK);
-               sigprocmask(SIG_SETMASK, &current->saved_sigmask, NULL);
-       }
+       restore_saved_sigmask();
 }
 
 void do_notify_resume(struct pt_regs *regs)
@@ -1193,9 +1179,6 @@ void do_notify_resume(struct pt_regs *regs)
        if (test_thread_flag(TIF_SIGPENDING))
                do_signal(regs);
 
-       if (test_and_clear_thread_flag(TIF_NOTIFY_RESUME)) {
+       if (test_and_clear_thread_flag(TIF_NOTIFY_RESUME))
                tracehook_notify_resume(regs);
-               if (current->replacement_session_keyring)
-                       key_replace_session_keyring();
-       }
 }
index 1a8ab6a5c03fc3ab399cd8fa75003b4220e69cd5..6c610234ffab96c8d849dfc8dac498bfaf1c9919 100644 (file)
@@ -166,7 +166,23 @@ static inline void set_restore_sigmask(void)
 {
        struct thread_info *ti = current_thread_info();
        ti->status |= TS_RESTORE_SIGMASK;
-       set_bit(TIF_SIGPENDING, (unsigned long *)&ti->flags);
+       WARN_ON(!test_bit(TIF_SIGPENDING, (unsigned long *)&ti->flags));
+}
+static inline void clear_restore_sigmask(void)
+{
+       current_thread_info()->status &= ~TS_RESTORE_SIGMASK;
+}
+static inline bool test_restore_sigmask(void)
+{
+       return current_thread_info()->status & TS_RESTORE_SIGMASK;
+}
+static inline bool test_and_clear_restore_sigmask(void)
+{
+       struct thread_info *ti = current_thread_info();
+       if (!(ti->status & TS_RESTORE_SIGMASK))
+               return false;
+       ti->status &= ~TS_RESTORE_SIGMASK;
+       return true;
 }
 #endif
 
index 7f4c7bef1642e0107afb3ffc7bbb02ce87b74585..76b9722557db77e99a3500154a34b8ed6556d958 100644 (file)
@@ -41,8 +41,6 @@
 #include <asm/cacheflush.h>
 #include <asm/syscalls.h>
 
-#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
-
 asmlinkage long
 sys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss,
                struct pt_regs *regs)
@@ -106,7 +104,6 @@ asmlinkage long sys_rt_sigreturn(struct pt_regs *regs)
        if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
                goto badframe;
 
-       sigdelsetmask(&set, ~_BLOCKABLE);
        set_current_blocked(&set);
 
        if (restore_sigcontext(regs, &frame->uc.uc_mcontext, &rval))
@@ -310,10 +307,11 @@ do_restart:
  * OK, we're invoking a handler
  */
 
-static int
+static void
 handle_signal(unsigned long sig, struct k_sigaction *ka,
-               siginfo_t *info, sigset_t *oldset, struct pt_regs *regs)
+               siginfo_t *info, struct pt_regs *regs)
 {
+       sigset_t *oldset = sigmask_to_save();
        int ret;
 
        /* Set up the stack frame */
@@ -323,11 +321,9 @@ handle_signal(unsigned long sig, struct k_sigaction *ka,
                ret = setup_rt_frame(sig, ka, NULL, oldset, regs);
 
        if (ret)
-               return ret;
-
-       block_sigmask(ka, sig);
+               return;
 
-       return 0;
+       signal_delivered(sig, info, ka, regs, 0);
 }
 
 /*
@@ -344,33 +340,18 @@ static void do_signal(struct pt_regs *regs, int in_syscall)
        siginfo_t info;
        int signr;
        struct k_sigaction ka;
-       sigset_t *oldset;
 #ifdef DEBUG_SIG
        printk(KERN_INFO "do signal: %p %d\n", regs, in_syscall);
        printk(KERN_INFO "do signal2: %lx %lx %ld [%lx]\n", regs->pc, regs->r1,
                        regs->r12, current_thread_info()->flags);
 #endif
 
-       if (current_thread_info()->status & TS_RESTORE_SIGMASK)
-               oldset = &current->saved_sigmask;
-       else
-               oldset = &current->blocked;
-
        signr = get_signal_to_deliver(&info, &ka, regs, NULL);
        if (signr > 0) {
                /* Whee! Actually deliver the signal. */
                if (in_syscall)
                        handle_restart(regs, &ka, 1);
-               if (!handle_signal(signr, &ka, &info, oldset, regs)) {
-                       /*
-                        * A signal was successfully delivered; the saved
-                        * sigmask will have been stored in the signal frame,
-                        * and will be restored by sigreturn, so we can simply
-                        * clear the TS_RESTORE_SIGMASK flag.
-                        */
-                       current_thread_info()->status &=
-                           ~TS_RESTORE_SIGMASK;
-               }
+               handle_signal(signr, &ka, &info, regs);
                return;
        }
 
@@ -381,10 +362,7 @@ static void do_signal(struct pt_regs *regs, int in_syscall)
         * If there's no signal to deliver, we just put the saved sigmask
         * back.
         */
-       if (current_thread_info()->status & TS_RESTORE_SIGMASK) {
-               current_thread_info()->status &= ~TS_RESTORE_SIGMASK;
-               sigprocmask(SIG_SETMASK, &current->saved_sigmask, NULL);
-       }
+       restore_saved_sigmask();
 }
 
 void do_notify_resume(struct pt_regs *regs, int in_syscall)
@@ -401,9 +379,6 @@ void do_notify_resume(struct pt_regs *regs, int in_syscall)
        if (test_thread_flag(TIF_SIGPENDING))
                do_signal(regs, in_syscall);
 
-       if (test_and_clear_thread_flag(TIF_NOTIFY_RESUME)) {
+       if (test_and_clear_thread_flag(TIF_NOTIFY_RESUME))
                tracehook_notify_resume(regs);
-               if (current->replacement_session_keyring)
-                       key_replace_session_keyring();
-       }
 }
index 77050671eeef5ba9629cc5e991b23459350aa555..09ab87ee6fef654eef0220ab05b1224492947efc 100644 (file)
@@ -233,8 +233,9 @@ config LANTIQ
        select ARCH_REQUIRE_GPIOLIB
        select SWAP_IO_SPACE
        select BOOT_RAW
-       select HAVE_CLK
-       select MIPS_MACHINE
+       select HAVE_MACH_CLKDEV
+       select CLKDEV_LOOKUP
+       select USE_OF
 
 config LASAT
        bool "LASAT Networks platforms"
@@ -1783,10 +1784,12 @@ endchoice
 
 config FORCE_MAX_ZONEORDER
        int "Maximum zone order"
-       range 13 64 if SYS_SUPPORTS_HUGETLBFS && PAGE_SIZE_32KB
-       default "13" if SYS_SUPPORTS_HUGETLBFS && PAGE_SIZE_32KB
-       range 12 64 if SYS_SUPPORTS_HUGETLBFS && PAGE_SIZE_16KB
-       default "12" if SYS_SUPPORTS_HUGETLBFS && PAGE_SIZE_16KB
+       range 14 64 if HUGETLB_PAGE && PAGE_SIZE_64KB
+       default "14" if HUGETLB_PAGE && PAGE_SIZE_64KB
+       range 13 64 if HUGETLB_PAGE && PAGE_SIZE_32KB
+       default "13" if HUGETLB_PAGE && PAGE_SIZE_32KB
+       range 12 64 if HUGETLB_PAGE && PAGE_SIZE_16KB
+       default "12" if HUGETLB_PAGE && PAGE_SIZE_16KB
        range 11 64
        default "11"
        help
index a83302b96c0163187b6f87710a34da57ada23a47..7dde01642d6b93527b5a6e1cc6af7d69d429ff34 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/gpio.h>
 #include <linux/i2c.h>
 #include <linux/init.h>
+#include <linux/module.h>
 #include <linux/interrupt.h>
 #include <linux/io.h>
 #include <linux/leds.h>
index e0fae8f4442b792dbb86bcbbf15a260edf91b2ce..f44feee2d67f9f506bdeffe0d5bc2d205a860d7f 100644 (file)
@@ -26,6 +26,18 @@ config ATH79_MACH_AP81
          Say 'Y' here if you want your kernel to support the
          Atheros AP81 reference board.
 
+config ATH79_MACH_DB120
+       bool "Atheros DB120 reference board"
+       select SOC_AR934X
+       select ATH79_DEV_GPIO_BUTTONS
+       select ATH79_DEV_LEDS_GPIO
+       select ATH79_DEV_SPI
+       select ATH79_DEV_USB
+       select ATH79_DEV_WMAC
+       help
+         Say 'Y' here if you want your kernel to support the
+         Atheros DB120 reference board.
+
 config ATH79_MACH_PB44
        bool "Atheros PB44 reference board"
        select SOC_AR71XX
@@ -52,12 +64,14 @@ endmenu
 config SOC_AR71XX
        select USB_ARCH_HAS_EHCI
        select USB_ARCH_HAS_OHCI
+       select HW_HAS_PCI
        def_bool n
 
 config SOC_AR724X
        select USB_ARCH_HAS_EHCI
        select USB_ARCH_HAS_OHCI
        select HW_HAS_PCI
+       select PCI_AR724X if PCI
        def_bool n
 
 config SOC_AR913X
@@ -68,6 +82,15 @@ config SOC_AR933X
        select USB_ARCH_HAS_EHCI
        def_bool n
 
+config SOC_AR934X
+       select USB_ARCH_HAS_EHCI
+       select HW_HAS_PCI
+       select PCI_AR724X if PCI
+       def_bool n
+
+config PCI_AR724X
+       def_bool n
+
 config ATH79_DEV_GPIO_BUTTONS
        def_bool n
 
@@ -81,7 +104,7 @@ config ATH79_DEV_USB
        def_bool n
 
 config ATH79_DEV_WMAC
-       depends on (SOC_AR913X || SOC_AR933X)
+       depends on (SOC_AR913X || SOC_AR933X || SOC_AR934X)
        def_bool n
 
 endif
index 3b911e09dbecb5386136b4872affb515a8438caa..2b54d98263f30acb2b7db532fc44922d20b55385 100644 (file)
@@ -11,6 +11,7 @@
 obj-y  := prom.o setup.o irq.o common.o clock.o gpio.o
 
 obj-$(CONFIG_EARLY_PRINTK)             += early_printk.o
+obj-$(CONFIG_PCI)                      += pci.o
 
 #
 # Devices
@@ -27,5 +28,6 @@ obj-$(CONFIG_ATH79_DEV_WMAC)          += dev-wmac.o
 #
 obj-$(CONFIG_ATH79_MACH_AP121)         += mach-ap121.o
 obj-$(CONFIG_ATH79_MACH_AP81)          += mach-ap81.o
+obj-$(CONFIG_ATH79_MACH_DB120)         += mach-db120.o
 obj-$(CONFIG_ATH79_MACH_PB44)          += mach-pb44.o
 obj-$(CONFIG_ATH79_MACH_UBNT_XM)       += mach-ubnt-xm.o
index 54d0eb4db987266657f1ac5fa58085015d820f4c..b91ad3efe29e816a8b36567f2b6f090e9336c9a8 100644 (file)
@@ -1,8 +1,11 @@
 /*
  *  Atheros AR71XX/AR724X/AR913X common routines
  *
+ *  Copyright (C) 2010-2011 Jaiganesh Narayanan <jnarayanan@atheros.com>
  *  Copyright (C) 2011 Gabor Juhos <juhosg@openwrt.org>
  *
+ *  Parts of this file are based on Atheros' 2.6.15/2.6.31 BSP
+ *
  *  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.
@@ -163,6 +166,82 @@ static void __init ar933x_clocks_init(void)
        ath79_uart_clk.rate = ath79_ref_clk.rate;
 }
 
+static void __init ar934x_clocks_init(void)
+{
+       u32 pll, out_div, ref_div, nint, frac, clk_ctrl, postdiv;
+       u32 cpu_pll, ddr_pll;
+       u32 bootstrap;
+
+       bootstrap = ath79_reset_rr(AR934X_RESET_REG_BOOTSTRAP);
+       if (bootstrap & AR934X_BOOTSTRAP_REF_CLK_40)
+               ath79_ref_clk.rate = 40 * 1000 * 1000;
+       else
+               ath79_ref_clk.rate = 25 * 1000 * 1000;
+
+       pll = ath79_pll_rr(AR934X_PLL_CPU_CONFIG_REG);
+       out_div = (pll >> AR934X_PLL_CPU_CONFIG_OUTDIV_SHIFT) &
+                 AR934X_PLL_CPU_CONFIG_OUTDIV_MASK;
+       ref_div = (pll >> AR934X_PLL_CPU_CONFIG_REFDIV_SHIFT) &
+                 AR934X_PLL_CPU_CONFIG_REFDIV_MASK;
+       nint = (pll >> AR934X_PLL_CPU_CONFIG_NINT_SHIFT) &
+              AR934X_PLL_CPU_CONFIG_NINT_MASK;
+       frac = (pll >> AR934X_PLL_CPU_CONFIG_NFRAC_SHIFT) &
+              AR934X_PLL_CPU_CONFIG_NFRAC_MASK;
+
+       cpu_pll = nint * ath79_ref_clk.rate / ref_div;
+       cpu_pll += frac * ath79_ref_clk.rate / (ref_div * (2 << 6));
+       cpu_pll /= (1 << out_div);
+
+       pll = ath79_pll_rr(AR934X_PLL_DDR_CONFIG_REG);
+       out_div = (pll >> AR934X_PLL_DDR_CONFIG_OUTDIV_SHIFT) &
+                 AR934X_PLL_DDR_CONFIG_OUTDIV_MASK;
+       ref_div = (pll >> AR934X_PLL_DDR_CONFIG_REFDIV_SHIFT) &
+                 AR934X_PLL_DDR_CONFIG_REFDIV_MASK;
+       nint = (pll >> AR934X_PLL_DDR_CONFIG_NINT_SHIFT) &
+              AR934X_PLL_DDR_CONFIG_NINT_MASK;
+       frac = (pll >> AR934X_PLL_DDR_CONFIG_NFRAC_SHIFT) &
+              AR934X_PLL_DDR_CONFIG_NFRAC_MASK;
+
+       ddr_pll = nint * ath79_ref_clk.rate / ref_div;
+       ddr_pll += frac * ath79_ref_clk.rate / (ref_div * (2 << 10));
+       ddr_pll /= (1 << out_div);
+
+       clk_ctrl = ath79_pll_rr(AR934X_PLL_CPU_DDR_CLK_CTRL_REG);
+
+       postdiv = (clk_ctrl >> AR934X_PLL_CPU_DDR_CLK_CTRL_CPU_POST_DIV_SHIFT) &
+                 AR934X_PLL_CPU_DDR_CLK_CTRL_CPU_POST_DIV_MASK;
+
+       if (clk_ctrl & AR934X_PLL_CPU_DDR_CLK_CTRL_CPU_PLL_BYPASS)
+               ath79_cpu_clk.rate = ath79_ref_clk.rate;
+       else if (clk_ctrl & AR934X_PLL_CPU_DDR_CLK_CTRL_CPUCLK_FROM_CPUPLL)
+               ath79_cpu_clk.rate = cpu_pll / (postdiv + 1);
+       else
+               ath79_cpu_clk.rate = ddr_pll / (postdiv + 1);
+
+       postdiv = (clk_ctrl >> AR934X_PLL_CPU_DDR_CLK_CTRL_DDR_POST_DIV_SHIFT) &
+                 AR934X_PLL_CPU_DDR_CLK_CTRL_DDR_POST_DIV_MASK;
+
+       if (clk_ctrl & AR934X_PLL_CPU_DDR_CLK_CTRL_DDR_PLL_BYPASS)
+               ath79_ddr_clk.rate = ath79_ref_clk.rate;
+       else if (clk_ctrl & AR934X_PLL_CPU_DDR_CLK_CTRL_DDRCLK_FROM_DDRPLL)
+               ath79_ddr_clk.rate = ddr_pll / (postdiv + 1);
+       else
+               ath79_ddr_clk.rate = cpu_pll / (postdiv + 1);
+
+       postdiv = (clk_ctrl >> AR934X_PLL_CPU_DDR_CLK_CTRL_AHB_POST_DIV_SHIFT) &
+                 AR934X_PLL_CPU_DDR_CLK_CTRL_AHB_POST_DIV_MASK;
+
+       if (clk_ctrl & AR934X_PLL_CPU_DDR_CLK_CTRL_AHB_PLL_BYPASS)
+               ath79_ahb_clk.rate = ath79_ref_clk.rate;
+       else if (clk_ctrl & AR934X_PLL_CPU_DDR_CLK_CTRL_AHBCLK_FROM_DDRPLL)
+               ath79_ahb_clk.rate = ddr_pll / (postdiv + 1);
+       else
+               ath79_ahb_clk.rate = cpu_pll / (postdiv + 1);
+
+       ath79_wdt_clk.rate = ath79_ref_clk.rate;
+       ath79_uart_clk.rate = ath79_ref_clk.rate;
+}
+
 void __init ath79_clocks_init(void)
 {
        if (soc_is_ar71xx())
@@ -173,6 +252,8 @@ void __init ath79_clocks_init(void)
                ar913x_clocks_init();
        else if (soc_is_ar933x())
                ar933x_clocks_init();
+       else if (soc_is_ar934x())
+               ar934x_clocks_init();
        else
                BUG();
 
index f0fda982b9650698c6b641aeeb6c38d9ea16f060..5a4adfc9d79dc3b3fe891456530ff88e20c5ff90 100644 (file)
@@ -1,9 +1,12 @@
 /*
  *  Atheros AR71XX/AR724X/AR913X common routines
  *
- *  Copyright (C) 2008-2010 Gabor Juhos <juhosg@openwrt.org>
+ *  Copyright (C) 2010-2011 Jaiganesh Narayanan <jnarayanan@atheros.com>
+ *  Copyright (C) 2008-2011 Gabor Juhos <juhosg@openwrt.org>
  *  Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
  *
+ *  Parts of this file are based on Atheros' 2.6.15/2.6.31 BSP
+ *
  *  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.
@@ -67,6 +70,8 @@ void ath79_device_reset_set(u32 mask)
                reg = AR913X_RESET_REG_RESET_MODULE;
        else if (soc_is_ar933x())
                reg = AR933X_RESET_REG_RESET_MODULE;
+       else if (soc_is_ar934x())
+               reg = AR934X_RESET_REG_RESET_MODULE;
        else
                BUG();
 
@@ -91,6 +96,8 @@ void ath79_device_reset_clear(u32 mask)
                reg = AR913X_RESET_REG_RESET_MODULE;
        else if (soc_is_ar933x())
                reg = AR933X_RESET_REG_RESET_MODULE;
+       else if (soc_is_ar934x())
+               reg = AR934X_RESET_REG_RESET_MODULE;
        else
                BUG();
 
index f4956f809072e37f975ee1dd2ac9d3885c425422..45efc63b08b65a981b00dde2fb8526eb1d2382d9 100644 (file)
@@ -89,7 +89,8 @@ void __init ath79_register_uart(void)
 
        if (soc_is_ar71xx() ||
            soc_is_ar724x() ||
-           soc_is_ar913x()) {
+           soc_is_ar913x() ||
+           soc_is_ar934x()) {
                ath79_uart_data[0].uartclk = clk_get_rate(clk);
                platform_device_register(&ath79_uart_device);
        } else if (soc_is_ar933x()) {
index 4b0168a11c010ad7e808bed77329825ed1f582c7..366b35fb164dddbad566cb1c23ec8a25f518396b 100644 (file)
@@ -25,12 +25,10 @@ void __init ath79_register_gpio_keys_polled(int id,
        struct gpio_keys_button *p;
        int err;
 
-       p = kmalloc(nbuttons * sizeof(*p), GFP_KERNEL);
+       p = kmemdup(buttons, nbuttons * sizeof(*p), GFP_KERNEL);
        if (!p)
                return;
 
-       memcpy(p, buttons, nbuttons * sizeof(*p));
-
        pdev = platform_device_alloc("gpio-keys-polled", id);
        if (!pdev)
                goto err_free_buttons;
index cdade68dcd17a311d76af069c24e714d4aa690b6..dcb1debcefb8f81065ee48f7a7ceaa57fd16e6bb 100644 (file)
@@ -24,12 +24,10 @@ void __init ath79_register_leds_gpio(int id,
        struct gpio_led *p;
        int err;
 
-       p = kmalloc(num_leds * sizeof(*p), GFP_KERNEL);
+       p = kmemdup(leds, num_leds * sizeof(*p), GFP_KERNEL);
        if (!p)
                return;
 
-       memcpy(p, leds, num_leds * sizeof(*p));
-
        pdev = platform_device_alloc("leds-gpio", id);
        if (!pdev)
                goto err_free_leds;
index 9c717bf98ffe629f9f9c4061bd49b9114a9a99f5..d6d893c16ad405f6e48aee6f2ef088c36877a8be 100644 (file)
@@ -1,9 +1,12 @@
 /*
  *  Atheros AR913X/AR933X SoC built-in WMAC device support
  *
+ *  Copyright (C) 2010-2011 Jaiganesh Narayanan <jnarayanan@atheros.com>
  *  Copyright (C) 2008-2011 Gabor Juhos <juhosg@openwrt.org>
  *  Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
  *
+ *  Parts of this file are based on Atheros 2.6.15/2.6.31 BSP
+ *
  *  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.
@@ -26,8 +29,7 @@ static struct resource ath79_wmac_resources[] = {
                /* .start and .end fields are filled dynamically */
                .flags  = IORESOURCE_MEM,
        }, {
-               .start  = ATH79_CPU_IRQ_IP2,
-               .end    = ATH79_CPU_IRQ_IP2,
+               /* .start and .end fields are filled dynamically */
                .flags  = IORESOURCE_IRQ,
        },
 };
@@ -53,6 +55,8 @@ static void __init ar913x_wmac_setup(void)
 
        ath79_wmac_resources[0].start = AR913X_WMAC_BASE;
        ath79_wmac_resources[0].end = AR913X_WMAC_BASE + AR913X_WMAC_SIZE - 1;
+       ath79_wmac_resources[1].start = ATH79_CPU_IRQ_IP2;
+       ath79_wmac_resources[1].end = ATH79_CPU_IRQ_IP2;
 }
 
 
@@ -79,6 +83,8 @@ static void __init ar933x_wmac_setup(void)
 
        ath79_wmac_resources[0].start = AR933X_WMAC_BASE;
        ath79_wmac_resources[0].end = AR933X_WMAC_BASE + AR933X_WMAC_SIZE - 1;
+       ath79_wmac_resources[1].start = ATH79_CPU_IRQ_IP2;
+       ath79_wmac_resources[1].end = ATH79_CPU_IRQ_IP2;
 
        t = ath79_reset_rr(AR933X_RESET_REG_BOOTSTRAP);
        if (t & AR933X_BOOTSTRAP_REF_CLK_40)
@@ -92,12 +98,32 @@ static void __init ar933x_wmac_setup(void)
        ath79_wmac_data.external_reset = ar933x_wmac_reset;
 }
 
+static void ar934x_wmac_setup(void)
+{
+       u32 t;
+
+       ath79_wmac_device.name = "ar934x_wmac";
+
+       ath79_wmac_resources[0].start = AR934X_WMAC_BASE;
+       ath79_wmac_resources[0].end = AR934X_WMAC_BASE + AR934X_WMAC_SIZE - 1;
+       ath79_wmac_resources[1].start = ATH79_IP2_IRQ(1);
+       ath79_wmac_resources[1].start = ATH79_IP2_IRQ(1);
+
+       t = ath79_reset_rr(AR934X_RESET_REG_BOOTSTRAP);
+       if (t & AR934X_BOOTSTRAP_REF_CLK_40)
+               ath79_wmac_data.is_clk_25mhz = false;
+       else
+               ath79_wmac_data.is_clk_25mhz = true;
+}
+
 void __init ath79_register_wmac(u8 *cal_data)
 {
        if (soc_is_ar913x())
                ar913x_wmac_setup();
        else if (soc_is_ar933x())
                ar933x_wmac_setup();
+       else if (soc_is_ar934x())
+               ar934x_wmac_setup();
        else
                BUG();
 
index 6a51ced7a293fe840351d1e7b0ff5f467784a334..dc938cb2ba58f13d320d67b169eed69636cc5ed8 100644 (file)
@@ -71,6 +71,9 @@ static void prom_putchar_init(void)
        case REV_ID_MAJOR_AR7241:
        case REV_ID_MAJOR_AR7242:
        case REV_ID_MAJOR_AR913X:
+       case REV_ID_MAJOR_AR9341:
+       case REV_ID_MAJOR_AR9342:
+       case REV_ID_MAJOR_AR9344:
                _prom_putchar = prom_putchar_ar71xx;
                break;
 
index a2f8ca630ed667bb49c2144129f2232c7c047fc1..29054f211832505d371930d3e13f752af56f69f3 100644 (file)
@@ -1,9 +1,12 @@
 /*
  *  Atheros AR71XX/AR724X/AR913X GPIO API support
  *
- *  Copyright (C) 2008-2010 Gabor Juhos <juhosg@openwrt.org>
+ *  Copyright (C) 2010-2011 Jaiganesh Narayanan <jnarayanan@atheros.com>
+ *  Copyright (C) 2008-2011 Gabor Juhos <juhosg@openwrt.org>
  *  Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
  *
+ *  Parts of this file are based on Atheros' 2.6.15/2.6.31 BSP
+ *
  *  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.
@@ -89,6 +92,42 @@ static int ath79_gpio_direction_output(struct gpio_chip *chip,
        return 0;
 }
 
+static int ar934x_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+       void __iomem *base = ath79_gpio_base;
+       unsigned long flags;
+
+       spin_lock_irqsave(&ath79_gpio_lock, flags);
+
+       __raw_writel(__raw_readl(base + AR71XX_GPIO_REG_OE) | (1 << offset),
+                    base + AR71XX_GPIO_REG_OE);
+
+       spin_unlock_irqrestore(&ath79_gpio_lock, flags);
+
+       return 0;
+}
+
+static int ar934x_gpio_direction_output(struct gpio_chip *chip, unsigned offset,
+                                       int value)
+{
+       void __iomem *base = ath79_gpio_base;
+       unsigned long flags;
+
+       spin_lock_irqsave(&ath79_gpio_lock, flags);
+
+       if (value)
+               __raw_writel(1 << offset, base + AR71XX_GPIO_REG_SET);
+       else
+               __raw_writel(1 << offset, base + AR71XX_GPIO_REG_CLEAR);
+
+       __raw_writel(__raw_readl(base + AR71XX_GPIO_REG_OE) & ~(1 << offset),
+                    base + AR71XX_GPIO_REG_OE);
+
+       spin_unlock_irqrestore(&ath79_gpio_lock, flags);
+
+       return 0;
+}
+
 static struct gpio_chip ath79_gpio_chip = {
        .label                  = "ath79",
        .get                    = ath79_gpio_get_value,
@@ -155,11 +194,17 @@ void __init ath79_gpio_init(void)
                ath79_gpio_count = AR913X_GPIO_COUNT;
        else if (soc_is_ar933x())
                ath79_gpio_count = AR933X_GPIO_COUNT;
+       else if (soc_is_ar934x())
+               ath79_gpio_count = AR934X_GPIO_COUNT;
        else
                BUG();
 
        ath79_gpio_base = ioremap_nocache(AR71XX_GPIO_BASE, AR71XX_GPIO_SIZE);
        ath79_gpio_chip.ngpio = ath79_gpio_count;
+       if (soc_is_ar934x()) {
+               ath79_gpio_chip.direction_input = ar934x_gpio_direction_input;
+               ath79_gpio_chip.direction_output = ar934x_gpio_direction_output;
+       }
 
        err = gpiochip_add(&ath79_gpio_chip);
        if (err)
index 1b073de44680ea0fdba8c95f84cf6f214f394448..90d09fc15398c18cd6ba89f1e3f33653da31a0f4 100644 (file)
@@ -1,10 +1,11 @@
 /*
  *  Atheros AR71xx/AR724x/AR913x specific interrupt handling
  *
- *  Copyright (C) 2008-2010 Gabor Juhos <juhosg@openwrt.org>
+ *  Copyright (C) 2010-2011 Jaiganesh Narayanan <jnarayanan@atheros.com>
+ *  Copyright (C) 2008-2011 Gabor Juhos <juhosg@openwrt.org>
  *  Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
  *
- *  Parts of this file are based on Atheros' 2.6.15 BSP
+ *  Parts of this file are based on Atheros' 2.6.15/2.6.31 BSP
  *
  *  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
@@ -23,8 +24,8 @@
 #include <asm/mach-ath79/ar71xx_regs.h>
 #include "common.h"
 
-static unsigned int ath79_ip2_flush_reg;
-static unsigned int ath79_ip3_flush_reg;
+static void (*ath79_ip2_handler)(void);
+static void (*ath79_ip3_handler)(void);
 
 static void ath79_misc_irq_handler(unsigned int irq, struct irq_desc *desc)
 {
@@ -129,7 +130,7 @@ static void __init ath79_misc_irq_init(void)
 
        if (soc_is_ar71xx() || soc_is_ar913x())
                ath79_misc_irq_chip.irq_mask_ack = ar71xx_misc_irq_mask;
-       else if (soc_is_ar724x() || soc_is_ar933x())
+       else if (soc_is_ar724x() || soc_is_ar933x() || soc_is_ar934x())
                ath79_misc_irq_chip.irq_ack = ar724x_misc_irq_ack;
        else
                BUG();
@@ -143,6 +144,39 @@ static void __init ath79_misc_irq_init(void)
        irq_set_chained_handler(ATH79_CPU_IRQ_MISC, ath79_misc_irq_handler);
 }
 
+static void ar934x_ip2_irq_dispatch(unsigned int irq, struct irq_desc *desc)
+{
+       u32 status;
+
+       disable_irq_nosync(irq);
+
+       status = ath79_reset_rr(AR934X_RESET_REG_PCIE_WMAC_INT_STATUS);
+
+       if (status & AR934X_PCIE_WMAC_INT_PCIE_ALL) {
+               ath79_ddr_wb_flush(AR934X_DDR_REG_FLUSH_PCIE);
+               generic_handle_irq(ATH79_IP2_IRQ(0));
+       } else if (status & AR934X_PCIE_WMAC_INT_WMAC_ALL) {
+               ath79_ddr_wb_flush(AR934X_DDR_REG_FLUSH_WMAC);
+               generic_handle_irq(ATH79_IP2_IRQ(1));
+       } else {
+               spurious_interrupt();
+       }
+
+       enable_irq(irq);
+}
+
+static void ar934x_ip2_irq_init(void)
+{
+       int i;
+
+       for (i = ATH79_IP2_IRQ_BASE;
+            i < ATH79_IP2_IRQ_BASE + ATH79_IP2_IRQ_COUNT; i++)
+               irq_set_chip_and_handler(i, &dummy_irq_chip,
+                                        handle_level_irq);
+
+       irq_set_chained_handler(ATH79_CPU_IRQ_IP2, ar934x_ip2_irq_dispatch);
+}
+
 asmlinkage void plat_irq_dispatch(void)
 {
        unsigned long pending;
@@ -152,10 +186,8 @@ asmlinkage void plat_irq_dispatch(void)
        if (pending & STATUSF_IP7)
                do_IRQ(ATH79_CPU_IRQ_TIMER);
 
-       else if (pending & STATUSF_IP2) {
-               ath79_ddr_wb_flush(ath79_ip2_flush_reg);
-               do_IRQ(ATH79_CPU_IRQ_IP2);
-       }
+       else if (pending & STATUSF_IP2)
+               ath79_ip2_handler();
 
        else if (pending & STATUSF_IP4)
                do_IRQ(ATH79_CPU_IRQ_GE0);
@@ -163,10 +195,8 @@ asmlinkage void plat_irq_dispatch(void)
        else if (pending & STATUSF_IP5)
                do_IRQ(ATH79_CPU_IRQ_GE1);
 
-       else if (pending & STATUSF_IP3) {
-               ath79_ddr_wb_flush(ath79_ip3_flush_reg);
-               do_IRQ(ATH79_CPU_IRQ_USB);
-       }
+       else if (pending & STATUSF_IP3)
+               ath79_ip3_handler();
 
        else if (pending & STATUSF_IP6)
                do_IRQ(ATH79_CPU_IRQ_MISC);
@@ -175,24 +205,97 @@ asmlinkage void plat_irq_dispatch(void)
                spurious_interrupt();
 }
 
+/*
+ * The IP2/IP3 lines are tied to a PCI/WMAC/USB device. Drivers for
+ * these devices typically allocate coherent DMA memory, however the
+ * DMA controller may still have some unsynchronized data in the FIFO.
+ * Issue a flush in the handlers to ensure that the driver sees
+ * the update.
+ */
+static void ar71xx_ip2_handler(void)
+{
+       ath79_ddr_wb_flush(AR71XX_DDR_REG_FLUSH_PCI);
+       do_IRQ(ATH79_CPU_IRQ_IP2);
+}
+
+static void ar724x_ip2_handler(void)
+{
+       ath79_ddr_wb_flush(AR724X_DDR_REG_FLUSH_PCIE);
+       do_IRQ(ATH79_CPU_IRQ_IP2);
+}
+
+static void ar913x_ip2_handler(void)
+{
+       ath79_ddr_wb_flush(AR913X_DDR_REG_FLUSH_WMAC);
+       do_IRQ(ATH79_CPU_IRQ_IP2);
+}
+
+static void ar933x_ip2_handler(void)
+{
+       ath79_ddr_wb_flush(AR933X_DDR_REG_FLUSH_WMAC);
+       do_IRQ(ATH79_CPU_IRQ_IP2);
+}
+
+static void ar934x_ip2_handler(void)
+{
+       do_IRQ(ATH79_CPU_IRQ_IP2);
+}
+
+static void ar71xx_ip3_handler(void)
+{
+       ath79_ddr_wb_flush(AR71XX_DDR_REG_FLUSH_USB);
+       do_IRQ(ATH79_CPU_IRQ_USB);
+}
+
+static void ar724x_ip3_handler(void)
+{
+       ath79_ddr_wb_flush(AR724X_DDR_REG_FLUSH_USB);
+       do_IRQ(ATH79_CPU_IRQ_USB);
+}
+
+static void ar913x_ip3_handler(void)
+{
+       ath79_ddr_wb_flush(AR913X_DDR_REG_FLUSH_USB);
+       do_IRQ(ATH79_CPU_IRQ_USB);
+}
+
+static void ar933x_ip3_handler(void)
+{
+       ath79_ddr_wb_flush(AR933X_DDR_REG_FLUSH_USB);
+       do_IRQ(ATH79_CPU_IRQ_USB);
+}
+
+static void ar934x_ip3_handler(void)
+{
+       ath79_ddr_wb_flush(AR934X_DDR_REG_FLUSH_USB);
+       do_IRQ(ATH79_CPU_IRQ_USB);
+}
+
 void __init arch_init_irq(void)
 {
        if (soc_is_ar71xx()) {
-               ath79_ip2_flush_reg = AR71XX_DDR_REG_FLUSH_PCI;
-               ath79_ip3_flush_reg = AR71XX_DDR_REG_FLUSH_USB;
+               ath79_ip2_handler = ar71xx_ip2_handler;
+               ath79_ip3_handler = ar71xx_ip3_handler;
        } else if (soc_is_ar724x()) {
-               ath79_ip2_flush_reg = AR724X_DDR_REG_FLUSH_PCIE;
-               ath79_ip3_flush_reg = AR724X_DDR_REG_FLUSH_USB;
+               ath79_ip2_handler = ar724x_ip2_handler;
+               ath79_ip3_handler = ar724x_ip3_handler;
        } else if (soc_is_ar913x()) {
-               ath79_ip2_flush_reg = AR913X_DDR_REG_FLUSH_WMAC;
-               ath79_ip3_flush_reg = AR913X_DDR_REG_FLUSH_USB;
+               ath79_ip2_handler = ar913x_ip2_handler;
+               ath79_ip3_handler = ar913x_ip3_handler;
        } else if (soc_is_ar933x()) {
-               ath79_ip2_flush_reg = AR933X_DDR_REG_FLUSH_WMAC;
-               ath79_ip3_flush_reg = AR933X_DDR_REG_FLUSH_USB;
-       } else
+               ath79_ip2_handler = ar933x_ip2_handler;
+               ath79_ip3_handler = ar933x_ip3_handler;
+       } else if (soc_is_ar934x()) {
+               ath79_ip2_handler = ar934x_ip2_handler;
+               ath79_ip3_handler = ar934x_ip3_handler;
+       } else {
                BUG();
+       }
 
        cp0_perfcount_irq = ATH79_MISC_IRQ_PERFC;
        mips_cpu_irq_init();
        ath79_misc_irq_init();
+
+       if (soc_is_ar934x())
+               ar934x_ip2_irq_init();
 }
diff --git a/arch/mips/ath79/mach-db120.c b/arch/mips/ath79/mach-db120.c
new file mode 100644 (file)
index 0000000..1983e4d
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * Atheros DB120 reference board support
+ *
+ * Copyright (c) 2011 Qualcomm Atheros
+ * Copyright (c) 2011 Gabor Juhos <juhosg@openwrt.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include <linux/pci.h>
+#include <linux/ath9k_platform.h>
+
+#include "machtypes.h"
+#include "dev-gpio-buttons.h"
+#include "dev-leds-gpio.h"
+#include "dev-spi.h"
+#include "dev-wmac.h"
+#include "pci.h"
+
+#define DB120_GPIO_LED_WLAN_5G         12
+#define DB120_GPIO_LED_WLAN_2G         13
+#define DB120_GPIO_LED_STATUS          14
+#define DB120_GPIO_LED_WPS             15
+
+#define DB120_GPIO_BTN_WPS             16
+
+#define DB120_KEYS_POLL_INTERVAL       20      /* msecs */
+#define DB120_KEYS_DEBOUNCE_INTERVAL   (3 * DB120_KEYS_POLL_INTERVAL)
+
+#define DB120_WMAC_CALDATA_OFFSET 0x1000
+#define DB120_PCIE_CALDATA_OFFSET 0x5000
+
+static struct gpio_led db120_leds_gpio[] __initdata = {
+       {
+               .name           = "db120:green:status",
+               .gpio           = DB120_GPIO_LED_STATUS,
+               .active_low     = 1,
+       },
+       {
+               .name           = "db120:green:wps",
+               .gpio           = DB120_GPIO_LED_WPS,
+               .active_low     = 1,
+       },
+       {
+               .name           = "db120:green:wlan-5g",
+               .gpio           = DB120_GPIO_LED_WLAN_5G,
+               .active_low     = 1,
+       },
+       {
+               .name           = "db120:green:wlan-2g",
+               .gpio           = DB120_GPIO_LED_WLAN_2G,
+               .active_low     = 1,
+       },
+};
+
+static struct gpio_keys_button db120_gpio_keys[] __initdata = {
+       {
+               .desc           = "WPS button",
+               .type           = EV_KEY,
+               .code           = KEY_WPS_BUTTON,
+               .debounce_interval = DB120_KEYS_DEBOUNCE_INTERVAL,
+               .gpio           = DB120_GPIO_BTN_WPS,
+               .active_low     = 1,
+       },
+};
+
+static struct spi_board_info db120_spi_info[] = {
+       {
+               .bus_num        = 0,
+               .chip_select    = 0,
+               .max_speed_hz   = 25000000,
+               .modalias       = "s25sl064a",
+       }
+};
+
+static struct ath79_spi_platform_data db120_spi_data = {
+       .bus_num        = 0,
+       .num_chipselect = 1,
+};
+
+#ifdef CONFIG_PCI
+static struct ath9k_platform_data db120_ath9k_data;
+
+static int db120_pci_plat_dev_init(struct pci_dev *dev)
+{
+       switch (PCI_SLOT(dev->devfn)) {
+       case 0:
+               dev->dev.platform_data = &db120_ath9k_data;
+               break;
+       }
+
+       return 0;
+}
+
+static void __init db120_pci_init(u8 *eeprom)
+{
+       memcpy(db120_ath9k_data.eeprom_data, eeprom,
+              sizeof(db120_ath9k_data.eeprom_data));
+
+       ath79_pci_set_plat_dev_init(db120_pci_plat_dev_init);
+       ath79_register_pci();
+}
+#else
+static inline void db120_pci_init(void) {}
+#endif /* CONFIG_PCI */
+
+static void __init db120_setup(void)
+{
+       u8 *art = (u8 *) KSEG1ADDR(0x1fff0000);
+
+       ath79_register_leds_gpio(-1, ARRAY_SIZE(db120_leds_gpio),
+                                db120_leds_gpio);
+       ath79_register_gpio_keys_polled(-1, DB120_KEYS_POLL_INTERVAL,
+                                       ARRAY_SIZE(db120_gpio_keys),
+                                       db120_gpio_keys);
+       ath79_register_spi(&db120_spi_data, db120_spi_info,
+                          ARRAY_SIZE(db120_spi_info));
+       ath79_register_wmac(art + DB120_WMAC_CALDATA_OFFSET);
+       db120_pci_init(art + DB120_PCIE_CALDATA_OFFSET);
+}
+
+MIPS_MACHINE(ATH79_MACH_DB120, "DB120", "Atheros DB120 reference board",
+            db120_setup);
index fe9701a322916bbb946b2cc037aa57fd02d6329f..c5f0ea5e00c38dc0e76312b401cf21793a91dcc5 100644 (file)
@@ -19,6 +19,7 @@
 #include "dev-leds-gpio.h"
 #include "dev-spi.h"
 #include "dev-usb.h"
+#include "pci.h"
 
 #define PB44_GPIO_I2C_SCL      0
 #define PB44_GPIO_I2C_SDA      1
@@ -114,6 +115,7 @@ static void __init pb44_init(void)
        ath79_register_spi(&pb44_spi_data, pb44_spi_info,
                           ARRAY_SIZE(pb44_spi_info));
        ath79_register_usb();
+       ath79_register_pci();
 }
 
 MIPS_MACHINE(ATH79_MACH_PB44, "PB44", "Atheros PB44 reference board",
index 3c311a5393471cb69cfa158bb2ba0c9ab417ddec..4a3c60694c756fedaa3a3f9e18687065789cb721 100644 (file)
 
 #include <linux/init.h>
 #include <linux/pci.h>
-
-#ifdef CONFIG_PCI
 #include <linux/ath9k_platform.h>
-#include <asm/mach-ath79/pci-ath724x.h>
-#endif /* CONFIG_PCI */
+
+#include <asm/mach-ath79/irq.h>
 
 #include "machtypes.h"
 #include "dev-gpio-buttons.h"
 #include "dev-leds-gpio.h"
 #include "dev-spi.h"
+#include "pci.h"
 
 #define UBNT_XM_GPIO_LED_L1            0
 #define UBNT_XM_GPIO_LED_L2            1
@@ -33,7 +32,6 @@
 #define UBNT_XM_KEYS_POLL_INTERVAL     20
 #define UBNT_XM_KEYS_DEBOUNCE_INTERVAL (3 * UBNT_XM_KEYS_POLL_INTERVAL)
 
-#define UBNT_XM_PCI_IRQ                        48
 #define UBNT_XM_EEPROM_ADDR            (u8 *) KSEG1ADDR(0x1fff1000)
 
 static struct gpio_led ubnt_xm_leds_gpio[] __initdata = {
@@ -84,12 +82,27 @@ static struct ath79_spi_platform_data ubnt_xm_spi_data = {
 #ifdef CONFIG_PCI
 static struct ath9k_platform_data ubnt_xm_eeprom_data;
 
-static struct ath724x_pci_data ubnt_xm_pci_data[] = {
-       {
-               .irq    = UBNT_XM_PCI_IRQ,
-               .pdata  = &ubnt_xm_eeprom_data,
-       },
-};
+static int ubnt_xm_pci_plat_dev_init(struct pci_dev *dev)
+{
+       switch (PCI_SLOT(dev->devfn)) {
+       case 0:
+               dev->dev.platform_data = &ubnt_xm_eeprom_data;
+               break;
+       }
+
+       return 0;
+}
+
+static void __init ubnt_xm_pci_init(void)
+{
+       memcpy(ubnt_xm_eeprom_data.eeprom_data, UBNT_XM_EEPROM_ADDR,
+              sizeof(ubnt_xm_eeprom_data.eeprom_data));
+
+       ath79_pci_set_plat_dev_init(ubnt_xm_pci_plat_dev_init);
+       ath79_register_pci();
+}
+#else
+static inline void ubnt_xm_pci_init(void) {}
 #endif /* CONFIG_PCI */
 
 static void __init ubnt_xm_init(void)
@@ -104,13 +117,7 @@ static void __init ubnt_xm_init(void)
        ath79_register_spi(&ubnt_xm_spi_data, ubnt_xm_spi_info,
                           ARRAY_SIZE(ubnt_xm_spi_info));
 
-#ifdef CONFIG_PCI
-       memcpy(ubnt_xm_eeprom_data.eeprom_data, UBNT_XM_EEPROM_ADDR,
-              sizeof(ubnt_xm_eeprom_data.eeprom_data));
-
-       ath724x_pci_add_data(ubnt_xm_pci_data, ARRAY_SIZE(ubnt_xm_pci_data));
-#endif /* CONFIG_PCI */
-
+       ubnt_xm_pci_init();
 }
 
 MIPS_MACHINE(ATH79_MACH_UBNT_XM,
index 9a1f3826626e13703c24abc17b05a930f7528558..af92e5c30d66306fd46490bea8c9cd49c56bd1c9 100644 (file)
@@ -18,6 +18,7 @@ enum ath79_mach_type {
        ATH79_MACH_GENERIC = 0,
        ATH79_MACH_AP121,               /* Atheros AP121 reference board */
        ATH79_MACH_AP81,                /* Atheros AP81 reference board */
+       ATH79_MACH_DB120,               /* Atheros DB120 reference board */
        ATH79_MACH_PB44,                /* Atheros PB44 reference board */
        ATH79_MACH_UBNT_XM,             /* Ubiquiti Networks XM board rev 1.0 */
 };
diff --git a/arch/mips/ath79/pci.c b/arch/mips/ath79/pci.c
new file mode 100644 (file)
index 0000000..ca83abd
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ *  Atheros AR71XX/AR724X specific PCI setup code
+ *
+ *  Copyright (C) 2011 René Bolldorf <xsecute@googlemail.com>
+ *  Copyright (C) 2008-2011 Gabor Juhos <juhosg@openwrt.org>
+ *  Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
+ *
+ *  Parts of this file are based on Atheros' 2.6.15 BSP
+ *
+ *  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/pci.h>
+#include <asm/mach-ath79/ar71xx_regs.h>
+#include <asm/mach-ath79/ath79.h>
+#include <asm/mach-ath79/irq.h>
+#include <asm/mach-ath79/pci.h>
+#include "pci.h"
+
+static int (*ath79_pci_plat_dev_init)(struct pci_dev *dev);
+static const struct ath79_pci_irq *ath79_pci_irq_map __initdata;
+static unsigned ath79_pci_nr_irqs __initdata;
+
+static const struct ath79_pci_irq ar71xx_pci_irq_map[] __initconst = {
+       {
+               .slot   = 17,
+               .pin    = 1,
+               .irq    = ATH79_PCI_IRQ(0),
+       }, {
+               .slot   = 18,
+               .pin    = 1,
+               .irq    = ATH79_PCI_IRQ(1),
+       }, {
+               .slot   = 19,
+               .pin    = 1,
+               .irq    = ATH79_PCI_IRQ(2),
+       }
+};
+
+static const struct ath79_pci_irq ar724x_pci_irq_map[] __initconst = {
+       {
+               .slot   = 0,
+               .pin    = 1,
+               .irq    = ATH79_PCI_IRQ(0),
+       }
+};
+
+int __init pcibios_map_irq(const struct pci_dev *dev, uint8_t slot, uint8_t pin)
+{
+       int irq = -1;
+       int i;
+
+       if (ath79_pci_nr_irqs == 0 ||
+           ath79_pci_irq_map == NULL) {
+               if (soc_is_ar71xx()) {
+                       ath79_pci_irq_map = ar71xx_pci_irq_map;
+                       ath79_pci_nr_irqs = ARRAY_SIZE(ar71xx_pci_irq_map);
+               } else if (soc_is_ar724x() ||
+                          soc_is_ar9342() ||
+                          soc_is_ar9344()) {
+                       ath79_pci_irq_map = ar724x_pci_irq_map;
+                       ath79_pci_nr_irqs = ARRAY_SIZE(ar724x_pci_irq_map);
+               } else {
+                       pr_crit("pci %s: invalid irq map\n",
+                               pci_name((struct pci_dev *) dev));
+                       return irq;
+               }
+       }
+
+       for (i = 0; i < ath79_pci_nr_irqs; i++) {
+               const struct ath79_pci_irq *entry;
+
+               entry = &ath79_pci_irq_map[i];
+               if (entry->slot == slot && entry->pin == pin) {
+                       irq = entry->irq;
+                       break;
+               }
+       }
+
+       if (irq < 0)
+               pr_crit("pci %s: no irq found for pin %u\n",
+                       pci_name((struct pci_dev *) dev), pin);
+       else
+               pr_info("pci %s: using irq %d for pin %u\n",
+                       pci_name((struct pci_dev *) dev), irq, pin);
+
+       return irq;
+}
+
+int pcibios_plat_dev_init(struct pci_dev *dev)
+{
+       if (ath79_pci_plat_dev_init)
+               return ath79_pci_plat_dev_init(dev);
+
+       return 0;
+}
+
+void __init ath79_pci_set_irq_map(unsigned nr_irqs,
+                                 const struct ath79_pci_irq *map)
+{
+       ath79_pci_nr_irqs = nr_irqs;
+       ath79_pci_irq_map = map;
+}
+
+void __init ath79_pci_set_plat_dev_init(int (*func)(struct pci_dev *dev))
+{
+       ath79_pci_plat_dev_init = func;
+}
+
+int __init ath79_register_pci(void)
+{
+       if (soc_is_ar71xx())
+               return ar71xx_pcibios_init();
+
+       if (soc_is_ar724x())
+               return ar724x_pcibios_init(ATH79_CPU_IRQ_IP2);
+
+       if (soc_is_ar9342() || soc_is_ar9344()) {
+               u32 bootstrap;
+
+               bootstrap = ath79_reset_rr(AR934X_RESET_REG_BOOTSTRAP);
+               if (bootstrap & AR934X_BOOTSTRAP_PCIE_RC)
+                       return ar724x_pcibios_init(ATH79_IP2_IRQ(0));
+       }
+
+       return -ENODEV;
+}
diff --git a/arch/mips/ath79/pci.h b/arch/mips/ath79/pci.h
new file mode 100644 (file)
index 0000000..51c6625
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ *  Atheros AR71XX/AR724X PCI support
+ *
+ *  Copyright (C) 2011 René Bolldorf <xsecute@googlemail.com>
+ *  Copyright (C) 2008-2011 Gabor Juhos <juhosg@openwrt.org>
+ *  Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
+ *
+ *  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 _ATH79_PCI_H
+#define _ATH79_PCI_H
+
+struct ath79_pci_irq {
+       u8      slot;
+       u8      pin;
+       int     irq;
+};
+
+#ifdef CONFIG_PCI
+void ath79_pci_set_irq_map(unsigned nr_irqs, const struct ath79_pci_irq *map);
+void ath79_pci_set_plat_dev_init(int (*func)(struct pci_dev *dev));
+int ath79_register_pci(void);
+#else
+static inline void
+ath79_pci_set_irq_map(unsigned nr_irqs, const struct ath79_pci_irq *map) {}
+static inline void
+ath79_pci_set_plat_dev_init(int (*func)(struct pci_dev *)) {}
+static inline int ath79_register_pci(void) { return 0; }
+#endif
+
+#endif /* _ATH79_PCI_H */
index 80a7d4023d7ffbd872ce76ed62bbe8b496d7e6bc..60d212ef86290c61d51ff5135e952506834caa2d 100644 (file)
@@ -1,10 +1,11 @@
 /*
  *  Atheros AR71XX/AR724X/AR913X specific setup
  *
+ *  Copyright (C) 2010-2011 Jaiganesh Narayanan <jnarayanan@atheros.com>
  *  Copyright (C) 2008-2011 Gabor Juhos <juhosg@openwrt.org>
  *  Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
  *
- *  Parts of this file are based on Atheros' 2.6.15 BSP
+ *  Parts of this file are based on Atheros' 2.6.15/2.6.31 BSP
  *
  *  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
@@ -116,18 +117,6 @@ static void __init ath79_detect_sys_type(void)
                rev = id & AR724X_REV_ID_REVISION_MASK;
                break;
 
-       case REV_ID_MAJOR_AR9330:
-               ath79_soc = ATH79_SOC_AR9330;
-               chip = "9330";
-               rev = id & AR933X_REV_ID_REVISION_MASK;
-               break;
-
-       case REV_ID_MAJOR_AR9331:
-               ath79_soc = ATH79_SOC_AR9331;
-               chip = "9331";
-               rev = id & AR933X_REV_ID_REVISION_MASK;
-               break;
-
        case REV_ID_MAJOR_AR913X:
                minor = id & AR913X_REV_ID_MINOR_MASK;
                rev = id >> AR913X_REV_ID_REVISION_SHIFT;
@@ -145,6 +134,36 @@ static void __init ath79_detect_sys_type(void)
                }
                break;
 
+       case REV_ID_MAJOR_AR9330:
+               ath79_soc = ATH79_SOC_AR9330;
+               chip = "9330";
+               rev = id & AR933X_REV_ID_REVISION_MASK;
+               break;
+
+       case REV_ID_MAJOR_AR9331:
+               ath79_soc = ATH79_SOC_AR9331;
+               chip = "9331";
+               rev = id & AR933X_REV_ID_REVISION_MASK;
+               break;
+
+       case REV_ID_MAJOR_AR9341:
+               ath79_soc = ATH79_SOC_AR9341;
+               chip = "9341";
+               rev = id & AR934X_REV_ID_REVISION_MASK;
+               break;
+
+       case REV_ID_MAJOR_AR9342:
+               ath79_soc = ATH79_SOC_AR9342;
+               chip = "9342";
+               rev = id & AR934X_REV_ID_REVISION_MASK;
+               break;
+
+       case REV_ID_MAJOR_AR9344:
+               ath79_soc = ATH79_SOC_AR9344;
+               chip = "9344";
+               rev = id & AR934X_REV_ID_REVISION_MASK;
+               break;
+
        default:
                panic("ath79: unknown SoC, id:0x%08x", id);
        }
index 9f64fb41407743358f55a36377617613fb9a0383..af07c1aa202fac322ec78e24664df14e1eee7d7a 100644 (file)
@@ -1,3 +1 @@
 obj-$(CONFIG_BOARD_BCM963XX)           += board_bcm963xx.o
-
-ccflags-y := -Werror
index d3a9f012aa0a57db9549d13a5d9ee26151218164..260dc247c052ca5874c23594f0db926a7fcfb1eb 100644 (file)
@@ -9,6 +9,7 @@
 #include <linux/init.h>
 #include <linux/console.h>
 #include <linux/delay.h>
+#include <linux/export.h>
 #include <linux/interrupt.h>
 #include <linux/io.h>
 #include <linux/serial.h>
index 97e7ce9b50ed4066d276ed98e47937bfd5236d42..4b93048044eb266457b4c9ac80a164c056e6eba9 100644 (file)
@@ -257,8 +257,6 @@ DEFINE_PER_CPU(int, cpu_state);
 
 extern void fixup_irqs(void);
 
-static DEFINE_SPINLOCK(smp_reserve_lock);
-
 static int octeon_cpu_disable(void)
 {
        unsigned int cpu = smp_processor_id();
@@ -266,8 +264,6 @@ static int octeon_cpu_disable(void)
        if (cpu == 0)
                return -EBUSY;
 
-       spin_lock(&smp_reserve_lock);
-
        set_cpu_online(cpu, false);
        cpu_clear(cpu, cpu_callin_map);
        local_irq_disable();
@@ -277,8 +273,6 @@ static int octeon_cpu_disable(void)
        flush_cache_all();
        local_flush_tlb_all();
 
-       spin_unlock(&smp_reserve_lock);
-
        return 0;
 }
 
index 5314b37aff2c493a736eb438a47f03d33cba509f..4f349ec1ea2da4bb200a1c296d16f27b54650935 100644 (file)
@@ -8,5 +8,3 @@ lib-y                           += cmdline.o env.o file.o identify.o init.o \
 lib-$(CONFIG_ARC_MEMORY)       += memory.o
 lib-$(CONFIG_ARC_CONSOLE)      += arc_con.o
 lib-$(CONFIG_ARC_PROMLIB)      += promlib.o
-
-ccflags-y                      := -Werror
diff --git a/arch/mips/include/asm/clkdev.h b/arch/mips/include/asm/clkdev.h
new file mode 100644 (file)
index 0000000..2624754
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ *  based on arch/arm/include/asm/clkdev.h
+ *
+ *  Copyright (C) 2008 Russell King.
+ *
+ * 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.
+ *
+ * Helper for the clk API to assist looking up a struct clk.
+ */
+#ifndef __ASM_CLKDEV_H
+#define __ASM_CLKDEV_H
+
+#include <linux/slab.h>
+
+#define __clk_get(clk) ({ 1; })
+#define __clk_put(clk) do { } while (0)
+
+static inline struct clk_lookup_alloc *__clkdev_alloc(size_t size)
+{
+       return kzalloc(size, GFP_KERNEL);
+}
+
+#endif
index 2f0becb4ec8f490283ce1c22e1af3ae44d05c0f7..1caa78ad06d5833306ee367cd44fbefe5dbd6fe3 100644 (file)
@@ -1,10 +1,11 @@
 /*
  *  Atheros AR71XX/AR724X/AR913X SoC register definitions
  *
+ *  Copyright (C) 2010-2011 Jaiganesh Narayanan <jnarayanan@atheros.com>
  *  Copyright (C) 2008-2010 Gabor Juhos <juhosg@openwrt.org>
  *  Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
  *
- *  Parts of this file are based on Atheros' 2.6.15 BSP
+ *  Parts of this file are based on Atheros' 2.6.15/2.6.31 BSP
  *
  *  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
@@ -60,6 +61,9 @@
 #define AR933X_EHCI_BASE       0x1b000000
 #define AR933X_EHCI_SIZE       0x1000
 
+#define AR934X_WMAC_BASE       (AR71XX_APB_BASE + 0x00100000)
+#define AR934X_WMAC_SIZE       0x20000
+
 /*
  * DDR_CTRL block
  */
 #define AR933X_DDR_REG_FLUSH_USB       0x84
 #define AR933X_DDR_REG_FLUSH_WMAC      0x88
 
+#define AR934X_DDR_REG_FLUSH_GE0       0x9c
+#define AR934X_DDR_REG_FLUSH_GE1       0xa0
+#define AR934X_DDR_REG_FLUSH_USB       0xa4
+#define AR934X_DDR_REG_FLUSH_PCIE      0xa8
+#define AR934X_DDR_REG_FLUSH_WMAC      0xac
+
 /*
  * PLL block
  */
 #define AR933X_PLL_CLOCK_CTRL_AHB_DIV_SHIFT    15
 #define AR933X_PLL_CLOCK_CTRL_AHB_DIV_MASK     0x7
 
+#define AR934X_PLL_CPU_CONFIG_REG              0x00
+#define AR934X_PLL_DDR_CONFIG_REG              0x04
+#define AR934X_PLL_CPU_DDR_CLK_CTRL_REG                0x08
+
+#define AR934X_PLL_CPU_CONFIG_NFRAC_SHIFT      0
+#define AR934X_PLL_CPU_CONFIG_NFRAC_MASK       0x3f
+#define AR934X_PLL_CPU_CONFIG_NINT_SHIFT       6
+#define AR934X_PLL_CPU_CONFIG_NINT_MASK                0x3f
+#define AR934X_PLL_CPU_CONFIG_REFDIV_SHIFT     12
+#define AR934X_PLL_CPU_CONFIG_REFDIV_MASK      0x1f
+#define AR934X_PLL_CPU_CONFIG_OUTDIV_SHIFT     19
+#define AR934X_PLL_CPU_CONFIG_OUTDIV_MASK      0x3
+
+#define AR934X_PLL_DDR_CONFIG_NFRAC_SHIFT      0
+#define AR934X_PLL_DDR_CONFIG_NFRAC_MASK       0x3ff
+#define AR934X_PLL_DDR_CONFIG_NINT_SHIFT       10
+#define AR934X_PLL_DDR_CONFIG_NINT_MASK                0x3f
+#define AR934X_PLL_DDR_CONFIG_REFDIV_SHIFT     16
+#define AR934X_PLL_DDR_CONFIG_REFDIV_MASK      0x1f
+#define AR934X_PLL_DDR_CONFIG_OUTDIV_SHIFT     23
+#define AR934X_PLL_DDR_CONFIG_OUTDIV_MASK      0x7
+
+#define AR934X_PLL_CPU_DDR_CLK_CTRL_CPU_PLL_BYPASS     BIT(2)
+#define AR934X_PLL_CPU_DDR_CLK_CTRL_DDR_PLL_BYPASS     BIT(3)
+#define AR934X_PLL_CPU_DDR_CLK_CTRL_AHB_PLL_BYPASS     BIT(4)
+#define AR934X_PLL_CPU_DDR_CLK_CTRL_CPU_POST_DIV_SHIFT 5
+#define AR934X_PLL_CPU_DDR_CLK_CTRL_CPU_POST_DIV_MASK  0x1f
+#define AR934X_PLL_CPU_DDR_CLK_CTRL_DDR_POST_DIV_SHIFT 10
+#define AR934X_PLL_CPU_DDR_CLK_CTRL_DDR_POST_DIV_MASK  0x1f
+#define AR934X_PLL_CPU_DDR_CLK_CTRL_AHB_POST_DIV_SHIFT 15
+#define AR934X_PLL_CPU_DDR_CLK_CTRL_AHB_POST_DIV_MASK  0x1f
+#define AR934X_PLL_CPU_DDR_CLK_CTRL_CPUCLK_FROM_CPUPLL BIT(20)
+#define AR934X_PLL_CPU_DDR_CLK_CTRL_DDRCLK_FROM_DDRPLL BIT(21)
+#define AR934X_PLL_CPU_DDR_CLK_CTRL_AHBCLK_FROM_DDRPLL BIT(24)
+
 /*
  * USB_CONFIG block
  */
 #define AR933X_RESET_REG_RESET_MODULE          0x1c
 #define AR933X_RESET_REG_BOOTSTRAP             0xac
 
+#define AR934X_RESET_REG_RESET_MODULE          0x1c
+#define AR934X_RESET_REG_BOOTSTRAP             0xb0
+#define AR934X_RESET_REG_PCIE_WMAC_INT_STATUS  0xac
+
 #define MISC_INT_ETHSW                 BIT(12)
 #define MISC_INT_TIMER4                        BIT(10)
 #define MISC_INT_TIMER3                        BIT(9)
 
 #define AR933X_BOOTSTRAP_REF_CLK_40    BIT(0)
 
+#define AR934X_BOOTSTRAP_SW_OPTION8    BIT(23)
+#define AR934X_BOOTSTRAP_SW_OPTION7    BIT(22)
+#define AR934X_BOOTSTRAP_SW_OPTION6    BIT(21)
+#define AR934X_BOOTSTRAP_SW_OPTION5    BIT(20)
+#define AR934X_BOOTSTRAP_SW_OPTION4    BIT(19)
+#define AR934X_BOOTSTRAP_SW_OPTION3    BIT(18)
+#define AR934X_BOOTSTRAP_SW_OPTION2    BIT(17)
+#define AR934X_BOOTSTRAP_SW_OPTION1    BIT(16)
+#define AR934X_BOOTSTRAP_USB_MODE_DEVICE BIT(7)
+#define AR934X_BOOTSTRAP_PCIE_RC       BIT(6)
+#define AR934X_BOOTSTRAP_EJTAG_MODE    BIT(5)
+#define AR934X_BOOTSTRAP_REF_CLK_40    BIT(4)
+#define AR934X_BOOTSTRAP_BOOT_FROM_SPI BIT(2)
+#define AR934X_BOOTSTRAP_SDRAM_DISABLED        BIT(1)
+#define AR934X_BOOTSTRAP_DDR1          BIT(0)
+
+#define AR934X_PCIE_WMAC_INT_WMAC_MISC         BIT(0)
+#define AR934X_PCIE_WMAC_INT_WMAC_TX           BIT(1)
+#define AR934X_PCIE_WMAC_INT_WMAC_RXLP         BIT(2)
+#define AR934X_PCIE_WMAC_INT_WMAC_RXHP         BIT(3)
+#define AR934X_PCIE_WMAC_INT_PCIE_RC           BIT(4)
+#define AR934X_PCIE_WMAC_INT_PCIE_RC0          BIT(5)
+#define AR934X_PCIE_WMAC_INT_PCIE_RC1          BIT(6)
+#define AR934X_PCIE_WMAC_INT_PCIE_RC2          BIT(7)
+#define AR934X_PCIE_WMAC_INT_PCIE_RC3          BIT(8)
+#define AR934X_PCIE_WMAC_INT_WMAC_ALL \
+       (AR934X_PCIE_WMAC_INT_WMAC_MISC | AR934X_PCIE_WMAC_INT_WMAC_TX | \
+        AR934X_PCIE_WMAC_INT_WMAC_RXLP | AR934X_PCIE_WMAC_INT_WMAC_RXHP)
+
+#define AR934X_PCIE_WMAC_INT_PCIE_ALL \
+       (AR934X_PCIE_WMAC_INT_PCIE_RC | AR934X_PCIE_WMAC_INT_PCIE_RC0 | \
+        AR934X_PCIE_WMAC_INT_PCIE_RC1 | AR934X_PCIE_WMAC_INT_PCIE_RC2 | \
+        AR934X_PCIE_WMAC_INT_PCIE_RC3)
+
 #define REV_ID_MAJOR_MASK              0xfff0
 #define REV_ID_MAJOR_AR71XX            0x00a0
 #define REV_ID_MAJOR_AR913X            0x00b0
 #define REV_ID_MAJOR_AR7242            0x1100
 #define REV_ID_MAJOR_AR9330            0x0110
 #define REV_ID_MAJOR_AR9331            0x1110
+#define REV_ID_MAJOR_AR9341            0x0120
+#define REV_ID_MAJOR_AR9342            0x1120
+#define REV_ID_MAJOR_AR9344            0x2120
 
 #define AR71XX_REV_ID_MINOR_MASK       0x3
 #define AR71XX_REV_ID_MINOR_AR7130     0x0
 
 #define AR724X_REV_ID_REVISION_MASK    0x3
 
+#define AR934X_REV_ID_REVISION_MASK     0xf
+
 /*
  * SPI block
  */
 #define AR724X_GPIO_COUNT              18
 #define AR913X_GPIO_COUNT              22
 #define AR933X_GPIO_COUNT              30
+#define AR934X_GPIO_COUNT              23
 
 #endif /* __ASM_MACH_AR71XX_REGS_H */
index 6d0c6c9d5622fbe7890add97b200e4e6ced76d02..4f248c3d7b237100e41a1bad3489d090ff8cbb29 100644 (file)
@@ -29,6 +29,9 @@ enum ath79_soc_type {
        ATH79_SOC_AR9132,
        ATH79_SOC_AR9330,
        ATH79_SOC_AR9331,
+       ATH79_SOC_AR9341,
+       ATH79_SOC_AR9342,
+       ATH79_SOC_AR9344,
 };
 
 extern enum ath79_soc_type ath79_soc;
@@ -75,6 +78,26 @@ static inline int soc_is_ar933x(void)
                ath79_soc == ATH79_SOC_AR9331);
 }
 
+static inline int soc_is_ar9341(void)
+{
+       return (ath79_soc == ATH79_SOC_AR9341);
+}
+
+static inline int soc_is_ar9342(void)
+{
+       return (ath79_soc == ATH79_SOC_AR9342);
+}
+
+static inline int soc_is_ar9344(void)
+{
+       return (ath79_soc == ATH79_SOC_AR9344);
+}
+
+static inline int soc_is_ar934x(void)
+{
+       return soc_is_ar9341() || soc_is_ar9342() || soc_is_ar9344();
+}
+
 extern void __iomem *ath79_ddr_base;
 extern void __iomem *ath79_pll_base;
 extern void __iomem *ath79_reset_base;
index 519958fe4e3c7d0532c6b639148e9aa2b9522e74..0968f69e2018527e6f35b40b7aa7f7be333323d8 100644 (file)
 #define __ASM_MACH_ATH79_IRQ_H
 
 #define MIPS_CPU_IRQ_BASE      0
-#define NR_IRQS                        40
+#define NR_IRQS                        48
 
 #define ATH79_MISC_IRQ_BASE    8
 #define ATH79_MISC_IRQ_COUNT   32
 
+#define ATH79_PCI_IRQ_BASE     (ATH79_MISC_IRQ_BASE + ATH79_MISC_IRQ_COUNT)
+#define ATH79_PCI_IRQ_COUNT    6
+#define ATH79_PCI_IRQ(_x)      (ATH79_PCI_IRQ_BASE + (_x))
+
+#define ATH79_IP2_IRQ_BASE     (ATH79_PCI_IRQ_BASE + ATH79_PCI_IRQ_COUNT)
+#define ATH79_IP2_IRQ_COUNT    2
+#define ATH79_IP2_IRQ(_x)      (ATH79_IP2_IRQ_BASE + (_x))
+
 #define ATH79_CPU_IRQ_IP2      (MIPS_CPU_IRQ_BASE + 2)
 #define ATH79_CPU_IRQ_USB      (MIPS_CPU_IRQ_BASE + 3)
 #define ATH79_CPU_IRQ_GE0      (MIPS_CPU_IRQ_BASE + 4)
diff --git a/arch/mips/include/asm/mach-ath79/pci-ath724x.h b/arch/mips/include/asm/mach-ath79/pci-ath724x.h
deleted file mode 100644 (file)
index 454885f..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- *  Atheros 724x PCI support
- *
- *  Copyright (C) 2011 René Bolldorf <xsecute@googlemail.com>
- *
- *  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 __ASM_MACH_ATH79_PCI_ATH724X_H
-#define __ASM_MACH_ATH79_PCI_ATH724X_H
-
-struct ath724x_pci_data {
-       int irq;
-       void *pdata;
-};
-
-void ath724x_pci_add_data(struct ath724x_pci_data *data, int size);
-
-#endif /* __ASM_MACH_ATH79_PCI_ATH724X_H */
diff --git a/arch/mips/include/asm/mach-ath79/pci.h b/arch/mips/include/asm/mach-ath79/pci.h
new file mode 100644 (file)
index 0000000..7868f7f
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ *  Atheros AR71XX/AR724X PCI support
+ *
+ *  Copyright (C) 2011 René Bolldorf <xsecute@googlemail.com>
+ *  Copyright (C) 2008-2011 Gabor Juhos <juhosg@openwrt.org>
+ *  Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
+ *
+ *  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 __ASM_MACH_ATH79_PCI_H
+#define __ASM_MACH_ATH79_PCI_H
+
+#if defined(CONFIG_PCI) && defined(CONFIG_SOC_AR71XX)
+int ar71xx_pcibios_init(void);
+#else
+static inline int ar71xx_pcibios_init(void) { return 0; }
+#endif
+
+#if defined(CONFIG_PCI_AR724X)
+int ar724x_pcibios_init(int irq);
+#else
+static inline int ar724x_pcibios_init(int irq) { return 0; }
+#endif
+
+#endif /* __ASM_MACH_ATH79_PCI_H */
index 3d5de96d40369523940662c3400302ef9a0e48fb..1d7dd96aa460b5d150f4d15660993a1170a4f3e8 100644 (file)
@@ -2,6 +2,7 @@
 #define BCM63XX_GPIO_H
 
 #include <linux/init.h>
+#include <bcm63xx_cpu.h>
 
 int __init bcm63xx_gpio_init(void);
 
diff --git a/arch/mips/include/asm/mach-lantiq/falcon/falcon_irq.h b/arch/mips/include/asm/mach-lantiq/falcon/falcon_irq.h
new file mode 100644 (file)
index 0000000..318f982
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ *  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.
+ *
+ *  Copyright (C) 2010 Thomas Langer <thomas.langer@lantiq.com>
+ */
+
+#ifndef _FALCON_IRQ__
+#define _FALCON_IRQ__
+
+#define INT_NUM_IRQ0                   8
+#define INT_NUM_IM0_IRL0               (INT_NUM_IRQ0 + 0)
+#define INT_NUM_IM1_IRL0               (INT_NUM_IM0_IRL0 + 32)
+#define INT_NUM_IM2_IRL0               (INT_NUM_IM1_IRL0 + 32)
+#define INT_NUM_IM3_IRL0               (INT_NUM_IM2_IRL0 + 32)
+#define INT_NUM_IM4_IRL0               (INT_NUM_IM3_IRL0 + 32)
+#define INT_NUM_EXTRA_START            (INT_NUM_IM4_IRL0 + 32)
+#define INT_NUM_IM_OFFSET              (INT_NUM_IM1_IRL0 - INT_NUM_IM0_IRL0)
+
+#define MIPS_CPU_TIMER_IRQ                     7
+
+#endif /* _FALCON_IRQ__ */
diff --git a/arch/mips/include/asm/mach-lantiq/falcon/irq.h b/arch/mips/include/asm/mach-lantiq/falcon/irq.h
new file mode 100644 (file)
index 0000000..2caccd9
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ *  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.
+ *
+ *  Copyright (C) 2011 Thomas Langer <thomas.langer@lantiq.com>
+ */
+
+#ifndef __FALCON_IRQ_H
+#define __FALCON_IRQ_H
+
+#include <falcon_irq.h>
+
+#define NR_IRQS 328
+
+#include_next <irq.h>
+
+#endif
diff --git a/arch/mips/include/asm/mach-lantiq/falcon/lantiq_soc.h b/arch/mips/include/asm/mach-lantiq/falcon/lantiq_soc.h
new file mode 100644 (file)
index 0000000..b385252
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ *
+ * Copyright (C) 2010 John Crispin <blogic@openwrt.org>
+ */
+
+#ifndef _LTQ_FALCON_H__
+#define _LTQ_FALCON_H__
+
+#ifdef CONFIG_SOC_FALCON
+
+#include <linux/pinctrl/pinctrl.h>
+#include <lantiq.h>
+
+/* Chip IDs */
+#define SOC_ID_FALCON          0x01B8
+
+/* SoC Types */
+#define SOC_TYPE_FALCON                0x01
+
+/*
+ * during early_printk no ioremap possible at this early stage
+ * lets use KSEG1 instead
+ */
+#define LTQ_ASC0_BASE_ADDR     0x1E100C00
+#define LTQ_EARLY_ASC          KSEG1ADDR(LTQ_ASC0_BASE_ADDR)
+
+/* WDT */
+#define LTQ_RST_CAUSE_WDTRST   0x0002
+
+/* CHIP ID */
+#define LTQ_STATUS_BASE_ADDR   0x1E802000
+
+#define FALCON_CHIPID          ((u32 *)(KSEG1 + LTQ_STATUS_BASE_ADDR + 0x0c))
+#define FALCON_CHIPTYPE                ((u32 *)(KSEG1 + LTQ_STATUS_BASE_ADDR + 0x38))
+#define FALCON_CHIPCONF                ((u32 *)(KSEG1 + LTQ_STATUS_BASE_ADDR + 0x40))
+
+/* SYSCTL - start/stop/restart/configure/... different parts of the Soc */
+#define SYSCTL_SYS1            0
+#define SYSCTL_SYSETH          1
+#define SYSCTL_SYSGPE          2
+
+/* BOOT_SEL - find what boot media we have */
+#define BS_FLASH               0x1
+#define BS_SPI                  0x4
+
+/* global register ranges */
+extern __iomem void *ltq_ebu_membase;
+extern __iomem void *ltq_sys1_membase;
+#define ltq_ebu_w32(x, y)      ltq_w32((x), ltq_ebu_membase + (y))
+#define ltq_ebu_r32(x)         ltq_r32(ltq_ebu_membase + (x))
+
+#define ltq_sys1_w32(x, y)     ltq_w32((x), ltq_sys1_membase + (y))
+#define ltq_sys1_r32(x)                ltq_r32(ltq_sys1_membase + (x))
+#define ltq_sys1_w32_mask(clear, set, reg)   \
+       ltq_sys1_w32((ltq_sys1_r32(reg) & ~(clear)) | (set), reg)
+
+/*
+ * to keep the irq code generic we need to define this to 0 as falcon
+ * has no EIU/EBU
+ */
+#define LTQ_EBU_PCC_ISTAT      0
+
+#endif /* CONFIG_SOC_FALCON */
+#endif /* _LTQ_XWAY_H__ */
diff --git a/arch/mips/include/asm/mach-lantiq/gpio.h b/arch/mips/include/asm/mach-lantiq/gpio.h
new file mode 100644 (file)
index 0000000..f79505b
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef __ASM_MIPS_MACH_LANTIQ_GPIO_H
+#define __ASM_MIPS_MACH_LANTIQ_GPIO_H
+
+static inline int gpio_to_irq(unsigned int gpio)
+{
+       return -1;
+}
+
+#define gpio_get_value __gpio_get_value
+#define gpio_set_value __gpio_set_value
+
+#define gpio_cansleep __gpio_cansleep
+
+#include <asm-generic/gpio.h>
+
+#endif
index ce2f02929d22f284cb99b554952d382e70b18a92..5e8a6e9657567c0d5ef2d5af9d10507bf0bcaf80 100644 (file)
@@ -9,6 +9,8 @@
 #define _LANTIQ_H__
 
 #include <linux/irq.h>
+#include <linux/device.h>
+#include <linux/clk.h>
 
 /* generic reg access functions */
 #define ltq_r32(reg)           __raw_readl(reg)
 /* register access macros for EBU and CGU */
 #define ltq_ebu_w32(x, y)      ltq_w32((x), ltq_ebu_membase + (y))
 #define ltq_ebu_r32(x)         ltq_r32(ltq_ebu_membase + (x))
-#define ltq_cgu_w32(x, y)      ltq_w32((x), ltq_cgu_membase + (y))
-#define ltq_cgu_r32(x)         ltq_r32(ltq_cgu_membase + (x))
-
+#define ltq_ebu_w32_mask(x, y, z) \
+       ltq_w32_mask(x, y, ltq_ebu_membase + (z))
 extern __iomem void *ltq_ebu_membase;
-extern __iomem void *ltq_cgu_membase;
-
-extern unsigned int ltq_get_cpu_ver(void);
-extern unsigned int ltq_get_soc_type(void);
-
-/* clock speeds */
-#define CLOCK_60M      60000000
-#define CLOCK_83M      83333333
-#define CLOCK_111M     111111111
-#define CLOCK_133M     133333333
-#define CLOCK_167M     166666667
-#define CLOCK_200M     200000000
-#define CLOCK_266M     266666666
-#define CLOCK_333M     333333333
-#define CLOCK_400M     400000000
 
 /* spinlock all ebu i/o */
 extern spinlock_t ebu_lock;
@@ -49,15 +35,21 @@ extern void ltq_disable_irq(struct irq_data *data);
 extern void ltq_mask_and_ack_irq(struct irq_data *data);
 extern void ltq_enable_irq(struct irq_data *data);
 
+/* clock handling */
+extern int clk_activate(struct clk *clk);
+extern void clk_deactivate(struct clk *clk);
+extern struct clk *clk_get_cpu(void);
+extern struct clk *clk_get_fpi(void);
+extern struct clk *clk_get_io(void);
+
+/* find out what bootsource we have */
+extern unsigned char ltq_boot_select(void);
 /* find out what caused the last cpu reset */
 extern int ltq_reset_cause(void);
-#define LTQ_RST_CAUSE_WDTRST   0x20
 
 #define IOPORT_RESOURCE_START  0x10000000
 #define IOPORT_RESOURCE_END    0xffffffff
 #define IOMEM_RESOURCE_START   0x10000000
 #define IOMEM_RESOURCE_END     0xffffffff
-#define LTQ_FLASH_START                0x10000000
-#define LTQ_FLASH_MAX          0x04000000
 
 #endif
index a305f1d0259e76e282c9787fa1042799c0d40f7f..e23bf7c9a2d0782fa4827ab2297de1a033eb6fdd 100644 (file)
@@ -9,41 +9,8 @@
 #ifndef _LANTIQ_PLATFORM_H__
 #define _LANTIQ_PLATFORM_H__
 
-#include <linux/mtd/partitions.h>
 #include <linux/socket.h>
 
-/* struct used to pass info to the pci core */
-enum {
-       PCI_CLOCK_INT = 0,
-       PCI_CLOCK_EXT
-};
-
-#define PCI_EXIN0      0x0001
-#define PCI_EXIN1      0x0002
-#define PCI_EXIN2      0x0004
-#define PCI_EXIN3      0x0008
-#define PCI_EXIN4      0x0010
-#define PCI_EXIN5      0x0020
-#define PCI_EXIN_MAX   6
-
-#define PCI_GNT1       0x0040
-#define PCI_GNT2       0x0080
-#define PCI_GNT3       0x0100
-#define PCI_GNT4       0x0200
-
-#define PCI_REQ1       0x0400
-#define PCI_REQ2       0x0800
-#define PCI_REQ3       0x1000
-#define PCI_REQ4       0x2000
-#define PCI_REQ_SHIFT  10
-#define PCI_REQ_MASK   0xf
-
-struct ltq_pci_data {
-       int clock;
-       int gpio;
-       int irq[16];
-};
-
 /* struct used to pass info to network drivers */
 struct ltq_eth_data {
        struct sockaddr mac;
index b4465a888e20eecab1558e01d3cf1eb9cb997b0a..aa0b3b866f8467bbb2d1b3f6b943ccb65d97621e 100644 (file)
 #define INT_NUM_IM4_IRL0       (INT_NUM_IRQ0 + 128)
 #define INT_NUM_IM_OFFSET      (INT_NUM_IM1_IRL0 - INT_NUM_IM0_IRL0)
 
-#define LTQ_ASC_TIR(x)         (INT_NUM_IM3_IRL0 + (x * 8))
-#define LTQ_ASC_RIR(x)         (INT_NUM_IM3_IRL0 + (x * 8) + 1)
-#define LTQ_ASC_EIR(x)         (INT_NUM_IM3_IRL0 + (x * 8) + 2)
-
-#define LTQ_ASC_ASE_TIR                INT_NUM_IM2_IRL0
-#define LTQ_ASC_ASE_RIR                (INT_NUM_IM2_IRL0 + 2)
-#define LTQ_ASC_ASE_EIR                (INT_NUM_IM2_IRL0 + 3)
-
-#define LTQ_SSC_TIR            (INT_NUM_IM0_IRL0 + 15)
-#define LTQ_SSC_RIR            (INT_NUM_IM0_IRL0 + 14)
-#define LTQ_SSC_EIR            (INT_NUM_IM0_IRL0 + 16)
-
-#define LTQ_MEI_DYING_GASP_INT (INT_NUM_IM1_IRL0 + 21)
-#define LTQ_MEI_INT            (INT_NUM_IM1_IRL0 + 23)
-
-#define LTQ_TIMER6_INT         (INT_NUM_IM1_IRL0 + 23)
-#define LTQ_USB_INT            (INT_NUM_IM1_IRL0 + 22)
-#define LTQ_USB_OC_INT         (INT_NUM_IM4_IRL0 + 23)
-
-#define MIPS_CPU_TIMER_IRQ             7
-
 #define LTQ_DMA_CH0_INT                (INT_NUM_IM2_IRL0)
-#define LTQ_DMA_CH1_INT                (INT_NUM_IM2_IRL0 + 1)
-#define LTQ_DMA_CH2_INT                (INT_NUM_IM2_IRL0 + 2)
-#define LTQ_DMA_CH3_INT                (INT_NUM_IM2_IRL0 + 3)
-#define LTQ_DMA_CH4_INT                (INT_NUM_IM2_IRL0 + 4)
-#define LTQ_DMA_CH5_INT                (INT_NUM_IM2_IRL0 + 5)
-#define LTQ_DMA_CH6_INT                (INT_NUM_IM2_IRL0 + 6)
-#define LTQ_DMA_CH7_INT                (INT_NUM_IM2_IRL0 + 7)
-#define LTQ_DMA_CH8_INT                (INT_NUM_IM2_IRL0 + 8)
-#define LTQ_DMA_CH9_INT                (INT_NUM_IM2_IRL0 + 9)
-#define LTQ_DMA_CH10_INT       (INT_NUM_IM2_IRL0 + 10)
-#define LTQ_DMA_CH11_INT       (INT_NUM_IM2_IRL0 + 11)
-#define LTQ_DMA_CH12_INT       (INT_NUM_IM2_IRL0 + 25)
-#define LTQ_DMA_CH13_INT       (INT_NUM_IM2_IRL0 + 26)
-#define LTQ_DMA_CH14_INT       (INT_NUM_IM2_IRL0 + 27)
-#define LTQ_DMA_CH15_INT       (INT_NUM_IM2_IRL0 + 28)
-#define LTQ_DMA_CH16_INT       (INT_NUM_IM2_IRL0 + 29)
-#define LTQ_DMA_CH17_INT       (INT_NUM_IM2_IRL0 + 30)
-#define LTQ_DMA_CH18_INT       (INT_NUM_IM2_IRL0 + 16)
-#define LTQ_DMA_CH19_INT       (INT_NUM_IM2_IRL0 + 21)
-
-#define LTQ_PPE_MBOX_INT       (INT_NUM_IM2_IRL0 + 24)
 
-#define INT_NUM_IM4_IRL14      (INT_NUM_IM4_IRL0 + 14)
+#define MIPS_CPU_TIMER_IRQ     7
 
 #endif
index 8a3c6be669d2136ad8d8d556e748274ae58ebeb2..6a2df709c576956a4c58541d2fad5c2f9c726917 100644 (file)
 #define SOC_ID_DANUBE1         0x129
 #define SOC_ID_DANUBE2         0x12B
 #define SOC_ID_TWINPASS                0x12D
-#define SOC_ID_AMAZON_SE       0x152
+#define SOC_ID_AMAZON_SE_1     0x152 /* 50601 */
+#define SOC_ID_AMAZON_SE_2     0x153 /* 50600 */
 #define SOC_ID_ARX188          0x16C
-#define SOC_ID_ARX168          0x16D
+#define SOC_ID_ARX168_1                0x16D
+#define SOC_ID_ARX168_2                0x16E
 #define SOC_ID_ARX182          0x16F
-
-/* SoC Types */
+#define SOC_ID_GRX188          0x170
+#define SOC_ID_GRX168          0x171
+
+#define SOC_ID_VRX288          0x1C0 /* v1.1 */
+#define SOC_ID_VRX282          0x1C1 /* v1.1 */
+#define SOC_ID_VRX268          0x1C2 /* v1.1 */
+#define SOC_ID_GRX268          0x1C8 /* v1.1 */
+#define SOC_ID_GRX288          0x1C9 /* v1.1 */
+#define SOC_ID_VRX288_2                0x00B /* v1.2 */
+#define SOC_ID_VRX268_2                0x00C /* v1.2 */
+#define SOC_ID_GRX288_2                0x00D /* v1.2 */
+#define SOC_ID_GRX282_2                0x00E /* v1.2 */
+
+ /* SoC Types */
 #define SOC_TYPE_DANUBE                0x01
 #define SOC_TYPE_TWINPASS      0x02
 #define SOC_TYPE_AR9           0x03
-#define SOC_TYPE_VR9           0x04
-#define SOC_TYPE_AMAZON_SE     0x05
+#define SOC_TYPE_VR9           0x04 /* v1.1 */
+#define SOC_TYPE_VR9_2         0x05 /* v1.2 */
+#define SOC_TYPE_AMAZON_SE     0x06
+
+/* BOOT_SEL - find what boot media we have */
+#define BS_EXT_ROM             0x0
+#define BS_FLASH               0x1
+#define BS_MII0                        0x2
+#define BS_PCI                 0x3
+#define BS_UART1               0x4
+#define BS_SPI                 0x5
+#define BS_NAND                        0x6
+#define BS_RMII0               0x7
+
+/* helpers used to access the cgu */
+#define ltq_cgu_w32(x, y)      ltq_w32((x), ltq_cgu_membase + (y))
+#define ltq_cgu_r32(x)         ltq_r32(ltq_cgu_membase + (x))
+extern __iomem void *ltq_cgu_membase;
 
-/* ASC0/1 - serial port */
-#define LTQ_ASC0_BASE_ADDR     0x1E100400
+/*
+ * during early_printk no ioremap is possible
+ * lets use KSEG1 instead
+ */
 #define LTQ_ASC1_BASE_ADDR     0x1E100C00
-#define LTQ_ASC_SIZE           0x400
-
-/* RCU - reset control unit */
-#define LTQ_RCU_BASE_ADDR      0x1F203000
-#define LTQ_RCU_SIZE           0x1000
-
-/* GPTU - general purpose timer unit */
-#define LTQ_GPTU_BASE_ADDR     0x18000300
-#define LTQ_GPTU_SIZE          0x100
+#define LTQ_EARLY_ASC          KSEG1ADDR(LTQ_ASC1_BASE_ADDR)
 
 /* EBU - external bus unit */
-#define LTQ_EBU_GPIO_START     0x14000000
-#define LTQ_EBU_GPIO_SIZE      0x1000
-
-#define LTQ_EBU_BASE_ADDR      0x1E105300
-#define LTQ_EBU_SIZE           0x100
-
 #define LTQ_EBU_BUSCON0                0x0060
 #define LTQ_EBU_PCC_CON                0x0090
 #define LTQ_EBU_PCC_IEN                0x00A4
 #define LTQ_EBU_ADDRSEL1       0x0024
 #define EBU_WRDIS              0x80000000
 
-/* CGU - clock generation unit */
-#define LTQ_CGU_BASE_ADDR      0x1F103000
-#define LTQ_CGU_SIZE           0x1000
-
-/* ICU - interrupt control unit */
-#define LTQ_ICU_BASE_ADDR      0x1F880200
-#define LTQ_ICU_SIZE           0x100
-
-/* EIU - external interrupt unit */
-#define LTQ_EIU_BASE_ADDR      0x1F101000
-#define LTQ_EIU_SIZE           0x1000
-
-/* PMU - power management unit */
-#define LTQ_PMU_BASE_ADDR      0x1F102000
-#define LTQ_PMU_SIZE           0x1000
-
-#define PMU_DMA                        0x0020
-#define PMU_USB                        0x8041
-#define PMU_LED                        0x0800
-#define PMU_GPT                        0x1000
-#define PMU_PPE                        0x2000
-#define PMU_FPI                        0x4000
-#define PMU_SWITCH             0x10000000
-
-/* ETOP - ethernet */
-#define LTQ_ETOP_BASE_ADDR     0x1E180000
-#define LTQ_ETOP_SIZE          0x40000
-
-/* DMA */
-#define LTQ_DMA_BASE_ADDR      0x1E104100
-#define LTQ_DMA_SIZE           0x800
-
-/* PCI */
-#define PCI_CR_BASE_ADDR       0x1E105400
-#define PCI_CR_SIZE            0x400
-
 /* WDT */
-#define LTQ_WDT_BASE_ADDR      0x1F8803F0
-#define LTQ_WDT_SIZE           0x10
-
-/* STP - serial to parallel conversion unit */
-#define LTQ_STP_BASE_ADDR      0x1E100BB0
-#define LTQ_STP_SIZE           0x40
-
-/* GPIO */
-#define LTQ_GPIO0_BASE_ADDR    0x1E100B10
-#define LTQ_GPIO1_BASE_ADDR    0x1E100B40
-#define LTQ_GPIO2_BASE_ADDR    0x1E100B70
-#define LTQ_GPIO_SIZE          0x30
-
-/* SSC */
-#define LTQ_SSC_BASE_ADDR      0x1e100800
-#define LTQ_SSC_SIZE           0x100
-
-/* MEI - dsl core */
-#define LTQ_MEI_BASE_ADDR      0x1E116000
-
-/* DEU - data encryption unit */
-#define LTQ_DEU_BASE_ADDR      0x1E103100
+#define LTQ_RST_CAUSE_WDTRST   0x20
 
 /* MPS - multi processor unit (voice) */
 #define LTQ_MPS_BASE_ADDR      (KSEG1 + 0x1F107000)
 #define LTQ_MPS_CHIPID         ((u32 *)(LTQ_MPS_BASE_ADDR + 0x0344))
 
 /* request a non-gpio and set the PIO config */
-extern int  ltq_gpio_request(unsigned int pin, unsigned int alt0,
-       unsigned int alt1, unsigned int dir, const char *name);
+#define PMU_PPE                         BIT(13)
 extern void ltq_pmu_enable(unsigned int module);
 extern void ltq_pmu_disable(unsigned int module);
 
-static inline int ltq_is_ar9(void)
-{
-       return (ltq_get_soc_type() == SOC_TYPE_AR9);
-}
-
-static inline int ltq_is_vr9(void)
-{
-       return (ltq_get_soc_type() == SOC_TYPE_VR9);
-}
-
 #endif /* CONFIG_SOC_TYPE_XWAY */
 #endif /* _LTQ_XWAY_H__ */
index 46c08563e5328419540341ab176831ea4e991b46..6e23ceb0ba8caa2c7e632e15125aa1e596ca84ae 100644 (file)
@@ -93,8 +93,4 @@ extern void mips_pcibios_init(void);
 #define mips_pcibios_init() do { } while (0)
 #endif
 
-#ifdef CONFIG_KGDB
-extern void kgdb_config(void);
-#endif
-
 #endif  /* __ASM_MIPS_BOARDS_GENERIC_H */
index 7467d1d933d54a0eba29fe93465b78c26ade6108..530008048c6227fb653ccb8b66b0319c442ce8ea 100644 (file)
@@ -2,6 +2,7 @@
 #define _ASM_MODULE_H
 
 #include <linux/list.h>
+#include <linux/elf.h>
 #include <asm/uaccess.h>
 
 struct mod_arch_specific {
diff --git a/arch/mips/include/asm/octeon/cvmx-pcieep-defs.h b/arch/mips/include/asm/octeon/cvmx-pcieep-defs.h
deleted file mode 100644 (file)
index d553f8e..0000000
+++ /dev/null
@@ -1,1365 +0,0 @@
-/***********************license start***************
- * Author: Cavium Networks
- *
- * Contact: support@caviumnetworks.com
- * This file is part of the OCTEON SDK
- *
- * Copyright (c) 2003-2008 Cavium Networks
- *
- * This file 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 file is distributed in the hope that it will be useful, but
- * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
- * NONINFRINGEMENT.  See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this file; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- * or visit http://www.gnu.org/licenses/.
- *
- * This file may also be available under a different license from Cavium.
- * Contact Cavium Networks for more information
- ***********************license end**************************************/
-
-#ifndef __CVMX_PCIEEP_DEFS_H__
-#define __CVMX_PCIEEP_DEFS_H__
-
-#define CVMX_PCIEEP_CFG000 \
-        (0x0000000000000000ull)
-#define CVMX_PCIEEP_CFG001 \
-        (0x0000000000000004ull)
-#define CVMX_PCIEEP_CFG002 \
-        (0x0000000000000008ull)
-#define CVMX_PCIEEP_CFG003 \
-        (0x000000000000000Cull)
-#define CVMX_PCIEEP_CFG004 \
-        (0x0000000000000010ull)
-#define CVMX_PCIEEP_CFG004_MASK \
-        (0x0000000080000010ull)
-#define CVMX_PCIEEP_CFG005 \
-        (0x0000000000000014ull)
-#define CVMX_PCIEEP_CFG005_MASK \
-        (0x0000000080000014ull)
-#define CVMX_PCIEEP_CFG006 \
-        (0x0000000000000018ull)
-#define CVMX_PCIEEP_CFG006_MASK \
-        (0x0000000080000018ull)
-#define CVMX_PCIEEP_CFG007 \
-        (0x000000000000001Cull)
-#define CVMX_PCIEEP_CFG007_MASK \
-        (0x000000008000001Cull)
-#define CVMX_PCIEEP_CFG008 \
-        (0x0000000000000020ull)
-#define CVMX_PCIEEP_CFG008_MASK \
-        (0x0000000080000020ull)
-#define CVMX_PCIEEP_CFG009 \
-        (0x0000000000000024ull)
-#define CVMX_PCIEEP_CFG009_MASK \
-        (0x0000000080000024ull)
-#define CVMX_PCIEEP_CFG010 \
-        (0x0000000000000028ull)
-#define CVMX_PCIEEP_CFG011 \
-        (0x000000000000002Cull)
-#define CVMX_PCIEEP_CFG012 \
-        (0x0000000000000030ull)
-#define CVMX_PCIEEP_CFG012_MASK \
-        (0x0000000080000030ull)
-#define CVMX_PCIEEP_CFG013 \
-        (0x0000000000000034ull)
-#define CVMX_PCIEEP_CFG015 \
-        (0x000000000000003Cull)
-#define CVMX_PCIEEP_CFG016 \
-        (0x0000000000000040ull)
-#define CVMX_PCIEEP_CFG017 \
-        (0x0000000000000044ull)
-#define CVMX_PCIEEP_CFG020 \
-        (0x0000000000000050ull)
-#define CVMX_PCIEEP_CFG021 \
-        (0x0000000000000054ull)
-#define CVMX_PCIEEP_CFG022 \
-        (0x0000000000000058ull)
-#define CVMX_PCIEEP_CFG023 \
-        (0x000000000000005Cull)
-#define CVMX_PCIEEP_CFG028 \
-        (0x0000000000000070ull)
-#define CVMX_PCIEEP_CFG029 \
-        (0x0000000000000074ull)
-#define CVMX_PCIEEP_CFG030 \
-        (0x0000000000000078ull)
-#define CVMX_PCIEEP_CFG031 \
-        (0x000000000000007Cull)
-#define CVMX_PCIEEP_CFG032 \
-        (0x0000000000000080ull)
-#define CVMX_PCIEEP_CFG033 \
-        (0x0000000000000084ull)
-#define CVMX_PCIEEP_CFG034 \
-        (0x0000000000000088ull)
-#define CVMX_PCIEEP_CFG037 \
-        (0x0000000000000094ull)
-#define CVMX_PCIEEP_CFG038 \
-        (0x0000000000000098ull)
-#define CVMX_PCIEEP_CFG039 \
-        (0x000000000000009Cull)
-#define CVMX_PCIEEP_CFG040 \
-        (0x00000000000000A0ull)
-#define CVMX_PCIEEP_CFG041 \
-        (0x00000000000000A4ull)
-#define CVMX_PCIEEP_CFG042 \
-        (0x00000000000000A8ull)
-#define CVMX_PCIEEP_CFG064 \
-        (0x0000000000000100ull)
-#define CVMX_PCIEEP_CFG065 \
-        (0x0000000000000104ull)
-#define CVMX_PCIEEP_CFG066 \
-        (0x0000000000000108ull)
-#define CVMX_PCIEEP_CFG067 \
-        (0x000000000000010Cull)
-#define CVMX_PCIEEP_CFG068 \
-        (0x0000000000000110ull)
-#define CVMX_PCIEEP_CFG069 \
-        (0x0000000000000114ull)
-#define CVMX_PCIEEP_CFG070 \
-        (0x0000000000000118ull)
-#define CVMX_PCIEEP_CFG071 \
-        (0x000000000000011Cull)
-#define CVMX_PCIEEP_CFG072 \
-        (0x0000000000000120ull)
-#define CVMX_PCIEEP_CFG073 \
-        (0x0000000000000124ull)
-#define CVMX_PCIEEP_CFG074 \
-        (0x0000000000000128ull)
-#define CVMX_PCIEEP_CFG448 \
-        (0x0000000000000700ull)
-#define CVMX_PCIEEP_CFG449 \
-        (0x0000000000000704ull)
-#define CVMX_PCIEEP_CFG450 \
-        (0x0000000000000708ull)
-#define CVMX_PCIEEP_CFG451 \
-        (0x000000000000070Cull)
-#define CVMX_PCIEEP_CFG452 \
-        (0x0000000000000710ull)
-#define CVMX_PCIEEP_CFG453 \
-        (0x0000000000000714ull)
-#define CVMX_PCIEEP_CFG454 \
-        (0x0000000000000718ull)
-#define CVMX_PCIEEP_CFG455 \
-        (0x000000000000071Cull)
-#define CVMX_PCIEEP_CFG456 \
-        (0x0000000000000720ull)
-#define CVMX_PCIEEP_CFG458 \
-        (0x0000000000000728ull)
-#define CVMX_PCIEEP_CFG459 \
-        (0x000000000000072Cull)
-#define CVMX_PCIEEP_CFG460 \
-        (0x0000000000000730ull)
-#define CVMX_PCIEEP_CFG461 \
-        (0x0000000000000734ull)
-#define CVMX_PCIEEP_CFG462 \
-        (0x0000000000000738ull)
-#define CVMX_PCIEEP_CFG463 \
-        (0x000000000000073Cull)
-#define CVMX_PCIEEP_CFG464 \
-        (0x0000000000000740ull)
-#define CVMX_PCIEEP_CFG465 \
-        (0x0000000000000744ull)
-#define CVMX_PCIEEP_CFG466 \
-        (0x0000000000000748ull)
-#define CVMX_PCIEEP_CFG467 \
-        (0x000000000000074Cull)
-#define CVMX_PCIEEP_CFG468 \
-        (0x0000000000000750ull)
-#define CVMX_PCIEEP_CFG490 \
-        (0x00000000000007A8ull)
-#define CVMX_PCIEEP_CFG491 \
-        (0x00000000000007ACull)
-#define CVMX_PCIEEP_CFG492 \
-        (0x00000000000007B0ull)
-#define CVMX_PCIEEP_CFG516 \
-        (0x0000000000000810ull)
-#define CVMX_PCIEEP_CFG517 \
-        (0x0000000000000814ull)
-
-union cvmx_pcieep_cfg000 {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg000_s {
-               uint32_t devid:16;
-               uint32_t vendid:16;
-       } s;
-       struct cvmx_pcieep_cfg000_s cn52xx;
-       struct cvmx_pcieep_cfg000_s cn52xxp1;
-       struct cvmx_pcieep_cfg000_s cn56xx;
-       struct cvmx_pcieep_cfg000_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg001 {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg001_s {
-               uint32_t dpe:1;
-               uint32_t sse:1;
-               uint32_t rma:1;
-               uint32_t rta:1;
-               uint32_t sta:1;
-               uint32_t devt:2;
-               uint32_t mdpe:1;
-               uint32_t fbb:1;
-               uint32_t reserved_22_22:1;
-               uint32_t m66:1;
-               uint32_t cl:1;
-               uint32_t i_stat:1;
-               uint32_t reserved_11_18:8;
-               uint32_t i_dis:1;
-               uint32_t fbbe:1;
-               uint32_t see:1;
-               uint32_t ids_wcc:1;
-               uint32_t per:1;
-               uint32_t vps:1;
-               uint32_t mwice:1;
-               uint32_t scse:1;
-               uint32_t me:1;
-               uint32_t msae:1;
-               uint32_t isae:1;
-       } s;
-       struct cvmx_pcieep_cfg001_s cn52xx;
-       struct cvmx_pcieep_cfg001_s cn52xxp1;
-       struct cvmx_pcieep_cfg001_s cn56xx;
-       struct cvmx_pcieep_cfg001_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg002 {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg002_s {
-               uint32_t bcc:8;
-               uint32_t sc:8;
-               uint32_t pi:8;
-               uint32_t rid:8;
-       } s;
-       struct cvmx_pcieep_cfg002_s cn52xx;
-       struct cvmx_pcieep_cfg002_s cn52xxp1;
-       struct cvmx_pcieep_cfg002_s cn56xx;
-       struct cvmx_pcieep_cfg002_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg003 {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg003_s {
-               uint32_t bist:8;
-               uint32_t mfd:1;
-               uint32_t chf:7;
-               uint32_t lt:8;
-               uint32_t cls:8;
-       } s;
-       struct cvmx_pcieep_cfg003_s cn52xx;
-       struct cvmx_pcieep_cfg003_s cn52xxp1;
-       struct cvmx_pcieep_cfg003_s cn56xx;
-       struct cvmx_pcieep_cfg003_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg004 {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg004_s {
-               uint32_t lbab:18;
-               uint32_t reserved_4_13:10;
-               uint32_t pf:1;
-               uint32_t typ:2;
-               uint32_t mspc:1;
-       } s;
-       struct cvmx_pcieep_cfg004_s cn52xx;
-       struct cvmx_pcieep_cfg004_s cn52xxp1;
-       struct cvmx_pcieep_cfg004_s cn56xx;
-       struct cvmx_pcieep_cfg004_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg004_mask {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg004_mask_s {
-               uint32_t lmask:31;
-               uint32_t enb:1;
-       } s;
-       struct cvmx_pcieep_cfg004_mask_s cn52xx;
-       struct cvmx_pcieep_cfg004_mask_s cn52xxp1;
-       struct cvmx_pcieep_cfg004_mask_s cn56xx;
-       struct cvmx_pcieep_cfg004_mask_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg005 {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg005_s {
-               uint32_t ubab:32;
-       } s;
-       struct cvmx_pcieep_cfg005_s cn52xx;
-       struct cvmx_pcieep_cfg005_s cn52xxp1;
-       struct cvmx_pcieep_cfg005_s cn56xx;
-       struct cvmx_pcieep_cfg005_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg005_mask {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg005_mask_s {
-               uint32_t umask:32;
-       } s;
-       struct cvmx_pcieep_cfg005_mask_s cn52xx;
-       struct cvmx_pcieep_cfg005_mask_s cn52xxp1;
-       struct cvmx_pcieep_cfg005_mask_s cn56xx;
-       struct cvmx_pcieep_cfg005_mask_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg006 {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg006_s {
-               uint32_t lbab:6;
-               uint32_t reserved_4_25:22;
-               uint32_t pf:1;
-               uint32_t typ:2;
-               uint32_t mspc:1;
-       } s;
-       struct cvmx_pcieep_cfg006_s cn52xx;
-       struct cvmx_pcieep_cfg006_s cn52xxp1;
-       struct cvmx_pcieep_cfg006_s cn56xx;
-       struct cvmx_pcieep_cfg006_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg006_mask {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg006_mask_s {
-               uint32_t lmask:31;
-               uint32_t enb:1;
-       } s;
-       struct cvmx_pcieep_cfg006_mask_s cn52xx;
-       struct cvmx_pcieep_cfg006_mask_s cn52xxp1;
-       struct cvmx_pcieep_cfg006_mask_s cn56xx;
-       struct cvmx_pcieep_cfg006_mask_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg007 {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg007_s {
-               uint32_t ubab:32;
-       } s;
-       struct cvmx_pcieep_cfg007_s cn52xx;
-       struct cvmx_pcieep_cfg007_s cn52xxp1;
-       struct cvmx_pcieep_cfg007_s cn56xx;
-       struct cvmx_pcieep_cfg007_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg007_mask {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg007_mask_s {
-               uint32_t umask:32;
-       } s;
-       struct cvmx_pcieep_cfg007_mask_s cn52xx;
-       struct cvmx_pcieep_cfg007_mask_s cn52xxp1;
-       struct cvmx_pcieep_cfg007_mask_s cn56xx;
-       struct cvmx_pcieep_cfg007_mask_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg008 {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg008_s {
-               uint32_t reserved_4_31:28;
-               uint32_t pf:1;
-               uint32_t typ:2;
-               uint32_t mspc:1;
-       } s;
-       struct cvmx_pcieep_cfg008_s cn52xx;
-       struct cvmx_pcieep_cfg008_s cn52xxp1;
-       struct cvmx_pcieep_cfg008_s cn56xx;
-       struct cvmx_pcieep_cfg008_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg008_mask {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg008_mask_s {
-               uint32_t lmask:31;
-               uint32_t enb:1;
-       } s;
-       struct cvmx_pcieep_cfg008_mask_s cn52xx;
-       struct cvmx_pcieep_cfg008_mask_s cn52xxp1;
-       struct cvmx_pcieep_cfg008_mask_s cn56xx;
-       struct cvmx_pcieep_cfg008_mask_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg009 {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg009_s {
-               uint32_t ubab:25;
-               uint32_t reserved_0_6:7;
-       } s;
-       struct cvmx_pcieep_cfg009_s cn52xx;
-       struct cvmx_pcieep_cfg009_s cn52xxp1;
-       struct cvmx_pcieep_cfg009_s cn56xx;
-       struct cvmx_pcieep_cfg009_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg009_mask {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg009_mask_s {
-               uint32_t umask:32;
-       } s;
-       struct cvmx_pcieep_cfg009_mask_s cn52xx;
-       struct cvmx_pcieep_cfg009_mask_s cn52xxp1;
-       struct cvmx_pcieep_cfg009_mask_s cn56xx;
-       struct cvmx_pcieep_cfg009_mask_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg010 {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg010_s {
-               uint32_t cisp:32;
-       } s;
-       struct cvmx_pcieep_cfg010_s cn52xx;
-       struct cvmx_pcieep_cfg010_s cn52xxp1;
-       struct cvmx_pcieep_cfg010_s cn56xx;
-       struct cvmx_pcieep_cfg010_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg011 {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg011_s {
-               uint32_t ssid:16;
-               uint32_t ssvid:16;
-       } s;
-       struct cvmx_pcieep_cfg011_s cn52xx;
-       struct cvmx_pcieep_cfg011_s cn52xxp1;
-       struct cvmx_pcieep_cfg011_s cn56xx;
-       struct cvmx_pcieep_cfg011_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg012 {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg012_s {
-               uint32_t eraddr:16;
-               uint32_t reserved_1_15:15;
-               uint32_t er_en:1;
-       } s;
-       struct cvmx_pcieep_cfg012_s cn52xx;
-       struct cvmx_pcieep_cfg012_s cn52xxp1;
-       struct cvmx_pcieep_cfg012_s cn56xx;
-       struct cvmx_pcieep_cfg012_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg012_mask {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg012_mask_s {
-               uint32_t mask:31;
-               uint32_t enb:1;
-       } s;
-       struct cvmx_pcieep_cfg012_mask_s cn52xx;
-       struct cvmx_pcieep_cfg012_mask_s cn52xxp1;
-       struct cvmx_pcieep_cfg012_mask_s cn56xx;
-       struct cvmx_pcieep_cfg012_mask_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg013 {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg013_s {
-               uint32_t reserved_8_31:24;
-               uint32_t cp:8;
-       } s;
-       struct cvmx_pcieep_cfg013_s cn52xx;
-       struct cvmx_pcieep_cfg013_s cn52xxp1;
-       struct cvmx_pcieep_cfg013_s cn56xx;
-       struct cvmx_pcieep_cfg013_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg015 {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg015_s {
-               uint32_t ml:8;
-               uint32_t mg:8;
-               uint32_t inta:8;
-               uint32_t il:8;
-       } s;
-       struct cvmx_pcieep_cfg015_s cn52xx;
-       struct cvmx_pcieep_cfg015_s cn52xxp1;
-       struct cvmx_pcieep_cfg015_s cn56xx;
-       struct cvmx_pcieep_cfg015_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg016 {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg016_s {
-               uint32_t pmes:5;
-               uint32_t d2s:1;
-               uint32_t d1s:1;
-               uint32_t auxc:3;
-               uint32_t dsi:1;
-               uint32_t reserved_20_20:1;
-               uint32_t pme_clock:1;
-               uint32_t pmsv:3;
-               uint32_t ncp:8;
-               uint32_t pmcid:8;
-       } s;
-       struct cvmx_pcieep_cfg016_s cn52xx;
-       struct cvmx_pcieep_cfg016_s cn52xxp1;
-       struct cvmx_pcieep_cfg016_s cn56xx;
-       struct cvmx_pcieep_cfg016_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg017 {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg017_s {
-               uint32_t pmdia:8;
-               uint32_t bpccee:1;
-               uint32_t bd3h:1;
-               uint32_t reserved_16_21:6;
-               uint32_t pmess:1;
-               uint32_t pmedsia:2;
-               uint32_t pmds:4;
-               uint32_t pmeens:1;
-               uint32_t reserved_4_7:4;
-               uint32_t nsr:1;
-               uint32_t reserved_2_2:1;
-               uint32_t ps:2;
-       } s;
-       struct cvmx_pcieep_cfg017_s cn52xx;
-       struct cvmx_pcieep_cfg017_s cn52xxp1;
-       struct cvmx_pcieep_cfg017_s cn56xx;
-       struct cvmx_pcieep_cfg017_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg020 {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg020_s {
-               uint32_t reserved_24_31:8;
-               uint32_t m64:1;
-               uint32_t mme:3;
-               uint32_t mmc:3;
-               uint32_t msien:1;
-               uint32_t ncp:8;
-               uint32_t msicid:8;
-       } s;
-       struct cvmx_pcieep_cfg020_s cn52xx;
-       struct cvmx_pcieep_cfg020_s cn52xxp1;
-       struct cvmx_pcieep_cfg020_s cn56xx;
-       struct cvmx_pcieep_cfg020_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg021 {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg021_s {
-               uint32_t lmsi:30;
-               uint32_t reserved_0_1:2;
-       } s;
-       struct cvmx_pcieep_cfg021_s cn52xx;
-       struct cvmx_pcieep_cfg021_s cn52xxp1;
-       struct cvmx_pcieep_cfg021_s cn56xx;
-       struct cvmx_pcieep_cfg021_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg022 {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg022_s {
-               uint32_t umsi:32;
-       } s;
-       struct cvmx_pcieep_cfg022_s cn52xx;
-       struct cvmx_pcieep_cfg022_s cn52xxp1;
-       struct cvmx_pcieep_cfg022_s cn56xx;
-       struct cvmx_pcieep_cfg022_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg023 {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg023_s {
-               uint32_t reserved_16_31:16;
-               uint32_t msimd:16;
-       } s;
-       struct cvmx_pcieep_cfg023_s cn52xx;
-       struct cvmx_pcieep_cfg023_s cn52xxp1;
-       struct cvmx_pcieep_cfg023_s cn56xx;
-       struct cvmx_pcieep_cfg023_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg028 {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg028_s {
-               uint32_t reserved_30_31:2;
-               uint32_t imn:5;
-               uint32_t si:1;
-               uint32_t dpt:4;
-               uint32_t pciecv:4;
-               uint32_t ncp:8;
-               uint32_t pcieid:8;
-       } s;
-       struct cvmx_pcieep_cfg028_s cn52xx;
-       struct cvmx_pcieep_cfg028_s cn52xxp1;
-       struct cvmx_pcieep_cfg028_s cn56xx;
-       struct cvmx_pcieep_cfg028_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg029 {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg029_s {
-               uint32_t reserved_28_31:4;
-               uint32_t cspls:2;
-               uint32_t csplv:8;
-               uint32_t reserved_16_17:2;
-               uint32_t rber:1;
-               uint32_t reserved_12_14:3;
-               uint32_t el1al:3;
-               uint32_t el0al:3;
-               uint32_t etfs:1;
-               uint32_t pfs:2;
-               uint32_t mpss:3;
-       } s;
-       struct cvmx_pcieep_cfg029_s cn52xx;
-       struct cvmx_pcieep_cfg029_s cn52xxp1;
-       struct cvmx_pcieep_cfg029_s cn56xx;
-       struct cvmx_pcieep_cfg029_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg030 {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg030_s {
-               uint32_t reserved_22_31:10;
-               uint32_t tp:1;
-               uint32_t ap_d:1;
-               uint32_t ur_d:1;
-               uint32_t fe_d:1;
-               uint32_t nfe_d:1;
-               uint32_t ce_d:1;
-               uint32_t reserved_15_15:1;
-               uint32_t mrrs:3;
-               uint32_t ns_en:1;
-               uint32_t ap_en:1;
-               uint32_t pf_en:1;
-               uint32_t etf_en:1;
-               uint32_t mps:3;
-               uint32_t ro_en:1;
-               uint32_t ur_en:1;
-               uint32_t fe_en:1;
-               uint32_t nfe_en:1;
-               uint32_t ce_en:1;
-       } s;
-       struct cvmx_pcieep_cfg030_s cn52xx;
-       struct cvmx_pcieep_cfg030_s cn52xxp1;
-       struct cvmx_pcieep_cfg030_s cn56xx;
-       struct cvmx_pcieep_cfg030_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg031 {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg031_s {
-               uint32_t pnum:8;
-               uint32_t reserved_22_23:2;
-               uint32_t lbnc:1;
-               uint32_t dllarc:1;
-               uint32_t sderc:1;
-               uint32_t cpm:1;
-               uint32_t l1el:3;
-               uint32_t l0el:3;
-               uint32_t aslpms:2;
-               uint32_t mlw:6;
-               uint32_t mls:4;
-       } s;
-       struct cvmx_pcieep_cfg031_s cn52xx;
-       struct cvmx_pcieep_cfg031_s cn52xxp1;
-       struct cvmx_pcieep_cfg031_s cn56xx;
-       struct cvmx_pcieep_cfg031_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg032 {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg032_s {
-               uint32_t reserved_30_31:2;
-               uint32_t dlla:1;
-               uint32_t scc:1;
-               uint32_t lt:1;
-               uint32_t reserved_26_26:1;
-               uint32_t nlw:6;
-               uint32_t ls:4;
-               uint32_t reserved_10_15:6;
-               uint32_t hawd:1;
-               uint32_t ecpm:1;
-               uint32_t es:1;
-               uint32_t ccc:1;
-               uint32_t rl:1;
-               uint32_t ld:1;
-               uint32_t rcb:1;
-               uint32_t reserved_2_2:1;
-               uint32_t aslpc:2;
-       } s;
-       struct cvmx_pcieep_cfg032_s cn52xx;
-       struct cvmx_pcieep_cfg032_s cn52xxp1;
-       struct cvmx_pcieep_cfg032_s cn56xx;
-       struct cvmx_pcieep_cfg032_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg033 {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg033_s {
-               uint32_t ps_num:13;
-               uint32_t nccs:1;
-               uint32_t emip:1;
-               uint32_t sp_ls:2;
-               uint32_t sp_lv:8;
-               uint32_t hp_c:1;
-               uint32_t hp_s:1;
-               uint32_t pip:1;
-               uint32_t aip:1;
-               uint32_t mrlsp:1;
-               uint32_t pcp:1;
-               uint32_t abp:1;
-       } s;
-       struct cvmx_pcieep_cfg033_s cn52xx;
-       struct cvmx_pcieep_cfg033_s cn52xxp1;
-       struct cvmx_pcieep_cfg033_s cn56xx;
-       struct cvmx_pcieep_cfg033_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg034 {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg034_s {
-               uint32_t reserved_25_31:7;
-               uint32_t dlls_c:1;
-               uint32_t emis:1;
-               uint32_t pds:1;
-               uint32_t mrlss:1;
-               uint32_t ccint_d:1;
-               uint32_t pd_c:1;
-               uint32_t mrls_c:1;
-               uint32_t pf_d:1;
-               uint32_t abp_d:1;
-               uint32_t reserved_13_15:3;
-               uint32_t dlls_en:1;
-               uint32_t emic:1;
-               uint32_t pcc:1;
-               uint32_t pic:2;
-               uint32_t aic:2;
-               uint32_t hpint_en:1;
-               uint32_t ccint_en:1;
-               uint32_t pd_en:1;
-               uint32_t mrls_en:1;
-               uint32_t pf_en:1;
-               uint32_t abp_en:1;
-       } s;
-       struct cvmx_pcieep_cfg034_s cn52xx;
-       struct cvmx_pcieep_cfg034_s cn52xxp1;
-       struct cvmx_pcieep_cfg034_s cn56xx;
-       struct cvmx_pcieep_cfg034_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg037 {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg037_s {
-               uint32_t reserved_5_31:27;
-               uint32_t ctds:1;
-               uint32_t ctrs:4;
-       } s;
-       struct cvmx_pcieep_cfg037_s cn52xx;
-       struct cvmx_pcieep_cfg037_s cn52xxp1;
-       struct cvmx_pcieep_cfg037_s cn56xx;
-       struct cvmx_pcieep_cfg037_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg038 {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg038_s {
-               uint32_t reserved_5_31:27;
-               uint32_t ctd:1;
-               uint32_t ctv:4;
-       } s;
-       struct cvmx_pcieep_cfg038_s cn52xx;
-       struct cvmx_pcieep_cfg038_s cn52xxp1;
-       struct cvmx_pcieep_cfg038_s cn56xx;
-       struct cvmx_pcieep_cfg038_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg039 {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg039_s {
-               uint32_t reserved_0_31:32;
-       } s;
-       struct cvmx_pcieep_cfg039_s cn52xx;
-       struct cvmx_pcieep_cfg039_s cn52xxp1;
-       struct cvmx_pcieep_cfg039_s cn56xx;
-       struct cvmx_pcieep_cfg039_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg040 {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg040_s {
-               uint32_t reserved_0_31:32;
-       } s;
-       struct cvmx_pcieep_cfg040_s cn52xx;
-       struct cvmx_pcieep_cfg040_s cn52xxp1;
-       struct cvmx_pcieep_cfg040_s cn56xx;
-       struct cvmx_pcieep_cfg040_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg041 {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg041_s {
-               uint32_t reserved_0_31:32;
-       } s;
-       struct cvmx_pcieep_cfg041_s cn52xx;
-       struct cvmx_pcieep_cfg041_s cn52xxp1;
-       struct cvmx_pcieep_cfg041_s cn56xx;
-       struct cvmx_pcieep_cfg041_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg042 {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg042_s {
-               uint32_t reserved_0_31:32;
-       } s;
-       struct cvmx_pcieep_cfg042_s cn52xx;
-       struct cvmx_pcieep_cfg042_s cn52xxp1;
-       struct cvmx_pcieep_cfg042_s cn56xx;
-       struct cvmx_pcieep_cfg042_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg064 {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg064_s {
-               uint32_t nco:12;
-               uint32_t cv:4;
-               uint32_t pcieec:16;
-       } s;
-       struct cvmx_pcieep_cfg064_s cn52xx;
-       struct cvmx_pcieep_cfg064_s cn52xxp1;
-       struct cvmx_pcieep_cfg064_s cn56xx;
-       struct cvmx_pcieep_cfg064_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg065 {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg065_s {
-               uint32_t reserved_21_31:11;
-               uint32_t ures:1;
-               uint32_t ecrces:1;
-               uint32_t mtlps:1;
-               uint32_t ros:1;
-               uint32_t ucs:1;
-               uint32_t cas:1;
-               uint32_t cts:1;
-               uint32_t fcpes:1;
-               uint32_t ptlps:1;
-               uint32_t reserved_6_11:6;
-               uint32_t sdes:1;
-               uint32_t dlpes:1;
-               uint32_t reserved_0_3:4;
-       } s;
-       struct cvmx_pcieep_cfg065_s cn52xx;
-       struct cvmx_pcieep_cfg065_s cn52xxp1;
-       struct cvmx_pcieep_cfg065_s cn56xx;
-       struct cvmx_pcieep_cfg065_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg066 {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg066_s {
-               uint32_t reserved_21_31:11;
-               uint32_t urem:1;
-               uint32_t ecrcem:1;
-               uint32_t mtlpm:1;
-               uint32_t rom:1;
-               uint32_t ucm:1;
-               uint32_t cam:1;
-               uint32_t ctm:1;
-               uint32_t fcpem:1;
-               uint32_t ptlpm:1;
-               uint32_t reserved_6_11:6;
-               uint32_t sdem:1;
-               uint32_t dlpem:1;
-               uint32_t reserved_0_3:4;
-       } s;
-       struct cvmx_pcieep_cfg066_s cn52xx;
-       struct cvmx_pcieep_cfg066_s cn52xxp1;
-       struct cvmx_pcieep_cfg066_s cn56xx;
-       struct cvmx_pcieep_cfg066_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg067 {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg067_s {
-               uint32_t reserved_21_31:11;
-               uint32_t ures:1;
-               uint32_t ecrces:1;
-               uint32_t mtlps:1;
-               uint32_t ros:1;
-               uint32_t ucs:1;
-               uint32_t cas:1;
-               uint32_t cts:1;
-               uint32_t fcpes:1;
-               uint32_t ptlps:1;
-               uint32_t reserved_6_11:6;
-               uint32_t sdes:1;
-               uint32_t dlpes:1;
-               uint32_t reserved_0_3:4;
-       } s;
-       struct cvmx_pcieep_cfg067_s cn52xx;
-       struct cvmx_pcieep_cfg067_s cn52xxp1;
-       struct cvmx_pcieep_cfg067_s cn56xx;
-       struct cvmx_pcieep_cfg067_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg068 {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg068_s {
-               uint32_t reserved_14_31:18;
-               uint32_t anfes:1;
-               uint32_t rtts:1;
-               uint32_t reserved_9_11:3;
-               uint32_t rnrs:1;
-               uint32_t bdllps:1;
-               uint32_t btlps:1;
-               uint32_t reserved_1_5:5;
-               uint32_t res:1;
-       } s;
-       struct cvmx_pcieep_cfg068_s cn52xx;
-       struct cvmx_pcieep_cfg068_s cn52xxp1;
-       struct cvmx_pcieep_cfg068_s cn56xx;
-       struct cvmx_pcieep_cfg068_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg069 {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg069_s {
-               uint32_t reserved_14_31:18;
-               uint32_t anfem:1;
-               uint32_t rttm:1;
-               uint32_t reserved_9_11:3;
-               uint32_t rnrm:1;
-               uint32_t bdllpm:1;
-               uint32_t btlpm:1;
-               uint32_t reserved_1_5:5;
-               uint32_t rem:1;
-       } s;
-       struct cvmx_pcieep_cfg069_s cn52xx;
-       struct cvmx_pcieep_cfg069_s cn52xxp1;
-       struct cvmx_pcieep_cfg069_s cn56xx;
-       struct cvmx_pcieep_cfg069_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg070 {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg070_s {
-               uint32_t reserved_9_31:23;
-               uint32_t ce:1;
-               uint32_t cc:1;
-               uint32_t ge:1;
-               uint32_t gc:1;
-               uint32_t fep:5;
-       } s;
-       struct cvmx_pcieep_cfg070_s cn52xx;
-       struct cvmx_pcieep_cfg070_s cn52xxp1;
-       struct cvmx_pcieep_cfg070_s cn56xx;
-       struct cvmx_pcieep_cfg070_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg071 {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg071_s {
-               uint32_t dword1:32;
-       } s;
-       struct cvmx_pcieep_cfg071_s cn52xx;
-       struct cvmx_pcieep_cfg071_s cn52xxp1;
-       struct cvmx_pcieep_cfg071_s cn56xx;
-       struct cvmx_pcieep_cfg071_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg072 {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg072_s {
-               uint32_t dword2:32;
-       } s;
-       struct cvmx_pcieep_cfg072_s cn52xx;
-       struct cvmx_pcieep_cfg072_s cn52xxp1;
-       struct cvmx_pcieep_cfg072_s cn56xx;
-       struct cvmx_pcieep_cfg072_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg073 {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg073_s {
-               uint32_t dword3:32;
-       } s;
-       struct cvmx_pcieep_cfg073_s cn52xx;
-       struct cvmx_pcieep_cfg073_s cn52xxp1;
-       struct cvmx_pcieep_cfg073_s cn56xx;
-       struct cvmx_pcieep_cfg073_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg074 {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg074_s {
-               uint32_t dword4:32;
-       } s;
-       struct cvmx_pcieep_cfg074_s cn52xx;
-       struct cvmx_pcieep_cfg074_s cn52xxp1;
-       struct cvmx_pcieep_cfg074_s cn56xx;
-       struct cvmx_pcieep_cfg074_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg448 {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg448_s {
-               uint32_t rtl:16;
-               uint32_t rtltl:16;
-       } s;
-       struct cvmx_pcieep_cfg448_s cn52xx;
-       struct cvmx_pcieep_cfg448_s cn52xxp1;
-       struct cvmx_pcieep_cfg448_s cn56xx;
-       struct cvmx_pcieep_cfg448_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg449 {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg449_s {
-               uint32_t omr:32;
-       } s;
-       struct cvmx_pcieep_cfg449_s cn52xx;
-       struct cvmx_pcieep_cfg449_s cn52xxp1;
-       struct cvmx_pcieep_cfg449_s cn56xx;
-       struct cvmx_pcieep_cfg449_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg450 {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg450_s {
-               uint32_t lpec:8;
-               uint32_t reserved_22_23:2;
-               uint32_t link_state:6;
-               uint32_t force_link:1;
-               uint32_t reserved_8_14:7;
-               uint32_t link_num:8;
-       } s;
-       struct cvmx_pcieep_cfg450_s cn52xx;
-       struct cvmx_pcieep_cfg450_s cn52xxp1;
-       struct cvmx_pcieep_cfg450_s cn56xx;
-       struct cvmx_pcieep_cfg450_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg451 {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg451_s {
-               uint32_t reserved_30_31:2;
-               uint32_t l1el:3;
-               uint32_t l0el:3;
-               uint32_t n_fts_cc:8;
-               uint32_t n_fts:8;
-               uint32_t ack_freq:8;
-       } s;
-       struct cvmx_pcieep_cfg451_s cn52xx;
-       struct cvmx_pcieep_cfg451_s cn52xxp1;
-       struct cvmx_pcieep_cfg451_s cn56xx;
-       struct cvmx_pcieep_cfg451_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg452 {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg452_s {
-               uint32_t reserved_26_31:6;
-               uint32_t eccrc:1;
-               uint32_t reserved_22_24:3;
-               uint32_t lme:6;
-               uint32_t reserved_8_15:8;
-               uint32_t flm:1;
-               uint32_t reserved_6_6:1;
-               uint32_t dllle:1;
-               uint32_t reserved_4_4:1;
-               uint32_t ra:1;
-               uint32_t le:1;
-               uint32_t sd:1;
-               uint32_t omr:1;
-       } s;
-       struct cvmx_pcieep_cfg452_s cn52xx;
-       struct cvmx_pcieep_cfg452_s cn52xxp1;
-       struct cvmx_pcieep_cfg452_s cn56xx;
-       struct cvmx_pcieep_cfg452_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg453 {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg453_s {
-               uint32_t dlld:1;
-               uint32_t reserved_26_30:5;
-               uint32_t ack_nak:1;
-               uint32_t fcd:1;
-               uint32_t ilst:24;
-       } s;
-       struct cvmx_pcieep_cfg453_s cn52xx;
-       struct cvmx_pcieep_cfg453_s cn52xxp1;
-       struct cvmx_pcieep_cfg453_s cn56xx;
-       struct cvmx_pcieep_cfg453_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg454 {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg454_s {
-               uint32_t reserved_29_31:3;
-               uint32_t tmfcwt:5;
-               uint32_t tmanlt:5;
-               uint32_t tmrt:5;
-               uint32_t reserved_11_13:3;
-               uint32_t nskps:3;
-               uint32_t reserved_4_7:4;
-               uint32_t ntss:4;
-       } s;
-       struct cvmx_pcieep_cfg454_s cn52xx;
-       struct cvmx_pcieep_cfg454_s cn52xxp1;
-       struct cvmx_pcieep_cfg454_s cn56xx;
-       struct cvmx_pcieep_cfg454_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg455 {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg455_s {
-               uint32_t m_cfg0_filt:1;
-               uint32_t m_io_filt:1;
-               uint32_t msg_ctrl:1;
-               uint32_t m_cpl_ecrc_filt:1;
-               uint32_t m_ecrc_filt:1;
-               uint32_t m_cpl_len_err:1;
-               uint32_t m_cpl_attr_err:1;
-               uint32_t m_cpl_tc_err:1;
-               uint32_t m_cpl_fun_err:1;
-               uint32_t m_cpl_rid_err:1;
-               uint32_t m_cpl_tag_err:1;
-               uint32_t m_lk_filt:1;
-               uint32_t m_cfg1_filt:1;
-               uint32_t m_bar_match:1;
-               uint32_t m_pois_filt:1;
-               uint32_t m_fun:1;
-               uint32_t dfcwt:1;
-               uint32_t reserved_11_14:4;
-               uint32_t skpiv:11;
-       } s;
-       struct cvmx_pcieep_cfg455_s cn52xx;
-       struct cvmx_pcieep_cfg455_s cn52xxp1;
-       struct cvmx_pcieep_cfg455_s cn56xx;
-       struct cvmx_pcieep_cfg455_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg456 {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg456_s {
-               uint32_t reserved_2_31:30;
-               uint32_t m_vend1_drp:1;
-               uint32_t m_vend0_drp:1;
-       } s;
-       struct cvmx_pcieep_cfg456_s cn52xx;
-       struct cvmx_pcieep_cfg456_s cn52xxp1;
-       struct cvmx_pcieep_cfg456_s cn56xx;
-       struct cvmx_pcieep_cfg456_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg458 {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg458_s {
-               uint32_t dbg_info_l32:32;
-       } s;
-       struct cvmx_pcieep_cfg458_s cn52xx;
-       struct cvmx_pcieep_cfg458_s cn52xxp1;
-       struct cvmx_pcieep_cfg458_s cn56xx;
-       struct cvmx_pcieep_cfg458_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg459 {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg459_s {
-               uint32_t dbg_info_u32:32;
-       } s;
-       struct cvmx_pcieep_cfg459_s cn52xx;
-       struct cvmx_pcieep_cfg459_s cn52xxp1;
-       struct cvmx_pcieep_cfg459_s cn56xx;
-       struct cvmx_pcieep_cfg459_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg460 {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg460_s {
-               uint32_t reserved_20_31:12;
-               uint32_t tphfcc:8;
-               uint32_t tpdfcc:12;
-       } s;
-       struct cvmx_pcieep_cfg460_s cn52xx;
-       struct cvmx_pcieep_cfg460_s cn52xxp1;
-       struct cvmx_pcieep_cfg460_s cn56xx;
-       struct cvmx_pcieep_cfg460_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg461 {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg461_s {
-               uint32_t reserved_20_31:12;
-               uint32_t tchfcc:8;
-               uint32_t tcdfcc:12;
-       } s;
-       struct cvmx_pcieep_cfg461_s cn52xx;
-       struct cvmx_pcieep_cfg461_s cn52xxp1;
-       struct cvmx_pcieep_cfg461_s cn56xx;
-       struct cvmx_pcieep_cfg461_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg462 {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg462_s {
-               uint32_t reserved_20_31:12;
-               uint32_t tchfcc:8;
-               uint32_t tcdfcc:12;
-       } s;
-       struct cvmx_pcieep_cfg462_s cn52xx;
-       struct cvmx_pcieep_cfg462_s cn52xxp1;
-       struct cvmx_pcieep_cfg462_s cn56xx;
-       struct cvmx_pcieep_cfg462_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg463 {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg463_s {
-               uint32_t reserved_3_31:29;
-               uint32_t rqne:1;
-               uint32_t trbne:1;
-               uint32_t rtlpfccnr:1;
-       } s;
-       struct cvmx_pcieep_cfg463_s cn52xx;
-       struct cvmx_pcieep_cfg463_s cn52xxp1;
-       struct cvmx_pcieep_cfg463_s cn56xx;
-       struct cvmx_pcieep_cfg463_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg464 {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg464_s {
-               uint32_t wrr_vc3:8;
-               uint32_t wrr_vc2:8;
-               uint32_t wrr_vc1:8;
-               uint32_t wrr_vc0:8;
-       } s;
-       struct cvmx_pcieep_cfg464_s cn52xx;
-       struct cvmx_pcieep_cfg464_s cn52xxp1;
-       struct cvmx_pcieep_cfg464_s cn56xx;
-       struct cvmx_pcieep_cfg464_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg465 {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg465_s {
-               uint32_t wrr_vc7:8;
-               uint32_t wrr_vc6:8;
-               uint32_t wrr_vc5:8;
-               uint32_t wrr_vc4:8;
-       } s;
-       struct cvmx_pcieep_cfg465_s cn52xx;
-       struct cvmx_pcieep_cfg465_s cn52xxp1;
-       struct cvmx_pcieep_cfg465_s cn56xx;
-       struct cvmx_pcieep_cfg465_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg466 {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg466_s {
-               uint32_t rx_queue_order:1;
-               uint32_t type_ordering:1;
-               uint32_t reserved_24_29:6;
-               uint32_t queue_mode:3;
-               uint32_t reserved_20_20:1;
-               uint32_t header_credits:8;
-               uint32_t data_credits:12;
-       } s;
-       struct cvmx_pcieep_cfg466_s cn52xx;
-       struct cvmx_pcieep_cfg466_s cn52xxp1;
-       struct cvmx_pcieep_cfg466_s cn56xx;
-       struct cvmx_pcieep_cfg466_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg467 {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg467_s {
-               uint32_t reserved_24_31:8;
-               uint32_t queue_mode:3;
-               uint32_t reserved_20_20:1;
-               uint32_t header_credits:8;
-               uint32_t data_credits:12;
-       } s;
-       struct cvmx_pcieep_cfg467_s cn52xx;
-       struct cvmx_pcieep_cfg467_s cn52xxp1;
-       struct cvmx_pcieep_cfg467_s cn56xx;
-       struct cvmx_pcieep_cfg467_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg468 {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg468_s {
-               uint32_t reserved_24_31:8;
-               uint32_t queue_mode:3;
-               uint32_t reserved_20_20:1;
-               uint32_t header_credits:8;
-               uint32_t data_credits:12;
-       } s;
-       struct cvmx_pcieep_cfg468_s cn52xx;
-       struct cvmx_pcieep_cfg468_s cn52xxp1;
-       struct cvmx_pcieep_cfg468_s cn56xx;
-       struct cvmx_pcieep_cfg468_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg490 {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg490_s {
-               uint32_t reserved_26_31:6;
-               uint32_t header_depth:10;
-               uint32_t reserved_14_15:2;
-               uint32_t data_depth:14;
-       } s;
-       struct cvmx_pcieep_cfg490_s cn52xx;
-       struct cvmx_pcieep_cfg490_s cn52xxp1;
-       struct cvmx_pcieep_cfg490_s cn56xx;
-       struct cvmx_pcieep_cfg490_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg491 {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg491_s {
-               uint32_t reserved_26_31:6;
-               uint32_t header_depth:10;
-               uint32_t reserved_14_15:2;
-               uint32_t data_depth:14;
-       } s;
-       struct cvmx_pcieep_cfg491_s cn52xx;
-       struct cvmx_pcieep_cfg491_s cn52xxp1;
-       struct cvmx_pcieep_cfg491_s cn56xx;
-       struct cvmx_pcieep_cfg491_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg492 {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg492_s {
-               uint32_t reserved_26_31:6;
-               uint32_t header_depth:10;
-               uint32_t reserved_14_15:2;
-               uint32_t data_depth:14;
-       } s;
-       struct cvmx_pcieep_cfg492_s cn52xx;
-       struct cvmx_pcieep_cfg492_s cn52xxp1;
-       struct cvmx_pcieep_cfg492_s cn56xx;
-       struct cvmx_pcieep_cfg492_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg516 {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg516_s {
-               uint32_t phy_stat:32;
-       } s;
-       struct cvmx_pcieep_cfg516_s cn52xx;
-       struct cvmx_pcieep_cfg516_s cn52xxp1;
-       struct cvmx_pcieep_cfg516_s cn56xx;
-       struct cvmx_pcieep_cfg516_s cn56xxp1;
-};
-
-union cvmx_pcieep_cfg517 {
-       uint32_t u32;
-       struct cvmx_pcieep_cfg517_s {
-               uint32_t phy_ctrl:32;
-       } s;
-       struct cvmx_pcieep_cfg517_s cn52xx;
-       struct cvmx_pcieep_cfg517_s cn52xxp1;
-       struct cvmx_pcieep_cfg517_s cn56xx;
-       struct cvmx_pcieep_cfg517_s cn56xxp1;
-};
-
-#endif
index fcd4060f642196b2e248fac9e3eca2d8cb3bb6d4..90bf3b3fce199cda78430b3af99770fe82de3fcd 100644 (file)
@@ -17,6 +17,7 @@
  */
 
 #include <linux/ioport.h>
+#include <linux/of.h>
 
 /*
  * Each pci channel is a top-level PCI bus seem by CPU.  A machine  with
@@ -26,6 +27,7 @@
 struct pci_controller {
        struct pci_controller *next;
        struct pci_bus *bus;
+       struct device_node *of_node;
 
        struct pci_ops *pci_ops;
        struct resource *mem_resource;
@@ -142,4 +144,8 @@ static inline int pci_get_legacy_ide_irq(struct pci_dev *dev, int channel)
 
 extern char * (*pcibios_plat_setup)(char *str);
 
+/* this function parses memory ranges from a device node */
+extern void __devinit pci_load_of_ranges(struct pci_controller *hose,
+                                        struct device_node *node);
+
 #endif /* _ASM_PCI_H */
index e0308dcca1358f6f2db6161486299de11d61dde5..fa03ec3fbf897a4c3271d8a38025f383c82760cc 100644 (file)
  * assume GCC is being used.
  */
 
-#if (_MIPS_SZLONG == 64)
-typedef unsigned int   __kernel_nlink_t;
-#define __kernel_nlink_t __kernel_nlink_t
-#endif
-
 typedef long           __kernel_daddr_t;
 #define __kernel_daddr_t __kernel_daddr_t
 
index 7a6e82ef449b3bf4e8747974dd3ed81c2a231401..7206d445bab876e926cec6bd242309b893870007 100644 (file)
@@ -12,6 +12,9 @@
 #define __ASM_PROM_H
 
 #ifdef CONFIG_OF
+#include <linux/bug.h>
+#include <linux/io.h>
+#include <linux/types.h>
 #include <asm/bootinfo.h>
 
 extern int early_init_dt_scan_memory_arch(unsigned long node,
@@ -21,6 +24,29 @@ extern int reserve_mem_mach(unsigned long addr, unsigned long size);
 extern void free_mem_mach(unsigned long addr, unsigned long size);
 
 extern void device_tree_init(void);
+
+static inline unsigned long pci_address_to_pio(phys_addr_t address)
+{
+       /*
+        * The ioport address can be directly used by inX() / outX()
+        */
+       BUG_ON(address > IO_SPACE_LIMIT);
+
+       return (unsigned long) address;
+}
+#define pci_address_to_pio pci_address_to_pio
+
+struct boot_param_header;
+
+extern void __dt_setup_arch(struct boot_param_header *bph);
+
+#define dt_setup_arch(sym)                                             \
+({                                                                     \
+       extern struct boot_param_header __dtb_##sym##_begin;            \
+                                                                       \
+       __dt_setup_arch(&__dtb_##sym##_begin);                          \
+})
+
 #else /* CONFIG_OF */
 static inline void device_tree_init(void) { }
 #endif /* CONFIG_OF */
index 6dce6d8d09ab08d77042c9b89a37da65e2c82f8d..2560b6b6a7d8f9b47ddc6bc607f80b732110dad3 100644 (file)
@@ -14,7 +14,8 @@ extern void *set_vi_handler(int n, vi_handler_t addr);
 
 extern void *set_except_vector(int n, void *addr);
 extern unsigned long ebase;
-extern void per_cpu_trap_init(void);
+extern void per_cpu_trap_init(bool);
+extern void cpu_cache_init(void);
 
 #endif /* __KERNEL__ */
 
index 7165333ad043b4ac22fa3304cb711f4e371e1cf0..4461198361c9760fa5afed8a101af0fa89207bc0 100644 (file)
@@ -6,7 +6,11 @@
  * SECTION_SIZE_BITS           2^N: how big each section will be
  * MAX_PHYSMEM_BITS            2^N: how much memory we can have in that space
  */
-#define SECTION_SIZE_BITS       28
+#if defined(CONFIG_HUGETLB_PAGE) && defined(CONFIG_PAGE_SIZE_64KB)
+# define SECTION_SIZE_BITS     29
+#else
+# define SECTION_SIZE_BITS     28
+#endif
 #define MAX_PHYSMEM_BITS        35
 
 #endif /* CONFIG_SPARSEMEM */
index 6e00f751ab6dc675b886d736e1f0c814136cfbd4..fe9a4c3ec5a1f2d9f557adf7c32348b0c892e374 100644 (file)
@@ -20,7 +20,7 @@ struct stat {
        long            st_pad1[3];             /* Reserved for network id */
        ino_t           st_ino;
        mode_t          st_mode;
-       nlink_t         st_nlink;
+       __u32           st_nlink;
        uid_t           st_uid;
        gid_t           st_gid;
        unsigned        st_rdev;
@@ -55,7 +55,7 @@ struct stat64 {
        unsigned long long      st_ino;
 
        mode_t          st_mode;
-       nlink_t         st_nlink;
+       __u32           st_nlink;
 
        uid_t           st_uid;
        gid_t           st_gid;
@@ -96,7 +96,7 @@ struct stat {
        unsigned long           st_ino;
 
        mode_t                  st_mode;
-       nlink_t                 st_nlink;
+       __u32                   st_nlink;
 
        uid_t                   st_uid;
        gid_t                   st_gid;
index 8f77f774a2a01e3b70fee2bae89a1b036c89fc6f..abdd87aaf609034742f0401e9846ea4ba2bd2ced 100644 (file)
@@ -60,7 +60,7 @@ struct termio {
 };
 
 #ifdef __KERNEL__
-#include <linux/module.h>
+#include <asm/uaccess.h>
 
 /*
  *     intr=^C         quit=^\         erase=del       kill=^U
index ff74aec3561a3f0a88829db33c15b87887fdcbc9..420ca06b2f42df601e8fcfbd5f148626f2df225d 100644 (file)
@@ -25,6 +25,7 @@ extern void (*board_nmi_handler_setup)(void);
 extern void (*board_ejtag_handler_setup)(void);
 extern void (*board_bind_eic_interrupt)(int irq, int regset);
 extern void (*board_ebase_setup)(void);
+extern void (*board_cache_error_setup)(void);
 
 extern int register_nmi_notifier(struct notifier_block *nb);
 
index 504d40aedfae670aff49644f1964e6a932b63ac6..440a21dab575705412805809c57ce31a893825b3 100644 (file)
@@ -11,7 +11,7 @@
 #include <linux/types.h>
 
 #ifdef CONFIG_EXPORT_UASM
-#include <linux/module.h>
+#include <linux/export.h>
 #define __uasminit
 #define __uasminitdata
 #define UASM_EXPORT_SYMBOL(sym) EXPORT_SYMBOL(sym)
index a9dff33212518d752b8620a969a79da3935543d1..e44abea9c209ef1c2e035373333e671a876b0093 100644 (file)
@@ -16,5 +16,3 @@ obj-$(CONFIG_JZ4740_QI_LB60)  += board-qi_lb60.o
 # PM support
 
 obj-$(CONFIG_PM) += pm.o
-
-ccflags-y := -Werror -Wall
index 5099201fb7bc9933f6b939697a5d93fc02520ea9..6ae7ce4ac63eb9b6a65ecf8ae0a49093579ae597 100644 (file)
@@ -340,7 +340,7 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu)
                __cpu_name[cpu] = "R2000";
                c->isa_level = MIPS_CPU_ISA_I;
                c->options = MIPS_CPU_TLB | MIPS_CPU_3K_CACHE |
-                            MIPS_CPU_NOFPUEX;
+                            MIPS_CPU_NOFPUEX;
                if (__cpu_has_fpu())
                        c->options |= MIPS_CPU_FPU;
                c->tlbsize = 64;
@@ -361,7 +361,7 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu)
                }
                c->isa_level = MIPS_CPU_ISA_I;
                c->options = MIPS_CPU_TLB | MIPS_CPU_3K_CACHE |
-                            MIPS_CPU_NOFPUEX;
+                            MIPS_CPU_NOFPUEX;
                if (__cpu_has_fpu())
                        c->options |= MIPS_CPU_FPU;
                c->tlbsize = 64;
@@ -387,8 +387,8 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu)
 
                c->isa_level = MIPS_CPU_ISA_III;
                c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_32FPR |
-                            MIPS_CPU_WATCH | MIPS_CPU_VCE |
-                            MIPS_CPU_LLSC;
+                            MIPS_CPU_WATCH | MIPS_CPU_VCE |
+                            MIPS_CPU_LLSC;
                c->tlbsize = 48;
                break;
        case PRID_IMP_VR41XX:
@@ -434,7 +434,7 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu)
                __cpu_name[cpu] = "R4300";
                c->isa_level = MIPS_CPU_ISA_III;
                c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_32FPR |
-                            MIPS_CPU_LLSC;
+                            MIPS_CPU_LLSC;
                c->tlbsize = 32;
                break;
        case PRID_IMP_R4600:
@@ -446,7 +446,7 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu)
                c->tlbsize = 48;
                break;
        #if 0
-       case PRID_IMP_R4650:
+       case PRID_IMP_R4650:
                /*
                 * This processor doesn't have an MMU, so it's not
                 * "real easy" to run Linux on it. It is left purely
@@ -455,9 +455,9 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu)
                 */
                c->cputype = CPU_R4650;
                __cpu_name[cpu] = "R4650";
-               c->isa_level = MIPS_CPU_ISA_III;
+               c->isa_level = MIPS_CPU_ISA_III;
                c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_LLSC;
-               c->tlbsize = 48;
+               c->tlbsize = 48;
                break;
        #endif
        case PRID_IMP_TX39:
@@ -488,7 +488,7 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu)
                __cpu_name[cpu] = "R4700";
                c->isa_level = MIPS_CPU_ISA_III;
                c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_32FPR |
-                            MIPS_CPU_LLSC;
+                            MIPS_CPU_LLSC;
                c->tlbsize = 48;
                break;
        case PRID_IMP_TX49:
@@ -505,7 +505,7 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu)
                __cpu_name[cpu] = "R5000";
                c->isa_level = MIPS_CPU_ISA_IV;
                c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_32FPR |
-                            MIPS_CPU_LLSC;
+                            MIPS_CPU_LLSC;
                c->tlbsize = 48;
                break;
        case PRID_IMP_R5432:
@@ -513,7 +513,7 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu)
                __cpu_name[cpu] = "R5432";
                c->isa_level = MIPS_CPU_ISA_IV;
                c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_32FPR |
-                            MIPS_CPU_WATCH | MIPS_CPU_LLSC;
+                            MIPS_CPU_WATCH | MIPS_CPU_LLSC;
                c->tlbsize = 48;
                break;
        case PRID_IMP_R5500:
@@ -521,7 +521,7 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu)
                __cpu_name[cpu] = "R5500";
                c->isa_level = MIPS_CPU_ISA_IV;
                c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_32FPR |
-                            MIPS_CPU_WATCH | MIPS_CPU_LLSC;
+                            MIPS_CPU_WATCH | MIPS_CPU_LLSC;
                c->tlbsize = 48;
                break;
        case PRID_IMP_NEVADA:
@@ -529,7 +529,7 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu)
                __cpu_name[cpu] = "Nevada";
                c->isa_level = MIPS_CPU_ISA_IV;
                c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_32FPR |
-                            MIPS_CPU_DIVEC | MIPS_CPU_LLSC;
+                            MIPS_CPU_DIVEC | MIPS_CPU_LLSC;
                c->tlbsize = 48;
                break;
        case PRID_IMP_R6000:
@@ -537,7 +537,7 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu)
                __cpu_name[cpu] = "R6000";
                c->isa_level = MIPS_CPU_ISA_II;
                c->options = MIPS_CPU_TLB | MIPS_CPU_FPU |
-                            MIPS_CPU_LLSC;
+                            MIPS_CPU_LLSC;
                c->tlbsize = 32;
                break;
        case PRID_IMP_R6000A:
@@ -545,7 +545,7 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu)
                __cpu_name[cpu] = "R6000A";
                c->isa_level = MIPS_CPU_ISA_II;
                c->options = MIPS_CPU_TLB | MIPS_CPU_FPU |
-                            MIPS_CPU_LLSC;
+                            MIPS_CPU_LLSC;
                c->tlbsize = 32;
                break;
        case PRID_IMP_RM7000:
@@ -553,7 +553,7 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu)
                __cpu_name[cpu] = "RM7000";
                c->isa_level = MIPS_CPU_ISA_IV;
                c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_32FPR |
-                            MIPS_CPU_LLSC;
+                            MIPS_CPU_LLSC;
                /*
                 * Undocumented RM7000:  Bit 29 in the info register of
                 * the RM7000 v2.0 indicates if the TLB has 48 or 64
@@ -569,7 +569,7 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu)
                __cpu_name[cpu] = "RM9000";
                c->isa_level = MIPS_CPU_ISA_IV;
                c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_32FPR |
-                            MIPS_CPU_LLSC;
+                            MIPS_CPU_LLSC;
                /*
                 * Bit 29 in the info register of the RM9000
                 * indicates if the TLB has 48 or 64 entries.
@@ -584,8 +584,8 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu)
                __cpu_name[cpu] = "RM8000";
                c->isa_level = MIPS_CPU_ISA_IV;
                c->options = MIPS_CPU_TLB | MIPS_CPU_4KEX |
-                            MIPS_CPU_FPU | MIPS_CPU_32FPR |
-                            MIPS_CPU_LLSC;
+                            MIPS_CPU_FPU | MIPS_CPU_32FPR |
+                            MIPS_CPU_LLSC;
                c->tlbsize = 384;      /* has weird TLB: 3-way x 128 */
                break;
        case PRID_IMP_R10000:
@@ -593,9 +593,9 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu)
                __cpu_name[cpu] = "R10000";
                c->isa_level = MIPS_CPU_ISA_IV;
                c->options = MIPS_CPU_TLB | MIPS_CPU_4K_CACHE | MIPS_CPU_4KEX |
-                            MIPS_CPU_FPU | MIPS_CPU_32FPR |
+                            MIPS_CPU_FPU | MIPS_CPU_32FPR |
                             MIPS_CPU_COUNTER | MIPS_CPU_WATCH |
-                            MIPS_CPU_LLSC;
+                            MIPS_CPU_LLSC;
                c->tlbsize = 64;
                break;
        case PRID_IMP_R12000:
@@ -603,9 +603,9 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu)
                __cpu_name[cpu] = "R12000";
                c->isa_level = MIPS_CPU_ISA_IV;
                c->options = MIPS_CPU_TLB | MIPS_CPU_4K_CACHE | MIPS_CPU_4KEX |
-                            MIPS_CPU_FPU | MIPS_CPU_32FPR |
+                            MIPS_CPU_FPU | MIPS_CPU_32FPR |
                             MIPS_CPU_COUNTER | MIPS_CPU_WATCH |
-                            MIPS_CPU_LLSC;
+                            MIPS_CPU_LLSC;
                c->tlbsize = 64;
                break;
        case PRID_IMP_R14000:
@@ -613,9 +613,9 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu)
                __cpu_name[cpu] = "R14000";
                c->isa_level = MIPS_CPU_ISA_IV;
                c->options = MIPS_CPU_TLB | MIPS_CPU_4K_CACHE | MIPS_CPU_4KEX |
-                            MIPS_CPU_FPU | MIPS_CPU_32FPR |
+                            MIPS_CPU_FPU | MIPS_CPU_32FPR |
                             MIPS_CPU_COUNTER | MIPS_CPU_WATCH |
-                            MIPS_CPU_LLSC;
+                            MIPS_CPU_LLSC;
                c->tlbsize = 64;
                break;
        case PRID_IMP_LOONGSON2:
@@ -739,7 +739,7 @@ static inline unsigned int decode_config3(struct cpuinfo_mips *c)
        if (config3 & MIPS_CONF3_VEIC)
                c->options |= MIPS_CPU_VEIC;
        if (config3 & MIPS_CONF3_MT)
-               c->ases |= MIPS_ASE_MIPSMT;
+               c->ases |= MIPS_ASE_MIPSMT;
        if (config3 & MIPS_CONF3_ULRI)
                c->options |= MIPS_CPU_ULRI;
 
@@ -767,7 +767,7 @@ static void __cpuinit decode_configs(struct cpuinfo_mips *c)
 
        /* MIPS32 or MIPS64 compliant CPU.  */
        c->options = MIPS_CPU_4KEX | MIPS_CPU_4K_CACHE | MIPS_CPU_COUNTER |
-                    MIPS_CPU_DIVEC | MIPS_CPU_LLSC | MIPS_CPU_MCHECK;
+                    MIPS_CPU_DIVEC | MIPS_CPU_LLSC | MIPS_CPU_MCHECK;
 
        c->scache.flags = MIPS_CACHE_NOT_PRESENT;
 
index ab73fa2fb9b5707343c6081b821faf9775b90a2f..f29099b104c497e5cb4b4a3d368a801fc6c60d05 100644 (file)
@@ -1532,7 +1532,8 @@ init_hw_perf_events(void)
                irq = MSC01E_INT_BASE + MSC01E_INT_PERFCTR;
        } else {
 #endif
-               if (cp0_perfcount_irq >= 0)
+               if ((cp0_perfcount_irq >= 0) &&
+                               (cp0_compare_irq != cp0_perfcount_irq))
                        irq = MIPS_CPU_IRQ_BASE + cp0_perfcount_irq;
                else
                        irq = -1;
index f8b2c592514de3293e219d92bb59939ec51e8ace..5542817c1b498869cef930f4bd09c7ce680fe130 100644 (file)
@@ -41,27 +41,27 @@ static int show_cpuinfo(struct seq_file *m, void *v)
 
        seq_printf(m, "processor\t\t: %ld\n", n);
        sprintf(fmt, "cpu model\t\t: %%s V%%d.%%d%s\n",
-               cpu_data[n].options & MIPS_CPU_FPU ? "  FPU V%d.%d" : "");
+                     cpu_data[n].options & MIPS_CPU_FPU ? "  FPU V%d.%d" : "");
        seq_printf(m, fmt, __cpu_name[n],
-                                  (version >> 4) & 0x0f, version & 0x0f,
-                                  (fp_vers >> 4) & 0x0f, fp_vers & 0x0f);
+                     (version >> 4) & 0x0f, version & 0x0f,
+                     (fp_vers >> 4) & 0x0f, fp_vers & 0x0f);
        seq_printf(m, "BogoMIPS\t\t: %u.%02u\n",
-                     cpu_data[n].udelay_val / (500000/HZ),
-                     (cpu_data[n].udelay_val / (5000/HZ)) % 100);
+                     cpu_data[n].udelay_val / (500000/HZ),
+                     (cpu_data[n].udelay_val / (5000/HZ)) % 100);
        seq_printf(m, "wait instruction\t: %s\n", cpu_wait ? "yes" : "no");
        seq_printf(m, "microsecond timers\t: %s\n",
-                     cpu_has_counter ? "yes" : "no");
+                     cpu_has_counter ? "yes" : "no");
        seq_printf(m, "tlb_entries\t\t: %d\n", cpu_data[n].tlbsize);
        seq_printf(m, "extra interrupt vector\t: %s\n",
-                     cpu_has_divec ? "yes" : "no");
+                     cpu_has_divec ? "yes" : "no");
        seq_printf(m, "hardware watchpoint\t: %s",
-                  cpu_has_watch ? "yes, " : "no\n");
+                     cpu_has_watch ? "yes, " : "no\n");
        if (cpu_has_watch) {
                seq_printf(m, "count: %d, address/irw mask: [",
-                          cpu_data[n].watch_reg_count);
+                     cpu_data[n].watch_reg_count);
                for (i = 0; i < cpu_data[n].watch_reg_count; i++)
                        seq_printf(m, "%s0x%04x", i ? ", " : "" ,
-                                  cpu_data[n].watch_reg_masks[i]);
+                               cpu_data[n].watch_reg_masks[i]);
                seq_printf(m, "]\n");
        }
        seq_printf(m, "ASEs implemented\t:%s%s%s%s%s%s\n",
@@ -73,13 +73,13 @@ static int show_cpuinfo(struct seq_file *m, void *v)
                      cpu_has_mipsmt ? " mt" : ""
                );
        seq_printf(m, "shadow register sets\t: %d\n",
-                      cpu_data[n].srsets);
+                     cpu_data[n].srsets);
        seq_printf(m, "kscratch registers\t: %d\n",
-                  hweight8(cpu_data[n].kscratch_mask));
+                     hweight8(cpu_data[n].kscratch_mask));
        seq_printf(m, "core\t\t\t: %d\n", cpu_data[n].core);
 
        sprintf(fmt, "VCE%%c exceptions\t\t: %s\n",
-               cpu_has_vce ? "%u" : "not available");
+                     cpu_has_vce ? "%u" : "not available");
        seq_printf(m, fmt, 'D', vced_count);
        seq_printf(m, fmt, 'I', vcei_count);
        seq_printf(m, "\n");
index 558b5395795df810d01c97ed36efa96fc9141eea..f11b2bbb826d223d10eefffb810c9bcdb67eb017 100644 (file)
@@ -95,3 +95,16 @@ void __init device_tree_init(void)
        /* free the space reserved for the dt blob */
        free_mem_mach(base, size);
 }
+
+void __init __dt_setup_arch(struct boot_param_header *bph)
+{
+       if (be32_to_cpu(bph->magic) != OF_DT_HEADER) {
+               pr_err("DTB has bad magic, ignoring builtin OF DTB\n");
+
+               return;
+       }
+
+       initial_boot_params = bph;
+
+       early_init_devtree(initial_boot_params);
+}
index c504b212f8f3f968ede65e514cbcc96168c7c330..a53f8ec37aac68beef41b905b88cf1fe92e805d9 100644 (file)
@@ -605,6 +605,8 @@ void __init setup_arch(char **cmdline_p)
 
        resource_init();
        plat_smp_setup();
+
+       cpu_cache_init();
 }
 
 unsigned long kernelsp[NR_CPUS];
index 10263b405981f0eb4604f1d38bd3887e10f1c129..9c60d09e62a71521007c3cae0894cdcf09f01424 100644 (file)
@@ -19,8 +19,6 @@
 #  define DEBUGP(fmt, args...)
 #endif
 
-#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
-
 /*
  * Determine which stack to use..
  */
index 17f6ee30ad0d604da53fca7da7569bf4006d3d35..f2c09cfc60ac338dc9300f3487bae83e48a8cbd1 100644 (file)
@@ -339,7 +339,6 @@ asmlinkage void sys_sigreturn(nabi_no_regargs struct pt_regs regs)
        if (__copy_from_user(&blocked, &frame->sf_mask, sizeof(blocked)))
                goto badframe;
 
-       sigdelsetmask(&blocked, ~_BLOCKABLE);
        set_current_blocked(&blocked);
 
        sig = restore_sigcontext(&regs, &frame->sf_sc);
@@ -375,7 +374,6 @@ asmlinkage void sys_rt_sigreturn(nabi_no_regargs struct pt_regs regs)
        if (__copy_from_user(&set, &frame->rs_uc.uc_sigmask, sizeof(set)))
                goto badframe;
 
-       sigdelsetmask(&set, ~_BLOCKABLE);
        set_current_blocked(&set);
 
        sig = restore_sigcontext(&regs, &frame->rs_uc.uc_mcontext);
@@ -514,9 +512,10 @@ struct mips_abi mips_abi = {
        .restart        = __NR_restart_syscall
 };
 
-static int handle_signal(unsigned long sig, siginfo_t *info,
-       struct k_sigaction *ka, sigset_t *oldset, struct pt_regs *regs)
+static void handle_signal(unsigned long sig, siginfo_t *info,
+       struct k_sigaction *ka, struct pt_regs *regs)
 {
+       sigset_t *oldset = sigmask_to_save();
        int ret;
        struct mips_abi *abi = current->thread.abi;
        void *vdso = current->mm->context.vdso;
@@ -550,17 +549,14 @@ static int handle_signal(unsigned long sig, siginfo_t *info,
                                       ka, regs, sig, oldset);
 
        if (ret)
-               return ret;
-
-       block_sigmask(ka, sig);
+               return;
 
-       return ret;
+       signal_delivered(sig, info, ka, regs, 0);
 }
 
 static void do_signal(struct pt_regs *regs)
 {
        struct k_sigaction ka;
-       sigset_t *oldset;
        siginfo_t info;
        int signr;
 
@@ -572,25 +568,10 @@ static void do_signal(struct pt_regs *regs)
        if (!user_mode(regs))
                return;
 
-       if (test_thread_flag(TIF_RESTORE_SIGMASK))
-               oldset = &current->saved_sigmask;
-       else
-               oldset = &current->blocked;
-
        signr = get_signal_to_deliver(&info, &ka, regs, NULL);
        if (signr > 0) {
                /* Whee!  Actually deliver the signal.  */
-               if (handle_signal(signr, &info, &ka, oldset, regs) == 0) {
-                       /*
-                        * A signal was successfully delivered; the saved
-                        * sigmask will have been stored in the signal frame,
-                        * and will be restored by sigreturn, so we can simply
-                        * clear the TIF_RESTORE_SIGMASK flag.
-                        */
-                       if (test_thread_flag(TIF_RESTORE_SIGMASK))
-                               clear_thread_flag(TIF_RESTORE_SIGMASK);
-               }
-
+               handle_signal(signr, &info, &ka, regs);
                return;
        }
 
@@ -614,10 +595,7 @@ static void do_signal(struct pt_regs *regs)
         * If there's no signal to deliver, we just put the saved sigmask
         * back
         */
-       if (test_thread_flag(TIF_RESTORE_SIGMASK)) {
-               clear_thread_flag(TIF_RESTORE_SIGMASK);
-               sigprocmask(SIG_SETMASK, &current->saved_sigmask, NULL);
-       }
+       restore_saved_sigmask();
 }
 
 /*
@@ -630,14 +608,12 @@ asmlinkage void do_notify_resume(struct pt_regs *regs, void *unused,
        local_irq_enable();
 
        /* deal with pending signal delivery */
-       if (thread_info_flags & (_TIF_SIGPENDING | _TIF_RESTORE_SIGMASK))
+       if (thread_info_flags & _TIF_SIGPENDING)
                do_signal(regs);
 
        if (thread_info_flags & _TIF_NOTIFY_RESUME) {
                clear_thread_flag(TIF_NOTIFY_RESUME);
                tracehook_notify_resume(regs);
-               if (current->replacement_session_keyring)
-                       key_replace_session_keyring();
        }
 }
 
index b4fe2eacbd5d258a55146f5d51154491119569e8..da1b56a39ac77815b4989183fd07821286468dfe 100644 (file)
@@ -465,7 +465,6 @@ asmlinkage void sys32_sigreturn(nabi_no_regargs struct pt_regs regs)
        if (__copy_conv_sigset_from_user(&blocked, &frame->sf_mask))
                goto badframe;
 
-       sigdelsetmask(&blocked, ~_BLOCKABLE);
        set_current_blocked(&blocked);
 
        sig = restore_sigcontext32(&regs, &frame->sf_sc);
@@ -503,7 +502,6 @@ asmlinkage void sys32_rt_sigreturn(nabi_no_regargs struct pt_regs regs)
        if (__copy_conv_sigset_from_user(&set, &frame->rs_uc.uc_sigmask))
                goto badframe;
 
-       sigdelsetmask(&set, ~_BLOCKABLE);
        set_current_blocked(&set);
 
        sig = restore_sigcontext32(&regs, &frame->rs_uc.uc_mcontext);
index 63ffac9af7c5b61fa9fa966e641889f935b7d747..3574c145511be486b0feb82177ced3c80679d180 100644 (file)
@@ -109,7 +109,6 @@ asmlinkage void sysn32_rt_sigreturn(nabi_no_regargs struct pt_regs regs)
        if (__copy_conv_sigset_from_user(&set, &frame->rs_uc.uc_sigmask))
                goto badframe;
 
-       sigdelsetmask(&set, ~_BLOCKABLE);
        set_current_blocked(&set);
 
        sig = restore_sigcontext(&regs, &frame->rs_uc.uc_mcontext);
index 71a95f55a6493be90e4695de4d4c948a0dfa465f..48650c8180401aeb1ce1c3f2713471d9c6e45536 100644 (file)
@@ -106,7 +106,7 @@ asmlinkage __cpuinit void start_secondary(void)
 #endif /* CONFIG_MIPS_MT_SMTC */
        cpu_probe();
        cpu_report();
-       per_cpu_trap_init();
+       per_cpu_trap_init(false);
        mips_clockevent_init();
        mp_ops->init_secondary();
 
index cfdaaa4cffc0a28fa60b662d6eccf992396ebe58..2d0c2a277f525b5b89b89500a5153c9032ed4ce3 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/compiler.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
+#include <linux/module.h>
 #include <linux/mm.h>
 #include <linux/sched.h>
 #include <linux/smp.h>
@@ -91,7 +92,7 @@ void (*board_nmi_handler_setup)(void);
 void (*board_ejtag_handler_setup)(void);
 void (*board_bind_eic_interrupt)(int irq, int regset);
 void (*board_ebase_setup)(void);
-
+void __cpuinitdata(*board_cache_error_setup)(void);
 
 static void show_raw_backtrace(unsigned long reg29)
 {
@@ -1490,7 +1491,6 @@ void *set_vi_handler(int n, vi_handler_t addr)
        return set_vi_srs_handler(n, addr, 0);
 }
 
-extern void cpu_cache_init(void);
 extern void tlb_init(void);
 extern void flush_tlb_handlers(void);
 
@@ -1517,7 +1517,7 @@ static int __init ulri_disable(char *s)
 }
 __setup("noulri", ulri_disable);
 
-void __cpuinit per_cpu_trap_init(void)
+void __cpuinit per_cpu_trap_init(bool is_boot_cpu)
 {
        unsigned int cpu = smp_processor_id();
        unsigned int status_set = ST0_CU0;
@@ -1616,7 +1616,9 @@ void __cpuinit per_cpu_trap_init(void)
 #ifdef CONFIG_MIPS_MT_SMTC
        if (bootTC) {
 #endif /* CONFIG_MIPS_MT_SMTC */
-               cpu_cache_init();
+               /* Boot CPU's cache setup in setup_arch(). */
+               if (!is_boot_cpu)
+                       cpu_cache_init();
                tlb_init();
 #ifdef CONFIG_MIPS_MT_SMTC
        } else if (!secondaryTC) {
@@ -1632,7 +1634,7 @@ void __cpuinit per_cpu_trap_init(void)
 }
 
 /* Install CPU exception handler */
-void __init set_handler(unsigned long offset, void *addr, unsigned long size)
+void __cpuinit set_handler(unsigned long offset, void *addr, unsigned long size)
 {
        memcpy((void *)(ebase + offset), addr, size);
        local_flush_icache_range(ebase + offset, ebase + offset + size);
@@ -1693,7 +1695,7 @@ void __init trap_init(void)
 
        if (board_ebase_setup)
                board_ebase_setup();
-       per_cpu_trap_init();
+       per_cpu_trap_init(true);
 
        /*
         * Copy the generic exception handlers to their final destination.
@@ -1797,6 +1799,9 @@ void __init trap_init(void)
 
        set_except_vector(26, handle_dsp);
 
+       if (board_cache_error_setup)
+               board_cache_error_setup();
+
        if (cpu_has_vce)
                /* Special exception: R4[04]00 uses also the divec space. */
                memcpy((void *)(ebase + 0x180), &except_vec3_r4000, 0x100);
index 3fccf2104513b7bbc1368313106ad1533a345a78..20bdf40b3efa7be98d3d1bac47ad30dec134452b 100644 (file)
@@ -16,8 +16,22 @@ config SOC_XWAY
        bool "XWAY"
        select SOC_TYPE_XWAY
        select HW_HAS_PCI
+
+config SOC_FALCON
+       bool "FALCON"
+
+endchoice
+
+choice
+       prompt "Devicetree"
+
+config DT_EASY50712
+       bool "Easy50712"
+       depends on SOC_XWAY
 endchoice
 
-source "arch/mips/lantiq/xway/Kconfig"
+config PCI_LANTIQ
+       bool "PCI Support"
+       depends on SOC_XWAY && PCI
 
 endif
index e5dae0e24b00f8319d09dd77e0461042a329b20f..d6bdc579419fb2bbc7bfc0e7390a321cb404c995 100644 (file)
@@ -4,8 +4,11 @@
 # under the terms of the GNU General Public License version 2 as published
 # by the Free Software Foundation.
 
-obj-y := irq.o setup.o clk.o prom.o devices.o
+obj-y := irq.o clk.o prom.o
+
+obj-y += dts/
 
 obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
 
 obj-$(CONFIG_SOC_TYPE_XWAY) += xway/
+obj-$(CONFIG_SOC_FALCON) += falcon/
index f3dff05722de63f02c30f6a67df56eb5304fd69a..b3ec49838fd7526f716c393cff823217b41fe099 100644 (file)
@@ -6,3 +6,4 @@ platform-$(CONFIG_LANTIQ)       += lantiq/
 cflags-$(CONFIG_LANTIQ)                += -I$(srctree)/arch/mips/include/asm/mach-lantiq
 load-$(CONFIG_LANTIQ)          = 0xffffffff80002000
 cflags-$(CONFIG_SOC_TYPE_XWAY) += -I$(srctree)/arch/mips/include/asm/mach-lantiq/xway
+cflags-$(CONFIG_SOC_FALCON)    += -I$(srctree)/arch/mips/include/asm/mach-lantiq/falcon
index 412814fdd3ee239ce5f7cfc6e6c7f6ead93ed9fa..d3bcc33f4699ae7dcf8036bf70f0a6be19293d70 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/kernel.h>
 #include <linux/types.h>
 #include <linux/clk.h>
+#include <linux/clkdev.h>
 #include <linux/err.h>
 #include <linux/list.h>
 
 #include <lantiq_soc.h>
 
 #include "clk.h"
+#include "prom.h"
 
-struct clk {
-       const char *name;
-       unsigned long rate;
-       unsigned long (*get_rate) (void);
-};
+/* lantiq socs have 3 static clocks */
+static struct clk cpu_clk_generic[3];
 
-static struct clk *cpu_clk;
-static int cpu_clk_cnt;
+void clkdev_add_static(unsigned long cpu, unsigned long fpi, unsigned long io)
+{
+       cpu_clk_generic[0].rate = cpu;
+       cpu_clk_generic[1].rate = fpi;
+       cpu_clk_generic[2].rate = io;
+}
 
-/* lantiq socs have 3 static clocks */
-static struct clk cpu_clk_generic[] = {
-       {
-               .name = "cpu",
-               .get_rate = ltq_get_cpu_hz,
-       }, {
-               .name = "fpi",
-               .get_rate = ltq_get_fpi_hz,
-       }, {
-               .name = "io",
-               .get_rate = ltq_get_io_region_clock,
-       },
-};
-
-static struct resource ltq_cgu_resource = {
-       .name   = "cgu",
-       .start  = LTQ_CGU_BASE_ADDR,
-       .end    = LTQ_CGU_BASE_ADDR + LTQ_CGU_SIZE - 1,
-       .flags  = IORESOURCE_MEM,
-};
-
-/* remapped clock register range */
-void __iomem *ltq_cgu_membase;
-
-void clk_init(void)
+struct clk *clk_get_cpu(void)
+{
+       return &cpu_clk_generic[0];
+}
+
+struct clk *clk_get_fpi(void)
+{
+       return &cpu_clk_generic[1];
+}
+EXPORT_SYMBOL_GPL(clk_get_fpi);
+
+struct clk *clk_get_io(void)
 {
-       cpu_clk = cpu_clk_generic;
-       cpu_clk_cnt = ARRAY_SIZE(cpu_clk_generic);
+       return &cpu_clk_generic[2];
 }
 
 static inline int clk_good(struct clk *clk)
@@ -82,38 +71,71 @@ unsigned long clk_get_rate(struct clk *clk)
 }
 EXPORT_SYMBOL(clk_get_rate);
 
-struct clk *clk_get(struct device *dev, const char *id)
+int clk_set_rate(struct clk *clk, unsigned long rate)
 {
-       int i;
-
-       for (i = 0; i < cpu_clk_cnt; i++)
-               if (!strcmp(id, cpu_clk[i].name))
-                       return &cpu_clk[i];
-       BUG();
-       return ERR_PTR(-ENOENT);
-}
-EXPORT_SYMBOL(clk_get);
-
-void clk_put(struct clk *clk)
-{
-       /* not used */
+       if (unlikely(!clk_good(clk)))
+               return 0;
+       if (clk->rates && *clk->rates) {
+               unsigned long *r = clk->rates;
+
+               while (*r && (*r != rate))
+                       r++;
+               if (!*r) {
+                       pr_err("clk %s.%s: trying to set invalid rate %ld\n",
+                               clk->cl.dev_id, clk->cl.con_id, rate);
+                       return -1;
+               }
+       }
+       clk->rate = rate;
+       return 0;
 }
-EXPORT_SYMBOL(clk_put);
+EXPORT_SYMBOL(clk_set_rate);
 
 int clk_enable(struct clk *clk)
 {
-       /* not used */
-       return 0;
+       if (unlikely(!clk_good(clk)))
+               return -1;
+
+       if (clk->enable)
+               return clk->enable(clk);
+
+       return -1;
 }
 EXPORT_SYMBOL(clk_enable);
 
 void clk_disable(struct clk *clk)
 {
-       /* not used */
+       if (unlikely(!clk_good(clk)))
+               return;
+
+       if (clk->disable)
+               clk->disable(clk);
 }
 EXPORT_SYMBOL(clk_disable);
 
-static inline u32 ltq_get_counter_resolution(void)
+int clk_activate(struct clk *clk)
+{
+       if (unlikely(!clk_good(clk)))
+               return -1;
+
+       if (clk->activate)
+               return clk->activate(clk);
+
+       return -1;
+}
+EXPORT_SYMBOL(clk_activate);
+
+void clk_deactivate(struct clk *clk)
+{
+       if (unlikely(!clk_good(clk)))
+               return;
+
+       if (clk->deactivate)
+               clk->deactivate(clk);
+}
+EXPORT_SYMBOL(clk_deactivate);
+
+static inline u32 get_counter_resolution(void)
 {
        u32 res;
 
@@ -133,21 +155,11 @@ void __init plat_time_init(void)
 {
        struct clk *clk;
 
-       if (insert_resource(&iomem_resource, &ltq_cgu_resource) < 0)
-               panic("Failed to insert cgu memory");
+       ltq_soc_init();
 
-       if (request_mem_region(ltq_cgu_resource.start,
-                       resource_size(&ltq_cgu_resource), "cgu") < 0)
-               panic("Failed to request cgu memory");
-
-       ltq_cgu_membase = ioremap_nocache(ltq_cgu_resource.start,
-                               resource_size(&ltq_cgu_resource));
-       if (!ltq_cgu_membase) {
-               pr_err("Failed to remap cgu memory\n");
-               unreachable();
-       }
-       clk = clk_get(0, "cpu");
-       mips_hpt_frequency = clk_get_rate(clk) / ltq_get_counter_resolution();
+       clk = clk_get_cpu();
+       mips_hpt_frequency = clk_get_rate(clk) / get_counter_resolution();
        write_c0_compare(read_c0_count());
+       pr_info("CPU Clock: %ldMHz\n", clk_get_rate(clk) / 1000000);
        clk_put(clk);
 }
index 3328925f2c3f260ad3c887bb66d50f2d084d12ee..fa670602b91b509713f2a58e93647d903e26eb81 100644 (file)
@@ -9,10 +9,70 @@
 #ifndef _LTQ_CLK_H__
 #define _LTQ_CLK_H__
 
-extern void clk_init(void);
+#include <linux/clkdev.h>
 
-extern unsigned long ltq_get_cpu_hz(void);
-extern unsigned long ltq_get_fpi_hz(void);
-extern unsigned long ltq_get_io_region_clock(void);
+/* clock speeds */
+#define CLOCK_33M      33333333
+#define CLOCK_60M      60000000
+#define CLOCK_62_5M    62500000
+#define CLOCK_83M      83333333
+#define CLOCK_83_5M    83500000
+#define CLOCK_98_304M  98304000
+#define CLOCK_100M     100000000
+#define CLOCK_111M     111111111
+#define CLOCK_125M     125000000
+#define CLOCK_133M     133333333
+#define CLOCK_150M     150000000
+#define CLOCK_166M     166666666
+#define CLOCK_167M     166666667
+#define CLOCK_196_608M 196608000
+#define CLOCK_200M     200000000
+#define CLOCK_250M     250000000
+#define CLOCK_266M     266666666
+#define CLOCK_300M     300000000
+#define CLOCK_333M     333333333
+#define CLOCK_393M     393215332
+#define CLOCK_400M     400000000
+#define CLOCK_500M     500000000
+#define CLOCK_600M     600000000
+
+/* clock out speeds */
+#define CLOCK_32_768K  32768
+#define CLOCK_1_536M   1536000
+#define CLOCK_2_5M     2500000
+#define CLOCK_12M      12000000
+#define CLOCK_24M      24000000
+#define CLOCK_25M      25000000
+#define CLOCK_30M      30000000
+#define CLOCK_40M      40000000
+#define CLOCK_48M      48000000
+#define CLOCK_50M      50000000
+#define CLOCK_60M      60000000
+
+struct clk {
+       struct clk_lookup cl;
+       unsigned long rate;
+       unsigned long *rates;
+       unsigned int module;
+       unsigned int bits;
+       unsigned long (*get_rate) (void);
+       int (*enable) (struct clk *clk);
+       void (*disable) (struct clk *clk);
+       int (*activate) (struct clk *clk);
+       void (*deactivate) (struct clk *clk);
+       void (*reboot) (struct clk *clk);
+};
+
+extern void clkdev_add_static(unsigned long cpu, unsigned long fpi,
+                               unsigned long io);
+
+extern unsigned long ltq_danube_cpu_hz(void);
+extern unsigned long ltq_danube_fpi_hz(void);
+
+extern unsigned long ltq_ar9_cpu_hz(void);
+extern unsigned long ltq_ar9_fpi_hz(void);
+
+extern unsigned long ltq_vr9_cpu_hz(void);
+extern unsigned long ltq_vr9_fpi_hz(void);
 
 #endif
diff --git a/arch/mips/lantiq/devices.c b/arch/mips/lantiq/devices.c
deleted file mode 100644 (file)
index de1cb2b..0000000
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- *  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.
- *
- *  Copyright (C) 2010 John Crispin <blogic@openwrt.org>
- */
-
-#include <linux/init.h>
-#include <linux/export.h>
-#include <linux/types.h>
-#include <linux/string.h>
-#include <linux/kernel.h>
-#include <linux/reboot.h>
-#include <linux/platform_device.h>
-#include <linux/leds.h>
-#include <linux/etherdevice.h>
-#include <linux/time.h>
-#include <linux/io.h>
-#include <linux/gpio.h>
-
-#include <asm/bootinfo.h>
-#include <asm/irq.h>
-
-#include <lantiq_soc.h>
-
-#include "devices.h"
-
-/* nor flash */
-static struct resource ltq_nor_resource = {
-       .name   = "nor",
-       .start  = LTQ_FLASH_START,
-       .end    = LTQ_FLASH_START + LTQ_FLASH_MAX - 1,
-       .flags  = IORESOURCE_MEM,
-};
-
-static struct platform_device ltq_nor = {
-       .name           = "ltq_nor",
-       .resource       = &ltq_nor_resource,
-       .num_resources  = 1,
-};
-
-void __init ltq_register_nor(struct physmap_flash_data *data)
-{
-       ltq_nor.dev.platform_data = data;
-       platform_device_register(&ltq_nor);
-}
-
-/* watchdog */
-static struct resource ltq_wdt_resource = {
-       .name   = "watchdog",
-       .start  = LTQ_WDT_BASE_ADDR,
-       .end    = LTQ_WDT_BASE_ADDR + LTQ_WDT_SIZE - 1,
-       .flags  = IORESOURCE_MEM,
-};
-
-void __init ltq_register_wdt(void)
-{
-       platform_device_register_simple("ltq_wdt", 0, &ltq_wdt_resource, 1);
-}
-
-/* asc ports */
-static struct resource ltq_asc0_resources[] = {
-       {
-               .name   = "asc0",
-               .start  = LTQ_ASC0_BASE_ADDR,
-               .end    = LTQ_ASC0_BASE_ADDR + LTQ_ASC_SIZE - 1,
-               .flags  = IORESOURCE_MEM,
-       },
-       IRQ_RES(tx, LTQ_ASC_TIR(0)),
-       IRQ_RES(rx, LTQ_ASC_RIR(0)),
-       IRQ_RES(err, LTQ_ASC_EIR(0)),
-};
-
-static struct resource ltq_asc1_resources[] = {
-       {
-               .name   = "asc1",
-               .start  = LTQ_ASC1_BASE_ADDR,
-               .end    = LTQ_ASC1_BASE_ADDR + LTQ_ASC_SIZE - 1,
-               .flags  = IORESOURCE_MEM,
-       },
-       IRQ_RES(tx, LTQ_ASC_TIR(1)),
-       IRQ_RES(rx, LTQ_ASC_RIR(1)),
-       IRQ_RES(err, LTQ_ASC_EIR(1)),
-};
-
-void __init ltq_register_asc(int port)
-{
-       switch (port) {
-       case 0:
-               platform_device_register_simple("ltq_asc", 0,
-                       ltq_asc0_resources, ARRAY_SIZE(ltq_asc0_resources));
-               break;
-       case 1:
-               platform_device_register_simple("ltq_asc", 1,
-                       ltq_asc1_resources, ARRAY_SIZE(ltq_asc1_resources));
-               break;
-       default:
-               break;
-       }
-}
-
-#ifdef CONFIG_PCI
-/* pci */
-static struct platform_device ltq_pci = {
-       .name           = "ltq_pci",
-       .num_resources  = 0,
-};
-
-void __init ltq_register_pci(struct ltq_pci_data *data)
-{
-       ltq_pci.dev.platform_data = data;
-       platform_device_register(&ltq_pci);
-}
-#else
-void __init ltq_register_pci(struct ltq_pci_data *data)
-{
-       pr_err("kernel is compiled without PCI support\n");
-}
-#endif
diff --git a/arch/mips/lantiq/devices.h b/arch/mips/lantiq/devices.h
deleted file mode 100644 (file)
index 2947bb1..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- *  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.
- *
- *  Copyright (C) 2010 John Crispin <blogic@openwrt.org>
- */
-
-#ifndef _LTQ_DEVICES_H__
-#define _LTQ_DEVICES_H__
-
-#include <lantiq_platform.h>
-#include <linux/mtd/physmap.h>
-
-#define IRQ_RES(resname, irq) \
-       {.name = #resname, .start = (irq), .flags = IORESOURCE_IRQ}
-
-extern void ltq_register_nor(struct physmap_flash_data *data);
-extern void ltq_register_wdt(void);
-extern void ltq_register_asc(int port);
-extern void ltq_register_pci(struct ltq_pci_data *data);
-
-#endif
diff --git a/arch/mips/lantiq/dts/Makefile b/arch/mips/lantiq/dts/Makefile
new file mode 100644 (file)
index 0000000..674fca4
--- /dev/null
@@ -0,0 +1,4 @@
+obj-$(CONFIG_DT_EASY50712) := easy50712.dtb.o
+
+$(obj)/%.dtb: $(obj)/%.dts
+       $(call if_changed,dtc)
diff --git a/arch/mips/lantiq/dts/danube.dtsi b/arch/mips/lantiq/dts/danube.dtsi
new file mode 100644 (file)
index 0000000..3a4520f
--- /dev/null
@@ -0,0 +1,105 @@
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+       compatible = "lantiq,xway", "lantiq,danube";
+
+       cpus {
+               cpu@0 {
+                       compatible = "mips,mips24Kc";
+               };
+       };
+
+       biu@1F800000 {
+               #address-cells = <1>;
+               #size-cells = <1>;
+               compatible = "lantiq,biu", "simple-bus";
+               reg = <0x1F800000 0x800000>;
+               ranges = <0x0 0x1F800000 0x7FFFFF>;
+
+               icu0: icu@80200 {
+                       #interrupt-cells = <1>;
+                       interrupt-controller;
+                       compatible = "lantiq,icu";
+                       reg = <0x80200 0x120>;
+               };
+
+               watchdog@803F0 {
+                       compatible = "lantiq,wdt";
+                       reg = <0x803F0 0x10>;
+               };
+       };
+
+       sram@1F000000 {
+               #address-cells = <1>;
+               #size-cells = <1>;
+               compatible = "lantiq,sram";
+               reg = <0x1F000000 0x800000>;
+               ranges = <0x0 0x1F000000 0x7FFFFF>;
+
+               eiu0: eiu@101000 {
+                       #interrupt-cells = <1>;
+                       interrupt-controller;
+                       interrupt-parent;
+                       compatible = "lantiq,eiu-xway";
+                       reg = <0x101000 0x1000>;
+               };
+
+               pmu0: pmu@102000 {
+                       compatible = "lantiq,pmu-xway";
+                       reg = <0x102000 0x1000>;
+               };
+
+               cgu0: cgu@103000 {
+                       compatible = "lantiq,cgu-xway";
+                       reg = <0x103000 0x1000>;
+                       #clock-cells = <1>;
+               };
+
+               rcu0: rcu@203000 {
+                       compatible = "lantiq,rcu-xway";
+                       reg = <0x203000 0x1000>;
+               };
+       };
+
+       fpi@10000000 {
+               #address-cells = <1>;
+               #size-cells = <1>;
+               compatible = "lantiq,fpi", "simple-bus";
+               ranges = <0x0 0x10000000 0xEEFFFFF>;
+               reg = <0x10000000 0xEF00000>;
+
+               gptu@E100A00 {
+                       compatible = "lantiq,gptu-xway";
+                       reg = <0xE100A00 0x100>;
+               };
+
+               serial@E100C00 {
+                       compatible = "lantiq,asc";
+                       reg = <0xE100C00 0x400>;
+                       interrupt-parent = <&icu0>;
+                       interrupts = <112 113 114>;
+               };
+
+               dma0: dma@E104100 {
+                       compatible = "lantiq,dma-xway";
+                       reg = <0xE104100 0x800>;
+               };
+
+               ebu0: ebu@E105300 {
+                       compatible = "lantiq,ebu-xway";
+                       reg = <0xE105300 0x100>;
+               };
+
+               pci0: pci@E105400 {
+                       #address-cells = <3>;
+                       #size-cells = <2>;
+                       #interrupt-cells = <1>;
+                       compatible = "lantiq,pci-xway";
+                       bus-range = <0x0 0x0>;
+                       ranges = <0x2000000 0 0x8000000 0x8000000 0 0x2000000   /* pci memory */
+                                 0x1000000 0 0x00000000 0xAE00000 0 0x200000>; /* io space */
+                       reg = <0x7000000 0x8000         /* config space */
+                               0xE105400 0x400>;       /* pci bridge */
+               };
+       };
+};
diff --git a/arch/mips/lantiq/dts/easy50712.dts b/arch/mips/lantiq/dts/easy50712.dts
new file mode 100644 (file)
index 0000000..68c1731
--- /dev/null
@@ -0,0 +1,113 @@
+/dts-v1/;
+
+/include/ "danube.dtsi"
+
+/ {
+       chosen {
+               bootargs = "console=ttyLTQ0,115200 init=/etc/preinit";
+       };
+
+       memory@0 {
+               reg = <0x0 0x2000000>;
+       };
+
+       fpi@10000000 {
+               #address-cells = <1>;
+               #size-cells = <1>;
+               localbus@0 {
+                       #address-cells = <2>;
+                       #size-cells = <1>;
+                       ranges = <0 0 0x0 0x3ffffff /* addrsel0 */
+                               1 0 0x4000000 0x4000010>; /* addsel1 */
+                       compatible = "lantiq,localbus", "simple-bus";
+
+                       nor-boot@0 {
+                               compatible = "lantiq,nor";
+                               bank-width = <2>;
+                               reg = <0 0x0 0x2000000>;
+                               #address-cells = <1>;
+                               #size-cells = <1>;
+
+                               partition@0 {
+                                       label = "uboot";
+                                       reg = <0x00000 0x10000>; /* 64 KB */
+                               };
+
+                               partition@10000 {
+                                       label = "uboot_env";
+                                       reg = <0x10000 0x10000>; /* 64 KB */
+                               };
+
+                               partition@20000 {
+                                       label = "linux";
+                                       reg = <0x20000 0x3d0000>;
+                               };
+
+                               partition@400000 {
+                                       label = "rootfs";
+                                       reg = <0x400000 0x400000>;
+                               };
+                       };
+               };
+
+               gpio: pinmux@E100B10 {
+                       compatible = "lantiq,pinctrl-xway";
+                       pinctrl-names = "default";
+                       pinctrl-0 = <&state_default>;
+
+                       #gpio-cells = <2>;
+                       gpio-controller;
+                       reg = <0xE100B10 0xA0>;
+
+                       state_default: pinmux {
+                               stp {
+                                       lantiq,groups = "stp";
+                                       lantiq,function = "stp";
+                               };
+                               exin {
+                                       lantiq,groups = "exin1";
+                                       lantiq,function = "exin";
+                               };
+                               pci {
+                                       lantiq,groups = "gnt1";
+                                       lantiq,function = "pci";
+                               };
+                               conf_out {
+                                       lantiq,pins = "io4", "io5", "io6"; /* stp */
+                                       lantiq,open-drain;
+                                       lantiq,pull = <0>;
+                               };
+                       };
+               };
+
+               etop@E180000 {
+                       compatible = "lantiq,etop-xway";
+                       reg = <0xE180000 0x40000>;
+                       interrupt-parent = <&icu0>;
+                       interrupts = <73 78>;
+                       phy-mode = "rmii";
+                       mac-address = [ 00 11 22 33 44 55 ];
+               };
+
+               stp0: stp@E100BB0 {
+                       #gpio-cells = <2>;
+                       compatible = "lantiq,gpio-stp-xway";
+                       gpio-controller;
+                       reg = <0xE100BB0 0x40>;
+
+                       lantiq,shadow = <0xfff>;
+                       lantiq,groups = <0x3>;
+               };
+
+               pci@E105400 {
+                       lantiq,bus-clock = <33333333>;
+                       interrupt-map-mask = <0xf800 0x0 0x0 0x7>;
+                       interrupt-map = <
+                                0x7000 0 0 1 &icu0 29 1 // slot 14, irq 29
+                       >;
+                       gpios-reset = <&gpio 21 0>;
+                       req-mask = <0x1>;               /* GNT1 */
+               };
+
+       };
+};
index 972e05f8763193f903edfa64c3135d05beaedf33..9b28d0940ef4c9c444ae811f3ba55d6aa01ea45b 100644 (file)
@@ -6,17 +6,16 @@
  *  Copyright (C) 2010 John Crispin <blogic@openwrt.org>
  */
 
-#include <linux/init.h>
 #include <linux/cpu.h>
-
-#include <lantiq.h>
 #include <lantiq_soc.h>
 
-/* no ioremap possible at this early stage, lets use KSEG1 instead  */
-#define LTQ_ASC_BASE   KSEG1ADDR(LTQ_ASC1_BASE_ADDR)
 #define ASC_BUF                1024
-#define LTQ_ASC_FSTAT  ((u32 *)(LTQ_ASC_BASE + 0x0048))
-#define LTQ_ASC_TBUF   ((u32 *)(LTQ_ASC_BASE + 0x0020))
+#define LTQ_ASC_FSTAT  ((u32 *)(LTQ_EARLY_ASC + 0x0048))
+#ifdef __BIG_ENDIAN
+#define LTQ_ASC_TBUF   ((u32 *)(LTQ_EARLY_ASC + 0x0020 + 3))
+#else
+#define LTQ_ASC_TBUF   ((u32 *)(LTQ_EARLY_ASC + 0x0020))
+#endif
 #define TXMASK         0x3F00
 #define TXOFFSET       8
 
@@ -27,7 +26,7 @@ void prom_putchar(char c)
        local_irq_save(flags);
        do { } while ((ltq_r32(LTQ_ASC_FSTAT) & TXMASK) >> TXOFFSET);
        if (c == '\n')
-               ltq_w32('\r', LTQ_ASC_TBUF);
-       ltq_w32(c, LTQ_ASC_TBUF);
+               ltq_w8('\r', LTQ_ASC_TBUF);
+       ltq_w8(c, LTQ_ASC_TBUF);
        local_irq_restore(flags);
 }
diff --git a/arch/mips/lantiq/falcon/Makefile b/arch/mips/lantiq/falcon/Makefile
new file mode 100644 (file)
index 0000000..ff220f9
--- /dev/null
@@ -0,0 +1 @@
+obj-y := prom.o reset.o sysctrl.o
diff --git a/arch/mips/lantiq/falcon/prom.c b/arch/mips/lantiq/falcon/prom.c
new file mode 100644 (file)
index 0000000..c1d278f
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * 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.
+ *
+ * Copyright (C) 2012 Thomas Langer <thomas.langer@lantiq.com>
+ * Copyright (C) 2012 John Crispin <blogic@openwrt.org>
+ */
+
+#include <linux/kernel.h>
+#include <asm/io.h>
+
+#include <lantiq_soc.h>
+
+#include "../prom.h"
+
+#define SOC_FALCON     "Falcon"
+#define SOC_FALCON_D   "Falcon-D"
+#define SOC_FALCON_V   "Falcon-V"
+#define SOC_FALCON_M   "Falcon-M"
+
+#define COMP_FALCON    "lantiq,falcon"
+
+#define PART_SHIFT     12
+#define PART_MASK      0x0FFFF000
+#define REV_SHIFT      28
+#define REV_MASK       0xF0000000
+#define SREV_SHIFT     22
+#define SREV_MASK      0x03C00000
+#define TYPE_SHIFT     26
+#define TYPE_MASK      0x3C000000
+
+/* reset, nmi and ejtag exception vectors */
+#define BOOT_REG_BASE  (KSEG1 | 0x1F200000)
+#define BOOT_RVEC      (BOOT_REG_BASE | 0x00)
+#define BOOT_NVEC      (BOOT_REG_BASE | 0x04)
+#define BOOT_EVEC      (BOOT_REG_BASE | 0x08)
+
+void __init ltq_soc_nmi_setup(void)
+{
+       extern void (*nmi_handler)(void);
+
+       ltq_w32((unsigned long)&nmi_handler, (void *)BOOT_NVEC);
+}
+
+void __init ltq_soc_ejtag_setup(void)
+{
+       extern void (*ejtag_debug_handler)(void);
+
+       ltq_w32((unsigned long)&ejtag_debug_handler, (void *)BOOT_EVEC);
+}
+
+void __init ltq_soc_detect(struct ltq_soc_info *i)
+{
+       u32 type;
+       i->partnum = (ltq_r32(FALCON_CHIPID) & PART_MASK) >> PART_SHIFT;
+       i->rev = (ltq_r32(FALCON_CHIPID) & REV_MASK) >> REV_SHIFT;
+       i->srev = ((ltq_r32(FALCON_CHIPCONF) & SREV_MASK) >> SREV_SHIFT);
+       i->compatible = COMP_FALCON;
+       i->type = SOC_TYPE_FALCON;
+       sprintf(i->rev_type, "%c%d%d", (i->srev & 0x4) ? ('B') : ('A'),
+               i->rev & 0x7, (i->srev & 0x3) + 1);
+
+       switch (i->partnum) {
+       case SOC_ID_FALCON:
+               type = (ltq_r32(FALCON_CHIPTYPE) & TYPE_MASK) >> TYPE_SHIFT;
+               switch (type) {
+               case 0:
+                       i->name = SOC_FALCON_D;
+                       break;
+               case 1:
+                       i->name = SOC_FALCON_V;
+                       break;
+               case 2:
+                       i->name = SOC_FALCON_M;
+                       break;
+               default:
+                       i->name = SOC_FALCON;
+                       break;
+               }
+               break;
+
+       default:
+               unreachable();
+               break;
+       }
+}
diff --git a/arch/mips/lantiq/falcon/reset.c b/arch/mips/lantiq/falcon/reset.c
new file mode 100644 (file)
index 0000000..5682482
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * 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.
+ *
+ * Copyright (C) 2012 Thomas Langer <thomas.langer@lantiq.com>
+ * Copyright (C) 2012 John Crispin <blogic@openwrt.org>
+ */
+
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/pm.h>
+#include <asm/reboot.h>
+#include <linux/export.h>
+
+#include <lantiq_soc.h>
+
+/* CPU0 Reset Source Register */
+#define SYS1_CPU0RS            0x0040
+/* reset cause mask */
+#define CPU0RS_MASK            0x0003
+/* CPU0 Boot Mode Register */
+#define SYS1_BM                        0x00a0
+/* boot mode mask */
+#define BM_MASK                        0x0005
+
+/* allow platform code to find out what surce we booted from */
+unsigned char ltq_boot_select(void)
+{
+       return ltq_sys1_r32(SYS1_BM) & BM_MASK;
+}
+
+/* allow the watchdog driver to find out what the boot reason was */
+int ltq_reset_cause(void)
+{
+       return ltq_sys1_r32(SYS1_CPU0RS) & CPU0RS_MASK;
+}
+EXPORT_SYMBOL_GPL(ltq_reset_cause);
+
+#define BOOT_REG_BASE  (KSEG1 | 0x1F200000)
+#define BOOT_PW1_REG   (BOOT_REG_BASE | 0x20)
+#define BOOT_PW2_REG   (BOOT_REG_BASE | 0x24)
+#define BOOT_PW1       0x4C545100
+#define BOOT_PW2       0x0051544C
+
+#define WDT_REG_BASE   (KSEG1 | 0x1F8803F0)
+#define WDT_PW1                0x00BE0000
+#define WDT_PW2                0x00DC0000
+
+static void machine_restart(char *command)
+{
+       local_irq_disable();
+
+       /* reboot magic */
+       ltq_w32(BOOT_PW1, (void *)BOOT_PW1_REG); /* 'LTQ\0' */
+       ltq_w32(BOOT_PW2, (void *)BOOT_PW2_REG); /* '\0QTL' */
+       ltq_w32(0, (void *)BOOT_REG_BASE); /* reset Bootreg RVEC */
+
+       /* watchdog magic */
+       ltq_w32(WDT_PW1, (void *)WDT_REG_BASE);
+       ltq_w32(WDT_PW2 |
+               (0x3 << 26) | /* PWL */
+               (0x2 << 24) | /* CLKDIV */
+               (0x1 << 31) | /* enable */
+               (1), /* reload */
+               (void *)WDT_REG_BASE);
+       unreachable();
+}
+
+static void machine_halt(void)
+{
+       local_irq_disable();
+       unreachable();
+}
+
+static void machine_power_off(void)
+{
+       local_irq_disable();
+       unreachable();
+}
+
+static int __init mips_reboot_setup(void)
+{
+       _machine_restart = machine_restart;
+       _machine_halt = machine_halt;
+       pm_power_off = machine_power_off;
+       return 0;
+}
+
+arch_initcall(mips_reboot_setup);
diff --git a/arch/mips/lantiq/falcon/sysctrl.c b/arch/mips/lantiq/falcon/sysctrl.c
new file mode 100644 (file)
index 0000000..ba0123d
--- /dev/null
@@ -0,0 +1,260 @@
+/*
+ * 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.
+ *
+ * Copyright (C) 2011 Thomas Langer <thomas.langer@lantiq.com>
+ * Copyright (C) 2011 John Crispin <blogic@openwrt.org>
+ */
+
+#include <linux/ioport.h>
+#include <linux/export.h>
+#include <linux/clkdev.h>
+#include <linux/of_address.h>
+#include <asm/delay.h>
+
+#include <lantiq_soc.h>
+
+#include "../clk.h"
+
+/* infrastructure control register */
+#define SYS1_INFRAC            0x00bc
+/* Configuration fuses for drivers and pll */
+#define STATUS_CONFIG          0x0040
+
+/* GPE frequency selection */
+#define GPPC_OFFSET            24
+#define GPEFREQ_MASK           0x00000C0
+#define GPEFREQ_OFFSET         10
+/* Clock status register */
+#define SYSCTL_CLKS            0x0000
+/* Clock enable register */
+#define SYSCTL_CLKEN           0x0004
+/* Clock clear register */
+#define SYSCTL_CLKCLR          0x0008
+/* Activation Status Register */
+#define SYSCTL_ACTS            0x0020
+/* Activation Register */
+#define SYSCTL_ACT             0x0024
+/* Deactivation Register */
+#define SYSCTL_DEACT           0x0028
+/* reboot Register */
+#define SYSCTL_RBT             0x002c
+/* CPU0 Clock Control Register */
+#define SYS1_CPU0CC            0x0040
+/* HRST_OUT_N Control Register */
+#define SYS1_HRSTOUTC          0x00c0
+/* clock divider bit */
+#define CPU0CC_CPUDIV          0x0001
+
+/* Activation Status Register */
+#define ACTS_ASC1_ACT  0x00000800
+#define ACTS_I2C_ACT   0x00004000
+#define ACTS_P0                0x00010000
+#define ACTS_P1                0x00010000
+#define ACTS_P2                0x00020000
+#define ACTS_P3                0x00020000
+#define ACTS_P4                0x00040000
+#define ACTS_PADCTRL0  0x00100000
+#define ACTS_PADCTRL1  0x00100000
+#define ACTS_PADCTRL2  0x00200000
+#define ACTS_PADCTRL3  0x00200000
+#define ACTS_PADCTRL4  0x00400000
+
+#define sysctl_w32(m, x, y)    ltq_w32((x), sysctl_membase[m] + (y))
+#define sysctl_r32(m, x)       ltq_r32(sysctl_membase[m] + (x))
+#define sysctl_w32_mask(m, clear, set, reg)    \
+               sysctl_w32(m, (sysctl_r32(m, reg) & ~(clear)) | (set), reg)
+
+#define status_w32(x, y)       ltq_w32((x), status_membase + (y))
+#define status_r32(x)          ltq_r32(status_membase + (x))
+
+static void __iomem *sysctl_membase[3], *status_membase;
+void __iomem *ltq_sys1_membase, *ltq_ebu_membase;
+
+void falcon_trigger_hrst(int level)
+{
+       sysctl_w32(SYSCTL_SYS1, level & 1, SYS1_HRSTOUTC);
+}
+
+static inline void sysctl_wait(struct clk *clk,
+               unsigned int test, unsigned int reg)
+{
+       int err = 1000000;
+
+       do {} while (--err && ((sysctl_r32(clk->module, reg)
+                                       & clk->bits) != test));
+       if (!err)
+               pr_err("module de/activation failed %d %08X %08X %08X\n",
+                       clk->module, clk->bits, test,
+                       sysctl_r32(clk->module, reg) & clk->bits);
+}
+
+static int sysctl_activate(struct clk *clk)
+{
+       sysctl_w32(clk->module, clk->bits, SYSCTL_CLKEN);
+       sysctl_w32(clk->module, clk->bits, SYSCTL_ACT);
+       sysctl_wait(clk, clk->bits, SYSCTL_ACTS);
+       return 0;
+}
+
+static void sysctl_deactivate(struct clk *clk)
+{
+       sysctl_w32(clk->module, clk->bits, SYSCTL_CLKCLR);
+       sysctl_w32(clk->module, clk->bits, SYSCTL_DEACT);
+       sysctl_wait(clk, 0, SYSCTL_ACTS);
+}
+
+static int sysctl_clken(struct clk *clk)
+{
+       sysctl_w32(clk->module, clk->bits, SYSCTL_CLKEN);
+       sysctl_wait(clk, clk->bits, SYSCTL_CLKS);
+       return 0;
+}
+
+static void sysctl_clkdis(struct clk *clk)
+{
+       sysctl_w32(clk->module, clk->bits, SYSCTL_CLKCLR);
+       sysctl_wait(clk, 0, SYSCTL_CLKS);
+}
+
+static void sysctl_reboot(struct clk *clk)
+{
+       unsigned int act;
+       unsigned int bits;
+
+       act = sysctl_r32(clk->module, SYSCTL_ACT);
+       bits = ~act & clk->bits;
+       if (bits != 0) {
+               sysctl_w32(clk->module, bits, SYSCTL_CLKEN);
+               sysctl_w32(clk->module, bits, SYSCTL_ACT);
+               sysctl_wait(clk, bits, SYSCTL_ACTS);
+       }
+       sysctl_w32(clk->module, act & clk->bits, SYSCTL_RBT);
+       sysctl_wait(clk, clk->bits, SYSCTL_ACTS);
+}
+
+/* enable the ONU core */
+static void falcon_gpe_enable(void)
+{
+       unsigned int freq;
+       unsigned int status;
+
+       /* if if the clock is already enabled */
+       status = sysctl_r32(SYSCTL_SYS1, SYS1_INFRAC);
+       if (status & (1 << (GPPC_OFFSET + 1)))
+               return;
+
+       if (status_r32(STATUS_CONFIG) == 0)
+               freq = 1; /* use 625MHz on unfused chip */
+       else
+               freq = (status_r32(STATUS_CONFIG) &
+                       GPEFREQ_MASK) >>
+                       GPEFREQ_OFFSET;
+
+       /* apply new frequency */
+       sysctl_w32_mask(SYSCTL_SYS1, 7 << (GPPC_OFFSET + 1),
+               freq << (GPPC_OFFSET + 2) , SYS1_INFRAC);
+       udelay(1);
+
+       /* enable new frequency */
+       sysctl_w32_mask(SYSCTL_SYS1, 0, 1 << (GPPC_OFFSET + 1), SYS1_INFRAC);
+       udelay(1);
+}
+
+static inline void clkdev_add_sys(const char *dev, unsigned int module,
+                                       unsigned int bits)
+{
+       struct clk *clk = kzalloc(sizeof(struct clk), GFP_KERNEL);
+
+       clk->cl.dev_id = dev;
+       clk->cl.con_id = NULL;
+       clk->cl.clk = clk;
+       clk->module = module;
+       clk->activate = sysctl_activate;
+       clk->deactivate = sysctl_deactivate;
+       clk->enable = sysctl_clken;
+       clk->disable = sysctl_clkdis;
+       clk->reboot = sysctl_reboot;
+       clkdev_add(&clk->cl);
+}
+
+void __init ltq_soc_init(void)
+{
+       struct device_node *np_status =
+               of_find_compatible_node(NULL, NULL, "lantiq,status-falcon");
+       struct device_node *np_ebu =
+               of_find_compatible_node(NULL, NULL, "lantiq,ebu-falcon");
+       struct device_node *np_sys1 =
+               of_find_compatible_node(NULL, NULL, "lantiq,sys1-falcon");
+       struct device_node *np_syseth =
+               of_find_compatible_node(NULL, NULL, "lantiq,syseth-falcon");
+       struct device_node *np_sysgpe =
+               of_find_compatible_node(NULL, NULL, "lantiq,sysgpe-falcon");
+       struct resource res_status, res_ebu, res_sys[3];
+       int i;
+
+       /* check if all the core register ranges are available */
+       if (!np_status || !np_ebu || !np_sys1 || !np_syseth || !np_sysgpe)
+               panic("Failed to load core nodes from devicetree");
+
+       if (of_address_to_resource(np_status, 0, &res_status) ||
+                       of_address_to_resource(np_ebu, 0, &res_ebu) ||
+                       of_address_to_resource(np_sys1, 0, &res_sys[0]) ||
+                       of_address_to_resource(np_syseth, 0, &res_sys[1]) ||
+                       of_address_to_resource(np_sysgpe, 0, &res_sys[2]))
+               panic("Failed to get core resources");
+
+       if ((request_mem_region(res_status.start, resource_size(&res_status),
+                               res_status.name) < 0) ||
+               (request_mem_region(res_ebu.start, resource_size(&res_ebu),
+                               res_ebu.name) < 0) ||
+               (request_mem_region(res_sys[0].start,
+                               resource_size(&res_sys[0]),
+                               res_sys[0].name) < 0) ||
+               (request_mem_region(res_sys[1].start,
+                               resource_size(&res_sys[1]),
+                               res_sys[1].name) < 0) ||
+               (request_mem_region(res_sys[2].start,
+                               resource_size(&res_sys[2]),
+                               res_sys[2].name) < 0))
+               pr_err("Failed to request core reources");
+
+       status_membase = ioremap_nocache(res_status.start,
+                                       resource_size(&res_status));
+       ltq_ebu_membase = ioremap_nocache(res_ebu.start,
+                                       resource_size(&res_ebu));
+
+       if (!status_membase || !ltq_ebu_membase)
+               panic("Failed to remap core resources");
+
+       for (i = 0; i < 3; i++) {
+               sysctl_membase[i] = ioremap_nocache(res_sys[i].start,
+                                               resource_size(&res_sys[i]));
+               if (!sysctl_membase[i])
+                       panic("Failed to remap sysctrl resources");
+       }
+       ltq_sys1_membase = sysctl_membase[0];
+
+       falcon_gpe_enable();
+
+       /* get our 3 static rates for cpu, fpi and io clocks */
+       if (ltq_sys1_r32(SYS1_CPU0CC) & CPU0CC_CPUDIV)
+               clkdev_add_static(CLOCK_200M, CLOCK_100M, CLOCK_200M);
+       else
+               clkdev_add_static(CLOCK_400M, CLOCK_100M, CLOCK_200M);
+
+       /* add our clock domains */
+       clkdev_add_sys("1d810000.gpio", SYSCTL_SYSETH, ACTS_P0);
+       clkdev_add_sys("1d810100.gpio", SYSCTL_SYSETH, ACTS_P2);
+       clkdev_add_sys("1e800100.gpio", SYSCTL_SYS1, ACTS_P1);
+       clkdev_add_sys("1e800200.gpio", SYSCTL_SYS1, ACTS_P3);
+       clkdev_add_sys("1e800300.gpio", SYSCTL_SYS1, ACTS_P4);
+       clkdev_add_sys("1db01000.pad", SYSCTL_SYSETH, ACTS_PADCTRL0);
+       clkdev_add_sys("1db02000.pad", SYSCTL_SYSETH, ACTS_PADCTRL2);
+       clkdev_add_sys("1e800400.pad", SYSCTL_SYS1, ACTS_PADCTRL1);
+       clkdev_add_sys("1e800500.pad", SYSCTL_SYS1, ACTS_PADCTRL3);
+       clkdev_add_sys("1e800600.pad", SYSCTL_SYS1, ACTS_PADCTRL4);
+       clkdev_add_sys("1e100C00.serial", SYSCTL_SYS1, ACTS_ASC1_ACT);
+       clkdev_add_sys("1e200000.i2c", SYSCTL_SYS1, ACTS_I2C_ACT);
+}
index d673731c538a4d7c6328d00d8249b1290d51dd91..57c1a4e51408c9800e644e8a299c4e8c9dd340de 100644 (file)
@@ -9,6 +9,11 @@
 
 #include <linux/interrupt.h>
 #include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/irqdomain.h>
+#include <linux/of_platform.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
 
 #include <asm/bootinfo.h>
 #include <asm/irq_cpu.h>
@@ -16,7 +21,7 @@
 #include <lantiq_soc.h>
 #include <irq.h>
 
-/* register definitions */
+/* register definitions - internal irqs */
 #define LTQ_ICU_IM0_ISR                0x0000
 #define LTQ_ICU_IM0_IER                0x0008
 #define LTQ_ICU_IM0_IOSR       0x0010
@@ -25,6 +30,7 @@
 #define LTQ_ICU_IM1_ISR                0x0028
 #define LTQ_ICU_OFFSET         (LTQ_ICU_IM1_ISR - LTQ_ICU_IM0_ISR)
 
+/* register definitions - external irqs */
 #define LTQ_EIU_EXIN_C         0x0000
 #define LTQ_EIU_EXIN_INIC      0x0004
 #define LTQ_EIU_EXIN_INEN      0x000C
 #define LTQ_EIU_IR4            (INT_NUM_IM1_IRL0 + 1)
 #define LTQ_EIU_IR5            (INT_NUM_IM1_IRL0 + 2)
 #define LTQ_EIU_IR6            (INT_NUM_IM2_IRL0 + 30)
-
+#define XWAY_EXIN_COUNT                3
 #define MAX_EIU                        6
 
-/* irqs generated by device attached to the EBU need to be acked in
+/* the performance counter */
+#define LTQ_PERF_IRQ           (INT_NUM_IM4_IRL0 + 31)
+
+/*
+ * irqs generated by devices attached to the EBU need to be acked in
  * a special manner
  */
 #define LTQ_ICU_EBU_IRQ                22
 #define ltq_eiu_w32(x, y)      ltq_w32((x), ltq_eiu_membase + (y))
 #define ltq_eiu_r32(x)         ltq_r32(ltq_eiu_membase + (x))
 
+/* our 2 ipi interrupts for VSMP */
+#define MIPS_CPU_IPI_RESCHED_IRQ       0
+#define MIPS_CPU_IPI_CALL_IRQ          1
+
+/* we have a cascade of 8 irqs */
+#define MIPS_CPU_IRQ_CASCADE           8
+
+#if defined(CONFIG_MIPS_MT_SMP) || defined(CONFIG_MIPS_MT_SMTC)
+int gic_present;
+#endif
+
 static unsigned short ltq_eiu_irq[MAX_EIU] = {
        LTQ_EIU_IR0,
        LTQ_EIU_IR1,
@@ -60,64 +81,51 @@ static unsigned short ltq_eiu_irq[MAX_EIU] = {
        LTQ_EIU_IR5,
 };
 
-static struct resource ltq_icu_resource = {
-       .name   = "icu",
-       .start  = LTQ_ICU_BASE_ADDR,
-       .end    = LTQ_ICU_BASE_ADDR + LTQ_ICU_SIZE - 1,
-       .flags  = IORESOURCE_MEM,
-};
-
-static struct resource ltq_eiu_resource = {
-       .name   = "eiu",
-       .start  = LTQ_EIU_BASE_ADDR,
-       .end    = LTQ_EIU_BASE_ADDR + LTQ_ICU_SIZE - 1,
-       .flags  = IORESOURCE_MEM,
-};
-
+static int exin_avail;
 static void __iomem *ltq_icu_membase;
 static void __iomem *ltq_eiu_membase;
 
 void ltq_disable_irq(struct irq_data *d)
 {
        u32 ier = LTQ_ICU_IM0_IER;
-       int irq_nr = d->irq - INT_NUM_IRQ0;
+       int offset = d->hwirq - MIPS_CPU_IRQ_CASCADE;
 
-       ier += LTQ_ICU_OFFSET * (irq_nr / INT_NUM_IM_OFFSET);
-       irq_nr %= INT_NUM_IM_OFFSET;
-       ltq_icu_w32(ltq_icu_r32(ier) & ~(1 << irq_nr), ier);
+       ier += LTQ_ICU_OFFSET * (offset / INT_NUM_IM_OFFSET);
+       offset %= INT_NUM_IM_OFFSET;
+       ltq_icu_w32(ltq_icu_r32(ier) & ~BIT(offset), ier);
 }
 
 void ltq_mask_and_ack_irq(struct irq_data *d)
 {
        u32 ier = LTQ_ICU_IM0_IER;
        u32 isr = LTQ_ICU_IM0_ISR;
-       int irq_nr = d->irq - INT_NUM_IRQ0;
+       int offset = d->hwirq - MIPS_CPU_IRQ_CASCADE;
 
-       ier += LTQ_ICU_OFFSET * (irq_nr / INT_NUM_IM_OFFSET);
-       isr += LTQ_ICU_OFFSET * (irq_nr / INT_NUM_IM_OFFSET);
-       irq_nr %= INT_NUM_IM_OFFSET;
-       ltq_icu_w32(ltq_icu_r32(ier) & ~(1 << irq_nr), ier);
-       ltq_icu_w32((1 << irq_nr), isr);
+       ier += LTQ_ICU_OFFSET * (offset / INT_NUM_IM_OFFSET);
+       isr += LTQ_ICU_OFFSET * (offset / INT_NUM_IM_OFFSET);
+       offset %= INT_NUM_IM_OFFSET;
+       ltq_icu_w32(ltq_icu_r32(ier) & ~BIT(offset), ier);
+       ltq_icu_w32(BIT(offset), isr);
 }
 
 static void ltq_ack_irq(struct irq_data *d)
 {
        u32 isr = LTQ_ICU_IM0_ISR;
-       int irq_nr = d->irq - INT_NUM_IRQ0;
+       int offset = d->hwirq - MIPS_CPU_IRQ_CASCADE;
 
-       isr += LTQ_ICU_OFFSET * (irq_nr / INT_NUM_IM_OFFSET);
-       irq_nr %= INT_NUM_IM_OFFSET;
-       ltq_icu_w32((1 << irq_nr), isr);
+       isr += LTQ_ICU_OFFSET * (offset / INT_NUM_IM_OFFSET);
+       offset %= INT_NUM_IM_OFFSET;
+       ltq_icu_w32(BIT(offset), isr);
 }
 
 void ltq_enable_irq(struct irq_data *d)
 {
        u32 ier = LTQ_ICU_IM0_IER;
-       int irq_nr = d->irq - INT_NUM_IRQ0;
+       int offset = d->hwirq - MIPS_CPU_IRQ_CASCADE;
 
-       ier += LTQ_ICU_OFFSET  * (irq_nr / INT_NUM_IM_OFFSET);
-       irq_nr %= INT_NUM_IM_OFFSET;
-       ltq_icu_w32(ltq_icu_r32(ier) | (1 << irq_nr), ier);
+       ier += LTQ_ICU_OFFSET  * (offset / INT_NUM_IM_OFFSET);
+       offset %= INT_NUM_IM_OFFSET;
+       ltq_icu_w32(ltq_icu_r32(ier) | BIT(offset), ier);
 }
 
 static unsigned int ltq_startup_eiu_irq(struct irq_data *d)
@@ -126,15 +134,15 @@ static unsigned int ltq_startup_eiu_irq(struct irq_data *d)
 
        ltq_enable_irq(d);
        for (i = 0; i < MAX_EIU; i++) {
-               if (d->irq == ltq_eiu_irq[i]) {
+               if (d->hwirq == ltq_eiu_irq[i]) {
                        /* low level - we should really handle set_type */
                        ltq_eiu_w32(ltq_eiu_r32(LTQ_EIU_EXIN_C) |
                                (0x6 << (i * 4)), LTQ_EIU_EXIN_C);
                        /* clear all pending */
-                       ltq_eiu_w32(ltq_eiu_r32(LTQ_EIU_EXIN_INIC) & ~(1 << i),
+                       ltq_eiu_w32(ltq_eiu_r32(LTQ_EIU_EXIN_INIC) & ~BIT(i),
                                LTQ_EIU_EXIN_INIC);
                        /* enable */
-                       ltq_eiu_w32(ltq_eiu_r32(LTQ_EIU_EXIN_INEN) | (1 << i),
+                       ltq_eiu_w32(ltq_eiu_r32(LTQ_EIU_EXIN_INEN) | BIT(i),
                                LTQ_EIU_EXIN_INEN);
                        break;
                }
@@ -149,9 +157,9 @@ static void ltq_shutdown_eiu_irq(struct irq_data *d)
 
        ltq_disable_irq(d);
        for (i = 0; i < MAX_EIU; i++) {
-               if (d->irq == ltq_eiu_irq[i]) {
+               if (d->hwirq == ltq_eiu_irq[i]) {
                        /* disable */
-                       ltq_eiu_w32(ltq_eiu_r32(LTQ_EIU_EXIN_INEN) & ~(1 << i),
+                       ltq_eiu_w32(ltq_eiu_r32(LTQ_EIU_EXIN_INEN) & ~BIT(i),
                                LTQ_EIU_EXIN_INEN);
                        break;
                }
@@ -188,14 +196,15 @@ static void ltq_hw_irqdispatch(int module)
        if (irq == 0)
                return;
 
-       /* silicon bug causes only the msb set to 1 to be valid. all
+       /*
+        * silicon bug causes only the msb set to 1 to be valid. all
         * other bits might be bogus
         */
        irq = __fls(irq);
-       do_IRQ((int)irq + INT_NUM_IM0_IRL0 + (INT_NUM_IM_OFFSET * module));
+       do_IRQ((int)irq + MIPS_CPU_IRQ_CASCADE + (INT_NUM_IM_OFFSET * module));
 
        /* if this is a EBU irq, we need to ack it or get a deadlock */
-       if ((irq == LTQ_ICU_EBU_IRQ) && (module == 0))
+       if ((irq == LTQ_ICU_EBU_IRQ) && (module == 0) && LTQ_EBU_PCC_ISTAT)
                ltq_ebu_w32(ltq_ebu_r32(LTQ_EBU_PCC_ISTAT) | 0x10,
                        LTQ_EBU_PCC_ISTAT);
 }
@@ -216,6 +225,47 @@ static void ltq_hw5_irqdispatch(void)
        do_IRQ(MIPS_CPU_TIMER_IRQ);
 }
 
+#ifdef CONFIG_MIPS_MT_SMP
+void __init arch_init_ipiirq(int irq, struct irqaction *action)
+{
+       setup_irq(irq, action);
+       irq_set_handler(irq, handle_percpu_irq);
+}
+
+static void ltq_sw0_irqdispatch(void)
+{
+       do_IRQ(MIPS_CPU_IRQ_BASE + MIPS_CPU_IPI_RESCHED_IRQ);
+}
+
+static void ltq_sw1_irqdispatch(void)
+{
+       do_IRQ(MIPS_CPU_IRQ_BASE + MIPS_CPU_IPI_CALL_IRQ);
+}
+static irqreturn_t ipi_resched_interrupt(int irq, void *dev_id)
+{
+       scheduler_ipi();
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t ipi_call_interrupt(int irq, void *dev_id)
+{
+       smp_call_function_interrupt();
+       return IRQ_HANDLED;
+}
+
+static struct irqaction irq_resched = {
+       .handler        = ipi_resched_interrupt,
+       .flags          = IRQF_PERCPU,
+       .name           = "IPI_resched"
+};
+
+static struct irqaction irq_call = {
+       .handler        = ipi_call_interrupt,
+       .flags          = IRQF_PERCPU,
+       .name           = "IPI_call"
+};
+#endif
+
 asmlinkage void plat_irq_dispatch(void)
 {
        unsigned int pending = read_c0_status() & read_c0_cause() & ST0_IM;
@@ -238,45 +288,75 @@ out:
        return;
 }
 
+static int icu_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw)
+{
+       struct irq_chip *chip = &ltq_irq_type;
+       int i;
+
+       for (i = 0; i < exin_avail; i++)
+               if (hw == ltq_eiu_irq[i])
+                       chip = &ltq_eiu_type;
+
+       irq_set_chip_and_handler(hw, chip, handle_level_irq);
+
+       return 0;
+}
+
+static const struct irq_domain_ops irq_domain_ops = {
+       .xlate = irq_domain_xlate_onetwocell,
+       .map = icu_map,
+};
+
 static struct irqaction cascade = {
        .handler = no_action,
        .name = "cascade",
 };
 
-void __init arch_init_irq(void)
+int __init icu_of_init(struct device_node *node, struct device_node *parent)
 {
+       struct device_node *eiu_node;
+       struct resource res;
        int i;
 
-       if (insert_resource(&iomem_resource, &ltq_icu_resource) < 0)
-               panic("Failed to insert icu memory");
+       if (of_address_to_resource(node, 0, &res))
+               panic("Failed to get icu memory range");
 
-       if (request_mem_region(ltq_icu_resource.start,
-                       resource_size(&ltq_icu_resource), "icu") < 0)
-               panic("Failed to request icu memory");
+       if (request_mem_region(res.start, resource_size(&res), res.name) < 0)
+               pr_err("Failed to request icu memory");
 
-       ltq_icu_membase = ioremap_nocache(ltq_icu_resource.start,
-                               resource_size(&ltq_icu_resource));
+       ltq_icu_membase = ioremap_nocache(res.start, resource_size(&res));
        if (!ltq_icu_membase)
                panic("Failed to remap icu memory");
 
-       if (insert_resource(&iomem_resource, &ltq_eiu_resource) < 0)
-               panic("Failed to insert eiu memory");
-
-       if (request_mem_region(ltq_eiu_resource.start,
-                       resource_size(&ltq_eiu_resource), "eiu") < 0)
-               panic("Failed to request eiu memory");
-
-       ltq_eiu_membase = ioremap_nocache(ltq_eiu_resource.start,
-                               resource_size(&ltq_eiu_resource));
-       if (!ltq_eiu_membase)
-               panic("Failed to remap eiu memory");
+       /* the external interrupts are optional and xway only */
+       eiu_node = of_find_compatible_node(NULL, NULL, "lantiq,eiu");
+       if (eiu_node && of_address_to_resource(eiu_node, 0, &res)) {
+               /* find out how many external irq sources we have */
+               const __be32 *count = of_get_property(node,
+                                                       "lantiq,count", NULL);
+
+               if (count)
+                       exin_avail = *count;
+               if (exin_avail > MAX_EIU)
+                       exin_avail = MAX_EIU;
+
+               if (request_mem_region(res.start, resource_size(&res),
+                                                       res.name) < 0)
+                       pr_err("Failed to request eiu memory");
+
+               ltq_eiu_membase = ioremap_nocache(res.start,
+                                                       resource_size(&res));
+               if (!ltq_eiu_membase)
+                       panic("Failed to remap eiu memory");
+       }
 
-       /* make sure all irqs are turned off by default */
-       for (i = 0; i < 5; i++)
+       /* turn off all irqs by default */
+       for (i = 0; i < 5; i++) {
+               /* make sure all irqs are turned off by default */
                ltq_icu_w32(0, LTQ_ICU_IM0_IER + (i * LTQ_ICU_OFFSET));
-
-       /* clear all possibly pending interrupts */
-       ltq_icu_w32(~0, LTQ_ICU_IM0_ISR + (i * LTQ_ICU_OFFSET));
+               /* clear all possibly pending interrupts */
+               ltq_icu_w32(~0, LTQ_ICU_IM0_ISR + (i * LTQ_ICU_OFFSET));
+       }
 
        mips_cpu_irq_init();
 
@@ -293,20 +373,19 @@ void __init arch_init_irq(void)
                set_vi_handler(7, ltq_hw5_irqdispatch);
        }
 
-       for (i = INT_NUM_IRQ0;
-               i <= (INT_NUM_IRQ0 + (5 * INT_NUM_IM_OFFSET)); i++)
-               if ((i == LTQ_EIU_IR0) || (i == LTQ_EIU_IR1) ||
-                       (i == LTQ_EIU_IR2))
-                       irq_set_chip_and_handler(i, &ltq_eiu_type,
-                               handle_level_irq);
-               /* EIU3-5 only exist on ar9 and vr9 */
-               else if (((i == LTQ_EIU_IR3) || (i == LTQ_EIU_IR4) ||
-                       (i == LTQ_EIU_IR5)) && (ltq_is_ar9() || ltq_is_vr9()))
-                       irq_set_chip_and_handler(i, &ltq_eiu_type,
-                               handle_level_irq);
-               else
-                       irq_set_chip_and_handler(i, &ltq_irq_type,
-                               handle_level_irq);
+       irq_domain_add_linear(node, 6 * INT_NUM_IM_OFFSET,
+               &irq_domain_ops, 0);
+
+#if defined(CONFIG_MIPS_MT_SMP)
+       if (cpu_has_vint) {
+               pr_info("Setting up IPI vectored interrupts\n");
+               set_vi_handler(MIPS_CPU_IPI_RESCHED_IRQ, ltq_sw0_irqdispatch);
+               set_vi_handler(MIPS_CPU_IPI_CALL_IRQ, ltq_sw1_irqdispatch);
+       }
+       arch_init_ipiirq(MIPS_CPU_IRQ_BASE + MIPS_CPU_IPI_RESCHED_IRQ,
+               &irq_resched);
+       arch_init_ipiirq(MIPS_CPU_IRQ_BASE + MIPS_CPU_IPI_CALL_IRQ, &irq_call);
+#endif
 
 #if !defined(CONFIG_MIPS_MT_SMP) && !defined(CONFIG_MIPS_MT_SMTC)
        set_c0_status(IE_IRQ0 | IE_IRQ1 | IE_IRQ2 |
@@ -315,9 +394,23 @@ void __init arch_init_irq(void)
        set_c0_status(IE_SW0 | IE_SW1 | IE_IRQ0 | IE_IRQ1 |
                IE_IRQ2 | IE_IRQ3 | IE_IRQ4 | IE_IRQ5);
 #endif
+
+       /* tell oprofile which irq to use */
+       cp0_perfcount_irq = LTQ_PERF_IRQ;
+       return 0;
 }
 
 unsigned int __cpuinit get_c0_compare_int(void)
 {
        return CP0_LEGACY_COMPARE_IRQ;
 }
+
+static struct of_device_id __initdata of_irq_ids[] = {
+       { .compatible = "lantiq,icu", .data = icu_of_init },
+       {},
+};
+
+void __init arch_init_irq(void)
+{
+       of_irq_init(of_irq_ids);
+}
diff --git a/arch/mips/lantiq/machtypes.h b/arch/mips/lantiq/machtypes.h
deleted file mode 100644 (file)
index 7e01b8c..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *  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.
- *
- *  Copyright (C) 2010 John Crispin <blogic@openwrt.org>
- */
-
-#ifndef _LANTIQ_MACH_H__
-#define _LANTIQ_MACH_H__
-
-#include <asm/mips_machine.h>
-
-enum lantiq_mach_type {
-       LTQ_MACH_GENERIC = 0,
-       LTQ_MACH_EASY50712,     /* Danube evaluation board */
-       LTQ_MACH_EASY50601,     /* Amazon SE evaluation board */
-};
-
-#endif
index e34fcfd0d5ca5763983c2b0a248352275b40f49b..d185e8477fdf2a8eb38d73ff74ed2c94acb894e8 100644 (file)
@@ -8,6 +8,7 @@
 
 #include <linux/export.h>
 #include <linux/clk.h>
+#include <linux/of_platform.h>
 #include <asm/bootinfo.h>
 #include <asm/time.h>
 
 #include "prom.h"
 #include "clk.h"
 
-static struct ltq_soc_info soc_info;
-
-unsigned int ltq_get_cpu_ver(void)
-{
-       return soc_info.rev;
-}
-EXPORT_SYMBOL(ltq_get_cpu_ver);
+/* access to the ebu needs to be locked between different drivers */
+DEFINE_SPINLOCK(ebu_lock);
+EXPORT_SYMBOL_GPL(ebu_lock);
 
-unsigned int ltq_get_soc_type(void)
-{
-       return soc_info.type;
-}
-EXPORT_SYMBOL(ltq_get_soc_type);
+/*
+ * this struct is filled by the soc specific detection code and holds
+ * information about the specific soc type, revision and name
+ */
+static struct ltq_soc_info soc_info;
 
 const char *get_system_type(void)
 {
@@ -45,27 +42,62 @@ static void __init prom_init_cmdline(void)
        char **argv = (char **) KSEG1ADDR(fw_arg1);
        int i;
 
+       arcs_cmdline[0] = '\0';
+
        for (i = 0; i < argc; i++) {
-               char *p = (char *)  KSEG1ADDR(argv[i]);
+               char *p = (char *) KSEG1ADDR(argv[i]);
 
-               if (p && *p) {
+               if (CPHYSADDR(p) && *p) {
                        strlcat(arcs_cmdline, p, sizeof(arcs_cmdline));
                        strlcat(arcs_cmdline, " ", sizeof(arcs_cmdline));
                }
        }
 }
 
-void __init prom_init(void)
+void __init plat_mem_setup(void)
 {
-       struct clk *clk;
+       ioport_resource.start = IOPORT_RESOURCE_START;
+       ioport_resource.end = IOPORT_RESOURCE_END;
+       iomem_resource.start = IOMEM_RESOURCE_START;
+       iomem_resource.end = IOMEM_RESOURCE_END;
+
+       set_io_port_base((unsigned long) KSEG1);
 
+       /*
+        * Load the builtin devicetree. This causes the chosen node to be
+        * parsed resulting in our memory appearing
+        */
+       __dt_setup_arch(&__dtb_start);
+}
+
+void __init prom_init(void)
+{
+       /* call the soc specific detetcion code and get it to fill soc_info */
        ltq_soc_detect(&soc_info);
-       clk_init();
-       clk = clk_get(0, "cpu");
-       snprintf(soc_info.sys_type, LTQ_SYS_TYPE_LEN - 1, "%s rev1.%d",
-               soc_info.name, soc_info.rev);
-       clk_put(clk);
+       snprintf(soc_info.sys_type, LTQ_SYS_TYPE_LEN - 1, "%s rev %s",
+               soc_info.name, soc_info.rev_type);
        soc_info.sys_type[LTQ_SYS_TYPE_LEN - 1] = '\0';
        pr_info("SoC: %s\n", soc_info.sys_type);
        prom_init_cmdline();
+
+#if defined(CONFIG_MIPS_MT_SMP)
+       if (register_vsmp_smp_ops())
+               panic("failed to register_vsmp_smp_ops()");
+#endif
 }
+
+int __init plat_of_setup(void)
+{
+       static struct of_device_id of_ids[3];
+
+       if (!of_have_populated_dt())
+               panic("device tree not present");
+
+       strncpy(of_ids[0].compatible, soc_info.compatible,
+               sizeof(of_ids[0].compatible));
+       strncpy(of_ids[1].compatible, "simple-bus",
+               sizeof(of_ids[1].compatible));
+       return of_platform_bus_probe(NULL, of_ids, NULL);
+}
+
+arch_initcall(plat_of_setup);
index b4229d94280f9cdd3f9f960698ac28cc7aee52a5..a3fa1a2bfaae5accf32fc341d9a92b0df17438e7 100644 (file)
 #define _LTQ_PROM_H__
 
 #define LTQ_SYS_TYPE_LEN       0x100
+#define LTQ_SYS_REV_LEN         0x10
 
 struct ltq_soc_info {
        unsigned char *name;
        unsigned int rev;
+       unsigned char rev_type[LTQ_SYS_REV_LEN];
+       unsigned int srev;
        unsigned int partnum;
        unsigned int type;
        unsigned char sys_type[LTQ_SYS_TYPE_LEN];
+       unsigned char *compatible;
 };
 
 extern void ltq_soc_detect(struct ltq_soc_info *i);
-extern void ltq_soc_setup(void);
+extern void ltq_soc_init(void);
+
+extern struct boot_param_header __dtb_start;
 
 #endif
diff --git a/arch/mips/lantiq/setup.c b/arch/mips/lantiq/setup.c
deleted file mode 100644 (file)
index 1ff6c9d..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- *  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.
- *
- * Copyright (C) 2010 John Crispin <blogic@openwrt.org>
- */
-
-#include <linux/kernel.h>
-#include <linux/export.h>
-#include <linux/io.h>
-#include <linux/ioport.h>
-#include <asm/bootinfo.h>
-
-#include <lantiq_soc.h>
-
-#include "machtypes.h"
-#include "devices.h"
-#include "prom.h"
-
-void __init plat_mem_setup(void)
-{
-       /* assume 16M as default incase uboot fails to pass proper ramsize */
-       unsigned long memsize = 16;
-       char **envp = (char **) KSEG1ADDR(fw_arg2);
-
-       ioport_resource.start = IOPORT_RESOURCE_START;
-       ioport_resource.end = IOPORT_RESOURCE_END;
-       iomem_resource.start = IOMEM_RESOURCE_START;
-       iomem_resource.end = IOMEM_RESOURCE_END;
-
-       set_io_port_base((unsigned long) KSEG1);
-
-       while (*envp) {
-               char *e = (char *)KSEG1ADDR(*envp);
-               if (!strncmp(e, "memsize=", 8)) {
-                       e += 8;
-                       if (strict_strtoul(e, 0, &memsize))
-                               pr_warn("bad memsize specified\n");
-               }
-               envp++;
-       }
-       memsize *= 1024 * 1024;
-       add_memory_region(0x00000000, memsize, BOOT_MEM_RAM);
-}
-
-static int __init
-lantiq_setup(void)
-{
-       ltq_soc_setup();
-       mips_machine_setup();
-       return 0;
-}
-
-arch_initcall(lantiq_setup);
-
-static void __init
-lantiq_generic_init(void)
-{
-       /* Nothing to do */
-}
-
-MIPS_MACHINE(LTQ_MACH_GENERIC,
-            "Generic",
-            "Generic Lantiq based board",
-            lantiq_generic_init);
diff --git a/arch/mips/lantiq/xway/Kconfig b/arch/mips/lantiq/xway/Kconfig
deleted file mode 100644 (file)
index 2b857de..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-if SOC_XWAY
-
-menu "MIPS Machine"
-
-config LANTIQ_MACH_EASY50712
-       bool "Easy50712 - Danube"
-       default y
-
-endmenu
-
-endif
-
-if SOC_AMAZON_SE
-
-menu "MIPS Machine"
-
-config LANTIQ_MACH_EASY50601
-       bool "Easy50601 - Amazon SE"
-       default y
-
-endmenu
-
-endif
index c517f2e77563cb3001146a28ec62853350798f53..dc3194f6ee421ca16c39295b9fcaec07fc5d9cf6 100644 (file)
@@ -1,7 +1 @@
-obj-y := pmu.o ebu.o reset.o gpio.o gpio_stp.o gpio_ebu.o devices.o dma.o
-
-obj-$(CONFIG_SOC_XWAY) += clk-xway.o prom-xway.o setup-xway.o
-obj-$(CONFIG_SOC_AMAZON_SE) += clk-ase.o prom-ase.o setup-ase.o
-
-obj-$(CONFIG_LANTIQ_MACH_EASY50712) += mach-easy50712.o
-obj-$(CONFIG_LANTIQ_MACH_EASY50601) += mach-easy50601.o
+obj-y := prom.o sysctrl.o clk.o reset.o gpio.o dma.o
diff --git a/arch/mips/lantiq/xway/clk-ase.c b/arch/mips/lantiq/xway/clk-ase.c
deleted file mode 100644 (file)
index 6522583..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- *  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.
- *
- *  Copyright (C) 2011 John Crispin <blogic@openwrt.org>
- */
-
-#include <linux/io.h>
-#include <linux/export.h>
-#include <linux/init.h>
-#include <linux/clk.h>
-
-#include <asm/time.h>
-#include <asm/irq.h>
-#include <asm/div64.h>
-
-#include <lantiq_soc.h>
-
-/* cgu registers */
-#define LTQ_CGU_SYS    0x0010
-
-unsigned int ltq_get_io_region_clock(void)
-{
-       return CLOCK_133M;
-}
-EXPORT_SYMBOL(ltq_get_io_region_clock);
-
-unsigned int ltq_get_fpi_bus_clock(int fpi)
-{
-       return CLOCK_133M;
-}
-EXPORT_SYMBOL(ltq_get_fpi_bus_clock);
-
-unsigned int ltq_get_cpu_hz(void)
-{
-       if (ltq_cgu_r32(LTQ_CGU_SYS) & (1 << 5))
-               return CLOCK_266M;
-       else
-               return CLOCK_133M;
-}
-EXPORT_SYMBOL(ltq_get_cpu_hz);
-
-unsigned int ltq_get_fpi_hz(void)
-{
-       return CLOCK_133M;
-}
-EXPORT_SYMBOL(ltq_get_fpi_hz);
diff --git a/arch/mips/lantiq/xway/clk-xway.c b/arch/mips/lantiq/xway/clk-xway.c
deleted file mode 100644 (file)
index 696b1a3..0000000
+++ /dev/null
@@ -1,223 +0,0 @@
-/*
- *  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.
- *
- *  Copyright (C) 2010 John Crispin <blogic@openwrt.org>
- */
-
-#include <linux/io.h>
-#include <linux/export.h>
-#include <linux/init.h>
-#include <linux/clk.h>
-
-#include <asm/time.h>
-#include <asm/irq.h>
-#include <asm/div64.h>
-
-#include <lantiq_soc.h>
-
-static unsigned int ltq_ram_clocks[] = {
-       CLOCK_167M, CLOCK_133M, CLOCK_111M, CLOCK_83M };
-#define DDR_HZ ltq_ram_clocks[ltq_cgu_r32(LTQ_CGU_SYS) & 0x3]
-
-#define BASIC_FREQUENCY_1      35328000
-#define BASIC_FREQUENCY_2      36000000
-#define BASIS_REQUENCY_USB     12000000
-
-#define GET_BITS(x, msb, lsb) \
-       (((x) & ((1 << ((msb) + 1)) - 1)) >> (lsb))
-
-#define LTQ_CGU_PLL0_CFG       0x0004
-#define LTQ_CGU_PLL1_CFG       0x0008
-#define LTQ_CGU_PLL2_CFG       0x000C
-#define LTQ_CGU_SYS            0x0010
-#define LTQ_CGU_UPDATE         0x0014
-#define LTQ_CGU_IF_CLK         0x0018
-#define LTQ_CGU_OSC_CON                0x001C
-#define LTQ_CGU_SMD            0x0020
-#define LTQ_CGU_CT1SR          0x0028
-#define LTQ_CGU_CT2SR          0x002C
-#define LTQ_CGU_PCMCR          0x0030
-#define LTQ_CGU_PCI_CR         0x0034
-#define LTQ_CGU_PD_PC          0x0038
-#define LTQ_CGU_FMR            0x003C
-
-#define CGU_PLL0_PHASE_DIVIDER_ENABLE  \
-       (ltq_cgu_r32(LTQ_CGU_PLL0_CFG) & (1 << 31))
-#define CGU_PLL0_BYPASS                        \
-       (ltq_cgu_r32(LTQ_CGU_PLL0_CFG) & (1 << 30))
-#define CGU_PLL0_CFG_DSMSEL            \
-       (ltq_cgu_r32(LTQ_CGU_PLL0_CFG) & (1 << 28))
-#define CGU_PLL0_CFG_FRAC_EN           \
-       (ltq_cgu_r32(LTQ_CGU_PLL0_CFG) & (1 << 27))
-#define CGU_PLL1_SRC                   \
-       (ltq_cgu_r32(LTQ_CGU_PLL1_CFG) & (1 << 31))
-#define CGU_PLL2_PHASE_DIVIDER_ENABLE  \
-       (ltq_cgu_r32(LTQ_CGU_PLL2_CFG) & (1 << 20))
-#define CGU_SYS_FPI_SEL                        (1 << 6)
-#define CGU_SYS_DDR_SEL                        0x3
-#define CGU_PLL0_SRC                   (1 << 29)
-
-#define CGU_PLL0_CFG_PLLK      GET_BITS(ltq_cgu_r32(LTQ_CGU_PLL0_CFG), 26, 17)
-#define CGU_PLL0_CFG_PLLN      GET_BITS(ltq_cgu_r32(LTQ_CGU_PLL0_CFG), 12, 6)
-#define CGU_PLL0_CFG_PLLM      GET_BITS(ltq_cgu_r32(LTQ_CGU_PLL0_CFG), 5, 2)
-#define CGU_PLL2_SRC           GET_BITS(ltq_cgu_r32(LTQ_CGU_PLL2_CFG), 18, 17)
-#define CGU_PLL2_CFG_INPUT_DIV GET_BITS(ltq_cgu_r32(LTQ_CGU_PLL2_CFG), 16, 13)
-
-static unsigned int ltq_get_pll0_fdiv(void);
-
-static inline unsigned int get_input_clock(int pll)
-{
-       switch (pll) {
-       case 0:
-               if (ltq_cgu_r32(LTQ_CGU_PLL0_CFG) & CGU_PLL0_SRC)
-                       return BASIS_REQUENCY_USB;
-               else if (CGU_PLL0_PHASE_DIVIDER_ENABLE)
-                       return BASIC_FREQUENCY_1;
-               else
-                       return BASIC_FREQUENCY_2;
-       case 1:
-               if (CGU_PLL1_SRC)
-                       return BASIS_REQUENCY_USB;
-               else if (CGU_PLL0_PHASE_DIVIDER_ENABLE)
-                       return BASIC_FREQUENCY_1;
-               else
-                       return BASIC_FREQUENCY_2;
-       case 2:
-               switch (CGU_PLL2_SRC) {
-               case 0:
-                       return ltq_get_pll0_fdiv();
-               case 1:
-                       return CGU_PLL2_PHASE_DIVIDER_ENABLE ?
-                               BASIC_FREQUENCY_1 :
-                               BASIC_FREQUENCY_2;
-               case 2:
-                       return BASIS_REQUENCY_USB;
-               }
-       default:
-               return 0;
-       }
-}
-
-static inline unsigned int cal_dsm(int pll, unsigned int num, unsigned int den)
-{
-       u64 res, clock = get_input_clock(pll);
-
-       res = num * clock;
-       do_div(res, den);
-       return res;
-}
-
-static inline unsigned int mash_dsm(int pll, unsigned int M, unsigned int N,
-       unsigned int K)
-{
-       unsigned int num = ((N + 1) << 10) + K;
-       unsigned int den = (M + 1) << 10;
-
-       return cal_dsm(pll, num, den);
-}
-
-static inline unsigned int ssff_dsm_1(int pll, unsigned int M, unsigned int N,
-       unsigned int K)
-{
-       unsigned int num = ((N + 1) << 11) + K + 512;
-       unsigned int den = (M + 1) << 11;
-
-       return cal_dsm(pll, num, den);
-}
-
-static inline unsigned int ssff_dsm_2(int pll, unsigned int M, unsigned int N,
-       unsigned int K)
-{
-       unsigned int num = K >= 512 ?
-               ((N + 1) << 12) + K - 512 : ((N + 1) << 12) + K + 3584;
-       unsigned int den = (M + 1) << 12;
-
-       return cal_dsm(pll, num, den);
-}
-
-static inline unsigned int dsm(int pll, unsigned int M, unsigned int N,
-       unsigned int K, unsigned int dsmsel, unsigned int phase_div_en)
-{
-       if (!dsmsel)
-               return mash_dsm(pll, M, N, K);
-       else if (!phase_div_en)
-               return mash_dsm(pll, M, N, K);
-       else
-               return ssff_dsm_2(pll, M, N, K);
-}
-
-static inline unsigned int ltq_get_pll0_fosc(void)
-{
-       if (CGU_PLL0_BYPASS)
-               return get_input_clock(0);
-       else
-               return !CGU_PLL0_CFG_FRAC_EN
-                       ? dsm(0, CGU_PLL0_CFG_PLLM, CGU_PLL0_CFG_PLLN, 0,
-                               CGU_PLL0_CFG_DSMSEL,
-                               CGU_PLL0_PHASE_DIVIDER_ENABLE)
-                       : dsm(0, CGU_PLL0_CFG_PLLM, CGU_PLL0_CFG_PLLN,
-                               CGU_PLL0_CFG_PLLK, CGU_PLL0_CFG_DSMSEL,
-                               CGU_PLL0_PHASE_DIVIDER_ENABLE);
-}
-
-static unsigned int ltq_get_pll0_fdiv(void)
-{
-       unsigned int div = CGU_PLL2_CFG_INPUT_DIV + 1;
-
-       return (ltq_get_pll0_fosc() + (div >> 1)) / div;
-}
-
-unsigned int ltq_get_io_region_clock(void)
-{
-       unsigned int ret = ltq_get_pll0_fosc();
-
-       switch (ltq_cgu_r32(LTQ_CGU_PLL2_CFG) & CGU_SYS_DDR_SEL) {
-       default:
-       case 0:
-               return (ret + 1) / 2;
-       case 1:
-               return (ret * 2 + 2) / 5;
-       case 2:
-               return (ret + 1) / 3;
-       case 3:
-               return (ret + 2) / 4;
-       }
-}
-EXPORT_SYMBOL(ltq_get_io_region_clock);
-
-unsigned int ltq_get_fpi_bus_clock(int fpi)
-{
-       unsigned int ret = ltq_get_io_region_clock();
-
-       if ((fpi == 2) && (ltq_cgu_r32(LTQ_CGU_SYS) & CGU_SYS_FPI_SEL))
-               ret >>= 1;
-       return ret;
-}
-EXPORT_SYMBOL(ltq_get_fpi_bus_clock);
-
-unsigned int ltq_get_cpu_hz(void)
-{
-       switch (ltq_cgu_r32(LTQ_CGU_SYS) & 0xc) {
-       case 0:
-               return CLOCK_333M;
-       case 4:
-               return DDR_HZ;
-       case 8:
-               return DDR_HZ << 1;
-       default:
-               return DDR_HZ >> 1;
-       }
-}
-EXPORT_SYMBOL(ltq_get_cpu_hz);
-
-unsigned int ltq_get_fpi_hz(void)
-{
-       unsigned int ddr_clock = DDR_HZ;
-
-       if (ltq_cgu_r32(LTQ_CGU_SYS) & 0x40)
-               return ddr_clock >> 1;
-       return ddr_clock;
-}
-EXPORT_SYMBOL(ltq_get_fpi_hz);
diff --git a/arch/mips/lantiq/xway/clk.c b/arch/mips/lantiq/xway/clk.c
new file mode 100644 (file)
index 0000000..9aa17f7
--- /dev/null
@@ -0,0 +1,151 @@
+/*
+ *  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.
+ *
+ *  Copyright (C) 2010 John Crispin <blogic@openwrt.org>
+ */
+
+#include <linux/io.h>
+#include <linux/export.h>
+#include <linux/init.h>
+#include <linux/clk.h>
+
+#include <asm/time.h>
+#include <asm/irq.h>
+#include <asm/div64.h>
+
+#include <lantiq_soc.h>
+
+#include "../clk.h"
+
+static unsigned int ram_clocks[] = {
+       CLOCK_167M, CLOCK_133M, CLOCK_111M, CLOCK_83M };
+#define DDR_HZ ram_clocks[ltq_cgu_r32(CGU_SYS) & 0x3]
+
+/* legacy xway clock */
+#define CGU_SYS                        0x10
+
+/* vr9 clock */
+#define CGU_SYS_VR9            0x0c
+#define CGU_IF_CLK_VR9         0x24
+
+unsigned long ltq_danube_fpi_hz(void)
+{
+       unsigned long ddr_clock = DDR_HZ;
+
+       if (ltq_cgu_r32(CGU_SYS) & 0x40)
+               return ddr_clock >> 1;
+       return ddr_clock;
+}
+
+unsigned long ltq_danube_cpu_hz(void)
+{
+       switch (ltq_cgu_r32(CGU_SYS) & 0xc) {
+       case 0:
+               return CLOCK_333M;
+       case 4:
+               return DDR_HZ;
+       case 8:
+               return DDR_HZ << 1;
+       default:
+               return DDR_HZ >> 1;
+       }
+}
+
+unsigned long ltq_ar9_sys_hz(void)
+{
+       if (((ltq_cgu_r32(CGU_SYS) >> 3) & 0x3) == 0x2)
+               return CLOCK_393M;
+       return CLOCK_333M;
+}
+
+unsigned long ltq_ar9_fpi_hz(void)
+{
+       unsigned long sys = ltq_ar9_sys_hz();
+
+       if (ltq_cgu_r32(CGU_SYS) & BIT(0))
+               return sys;
+       return sys >> 1;
+}
+
+unsigned long ltq_ar9_cpu_hz(void)
+{
+       if (ltq_cgu_r32(CGU_SYS) & BIT(2))
+               return ltq_ar9_fpi_hz();
+       else
+               return ltq_ar9_sys_hz();
+}
+
+unsigned long ltq_vr9_cpu_hz(void)
+{
+       unsigned int cpu_sel;
+       unsigned long clk;
+
+       cpu_sel = (ltq_cgu_r32(CGU_SYS_VR9) >> 4) & 0xf;
+
+       switch (cpu_sel) {
+       case 0:
+               clk = CLOCK_600M;
+               break;
+       case 1:
+               clk = CLOCK_500M;
+               break;
+       case 2:
+               clk = CLOCK_393M;
+               break;
+       case 3:
+               clk = CLOCK_333M;
+               break;
+       case 5:
+       case 6:
+               clk = CLOCK_196_608M;
+               break;
+       case 7:
+               clk = CLOCK_167M;
+               break;
+       case 4:
+       case 8:
+       case 9:
+               clk = CLOCK_125M;
+               break;
+       default:
+               clk = 0;
+               break;
+       }
+
+       return clk;
+}
+
+unsigned long ltq_vr9_fpi_hz(void)
+{
+       unsigned int ocp_sel, cpu_clk;
+       unsigned long clk;
+
+       cpu_clk = ltq_vr9_cpu_hz();
+       ocp_sel = ltq_cgu_r32(CGU_SYS_VR9) & 0x3;
+
+       switch (ocp_sel) {
+       case 0:
+               /* OCP ratio 1 */
+               clk = cpu_clk;
+               break;
+       case 2:
+               /* OCP ratio 2 */
+               clk = cpu_clk / 2;
+               break;
+       case 3:
+               /* OCP ratio 2.5 */
+               clk = (cpu_clk * 2) / 5;
+               break;
+       case 4:
+               /* OCP ratio 3 */
+               clk = cpu_clk / 3;
+               break;
+       default:
+               clk = 0;
+               break;
+       }
+
+       return clk;
+}
diff --git a/arch/mips/lantiq/xway/devices.c b/arch/mips/lantiq/xway/devices.c
deleted file mode 100644 (file)
index d614aa7..0000000
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- *  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.
- *
- *  Copyright (C) 2010 John Crispin <blogic@openwrt.org>
- */
-
-#include <linux/init.h>
-#include <linux/export.h>
-#include <linux/types.h>
-#include <linux/string.h>
-#include <linux/mtd/physmap.h>
-#include <linux/kernel.h>
-#include <linux/reboot.h>
-#include <linux/platform_device.h>
-#include <linux/leds.h>
-#include <linux/etherdevice.h>
-#include <linux/time.h>
-#include <linux/io.h>
-#include <linux/gpio.h>
-
-#include <asm/bootinfo.h>
-#include <asm/irq.h>
-
-#include <lantiq_soc.h>
-#include <lantiq_irq.h>
-#include <lantiq_platform.h>
-
-#include "devices.h"
-
-/* gpio */
-static struct resource ltq_gpio_resource[] = {
-       {
-               .name   = "gpio0",
-               .start  = LTQ_GPIO0_BASE_ADDR,
-               .end    = LTQ_GPIO0_BASE_ADDR + LTQ_GPIO_SIZE - 1,
-               .flags  = IORESOURCE_MEM,
-       }, {
-               .name   = "gpio1",
-               .start  = LTQ_GPIO1_BASE_ADDR,
-               .end    = LTQ_GPIO1_BASE_ADDR + LTQ_GPIO_SIZE - 1,
-               .flags  = IORESOURCE_MEM,
-       }, {
-               .name   = "gpio2",
-               .start  = LTQ_GPIO2_BASE_ADDR,
-               .end    = LTQ_GPIO2_BASE_ADDR + LTQ_GPIO_SIZE - 1,
-               .flags  = IORESOURCE_MEM,
-       }
-};
-
-void __init ltq_register_gpio(void)
-{
-       platform_device_register_simple("ltq_gpio", 0,
-               &ltq_gpio_resource[0], 1);
-       platform_device_register_simple("ltq_gpio", 1,
-               &ltq_gpio_resource[1], 1);
-
-       /* AR9 and VR9 have an extra gpio block */
-       if (ltq_is_ar9() || ltq_is_vr9()) {
-               platform_device_register_simple("ltq_gpio", 2,
-                       &ltq_gpio_resource[2], 1);
-       }
-}
-
-/* serial to parallel conversion */
-static struct resource ltq_stp_resource = {
-       .name   = "stp",
-       .start  = LTQ_STP_BASE_ADDR,
-       .end    = LTQ_STP_BASE_ADDR + LTQ_STP_SIZE - 1,
-       .flags  = IORESOURCE_MEM,
-};
-
-void __init ltq_register_gpio_stp(void)
-{
-       platform_device_register_simple("ltq_stp", 0, &ltq_stp_resource, 1);
-}
-
-/* asc ports - amazon se has its own serial mapping */
-static struct resource ltq_ase_asc_resources[] = {
-       {
-               .name   = "asc0",
-               .start  = LTQ_ASC1_BASE_ADDR,
-               .end    = LTQ_ASC1_BASE_ADDR + LTQ_ASC_SIZE - 1,
-               .flags  = IORESOURCE_MEM,
-       },
-       IRQ_RES(tx, LTQ_ASC_ASE_TIR),
-       IRQ_RES(rx, LTQ_ASC_ASE_RIR),
-       IRQ_RES(err, LTQ_ASC_ASE_EIR),
-};
-
-void __init ltq_register_ase_asc(void)
-{
-       platform_device_register_simple("ltq_asc", 0,
-               ltq_ase_asc_resources, ARRAY_SIZE(ltq_ase_asc_resources));
-}
-
-/* ethernet */
-static struct resource ltq_etop_resources = {
-       .name   = "etop",
-       .start  = LTQ_ETOP_BASE_ADDR,
-       .end    = LTQ_ETOP_BASE_ADDR + LTQ_ETOP_SIZE - 1,
-       .flags  = IORESOURCE_MEM,
-};
-
-static struct platform_device ltq_etop = {
-       .name           = "ltq_etop",
-       .resource       = &ltq_etop_resources,
-       .num_resources  = 1,
-};
-
-void __init
-ltq_register_etop(struct ltq_eth_data *eth)
-{
-       if (eth) {
-               ltq_etop.dev.platform_data = eth;
-               platform_device_register(&ltq_etop);
-       }
-}
diff --git a/arch/mips/lantiq/xway/devices.h b/arch/mips/lantiq/xway/devices.h
deleted file mode 100644 (file)
index e904934..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *  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.
- *
- *  Copyright (C) 2010 John Crispin <blogic@openwrt.org>
- */
-
-#ifndef _LTQ_DEVICES_XWAY_H__
-#define _LTQ_DEVICES_XWAY_H__
-
-#include "../devices.h"
-#include <linux/phy.h>
-
-extern void ltq_register_gpio(void);
-extern void ltq_register_gpio_stp(void);
-extern void ltq_register_ase_asc(void);
-extern void ltq_register_etop(struct ltq_eth_data *eth);
-
-#endif
index b210e936c7c3a84900eeeb0c1ba628b7479ba71a..55d2c4fa47140d555d762fadb74b982b7a2f89fd 100644 (file)
@@ -19,7 +19,8 @@
 #include <linux/platform_device.h>
 #include <linux/io.h>
 #include <linux/dma-mapping.h>
-#include <linux/export.h>
+#include <linux/module.h>
+#include <linux/clk.h>
 
 #include <lantiq_soc.h>
 #include <xway_dma.h>
 #define ltq_dma_w32_mask(x, y, z)      ltq_w32_mask(x, y, \
                                                ltq_dma_membase + (z))
 
-static struct resource ltq_dma_resource = {
-       .name   = "dma",
-       .start  = LTQ_DMA_BASE_ADDR,
-       .end    = LTQ_DMA_BASE_ADDR + LTQ_DMA_SIZE - 1,
-       .flags  = IORESOURCE_MEM,
-};
-
 static void __iomem *ltq_dma_membase;
 
 void
@@ -215,27 +209,28 @@ ltq_dma_init_port(int p)
 }
 EXPORT_SYMBOL_GPL(ltq_dma_init_port);
 
-int __init
-ltq_dma_init(void)
+static int __devinit
+ltq_dma_init(struct platform_device *pdev)
 {
+       struct clk *clk;
+       struct resource *res;
        int i;
 
-       /* insert and request the memory region */
-       if (insert_resource(&iomem_resource, &ltq_dma_resource) < 0)
-               panic("Failed to insert dma memory");
-
-       if (request_mem_region(ltq_dma_resource.start,
-                       resource_size(&ltq_dma_resource), "dma") < 0)
-               panic("Failed to request dma memory");
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res)
+               panic("Failed to get dma resource");
 
        /* remap dma register range */
-       ltq_dma_membase = ioremap_nocache(ltq_dma_resource.start,
-                               resource_size(&ltq_dma_resource));
+       ltq_dma_membase = devm_request_and_ioremap(&pdev->dev, res);
        if (!ltq_dma_membase)
-               panic("Failed to remap dma memory");
+               panic("Failed to remap dma resource");
 
        /* power up and reset the dma engine */
-       ltq_pmu_enable(PMU_DMA);
+       clk = clk_get(&pdev->dev, NULL);
+       if (IS_ERR(clk))
+               panic("Failed to get dma clock");
+
+       clk_enable(clk);
        ltq_dma_w32_mask(0, DMA_RESET, LTQ_DMA_CTRL);
 
        /* disable all interrupts */
@@ -248,7 +243,29 @@ ltq_dma_init(void)
                ltq_dma_w32(DMA_POLL | DMA_CLK_DIV4, LTQ_DMA_CPOLL);
                ltq_dma_w32_mask(DMA_CHAN_ON, 0, LTQ_DMA_CCTRL);
        }
+       dev_info(&pdev->dev, "init done\n");
        return 0;
 }
 
-postcore_initcall(ltq_dma_init);
+static const struct of_device_id dma_match[] = {
+       { .compatible = "lantiq,dma-xway" },
+       {},
+};
+MODULE_DEVICE_TABLE(of, dma_match);
+
+static struct platform_driver dma_driver = {
+       .probe = ltq_dma_init,
+       .driver = {
+               .name = "dma-xway",
+               .owner = THIS_MODULE,
+               .of_match_table = dma_match,
+       },
+};
+
+int __init
+dma_init(void)
+{
+       return platform_driver_register(&dma_driver);
+}
+
+postcore_initcall(dma_init);
diff --git a/arch/mips/lantiq/xway/ebu.c b/arch/mips/lantiq/xway/ebu.c
deleted file mode 100644 (file)
index 862e3e8..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- *  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.
- *
- *  EBU - the external bus unit attaches PCI, NOR and NAND
- *
- *  Copyright (C) 2010 John Crispin <blogic@openwrt.org>
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/ioport.h>
-
-#include <lantiq_soc.h>
-
-/* all access to the ebu must be locked */
-DEFINE_SPINLOCK(ebu_lock);
-EXPORT_SYMBOL_GPL(ebu_lock);
-
-static struct resource ltq_ebu_resource = {
-       .name   = "ebu",
-       .start  = LTQ_EBU_BASE_ADDR,
-       .end    = LTQ_EBU_BASE_ADDR + LTQ_EBU_SIZE - 1,
-       .flags  = IORESOURCE_MEM,
-};
-
-/* remapped base addr of the clock unit and external bus unit */
-void __iomem *ltq_ebu_membase;
-
-static int __init lantiq_ebu_init(void)
-{
-       /* insert and request the memory region */
-       if (insert_resource(&iomem_resource, &ltq_ebu_resource) < 0)
-               panic("Failed to insert ebu memory");
-
-       if (request_mem_region(ltq_ebu_resource.start,
-                       resource_size(&ltq_ebu_resource), "ebu") < 0)
-               panic("Failed to request ebu memory");
-
-       /* remap ebu register range */
-       ltq_ebu_membase = ioremap_nocache(ltq_ebu_resource.start,
-                               resource_size(&ltq_ebu_resource));
-       if (!ltq_ebu_membase)
-               panic("Failed to remap ebu memory");
-
-       /* make sure to unprotect the memory region where flash is located */
-       ltq_ebu_w32(ltq_ebu_r32(LTQ_EBU_BUSCON0) & ~EBU_WRDIS, LTQ_EBU_BUSCON0);
-       return 0;
-}
-
-postcore_initcall(lantiq_ebu_init);
index c429a5bc080fdfca9fd9c40eb61c5cdb9b8198d6..2ab39e93d9beb711a7f8b3250a69e2ec094a287d 100644 (file)
@@ -36,18 +36,6 @@ struct ltq_gpio {
 
 static struct ltq_gpio ltq_gpio_port[MAX_PORTS];
 
-int gpio_to_irq(unsigned int gpio)
-{
-       return -EINVAL;
-}
-EXPORT_SYMBOL(gpio_to_irq);
-
-int irq_to_gpio(unsigned int gpio)
-{
-       return -EINVAL;
-}
-EXPORT_SYMBOL(irq_to_gpio);
-
 int ltq_gpio_request(unsigned int pin, unsigned int alt0,
        unsigned int alt1, unsigned int dir, const char *name)
 {
diff --git a/arch/mips/lantiq/xway/gpio_ebu.c b/arch/mips/lantiq/xway/gpio_ebu.c
deleted file mode 100644 (file)
index aae1717..0000000
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- *  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.
- *
- *  Copyright (C) 2010 John Crispin <blogic@openwrt.org>
- */
-
-#include <linux/init.h>
-#include <linux/export.h>
-#include <linux/types.h>
-#include <linux/platform_device.h>
-#include <linux/mutex.h>
-#include <linux/gpio.h>
-#include <linux/io.h>
-
-#include <lantiq_soc.h>
-
-/*
- * By attaching hardware latches to the EBU it is possible to create output
- * only gpios. This driver configures a special memory address, which when
- * written to outputs 16 bit to the latches.
- */
-
-#define LTQ_EBU_BUSCON 0x1e7ff         /* 16 bit access, slowest timing */
-#define LTQ_EBU_WP     0x80000000      /* write protect bit */
-
-/* we keep a shadow value of the last value written to the ebu */
-static int ltq_ebu_gpio_shadow = 0x0;
-static void __iomem *ltq_ebu_gpio_membase;
-
-static void ltq_ebu_apply(void)
-{
-       unsigned long flags;
-
-       spin_lock_irqsave(&ebu_lock, flags);
-       ltq_ebu_w32(LTQ_EBU_BUSCON, LTQ_EBU_BUSCON1);
-       *((__u16 *)ltq_ebu_gpio_membase) = ltq_ebu_gpio_shadow;
-       ltq_ebu_w32(LTQ_EBU_BUSCON | LTQ_EBU_WP, LTQ_EBU_BUSCON1);
-       spin_unlock_irqrestore(&ebu_lock, flags);
-}
-
-static void ltq_ebu_set(struct gpio_chip *chip, unsigned offset, int value)
-{
-       if (value)
-               ltq_ebu_gpio_shadow |= (1 << offset);
-       else
-               ltq_ebu_gpio_shadow &= ~(1 << offset);
-       ltq_ebu_apply();
-}
-
-static int ltq_ebu_direction_output(struct gpio_chip *chip, unsigned offset,
-       int value)
-{
-       ltq_ebu_set(chip, offset, value);
-
-       return 0;
-}
-
-static struct gpio_chip ltq_ebu_chip = {
-       .label = "ltq_ebu",
-       .direction_output = ltq_ebu_direction_output,
-       .set = ltq_ebu_set,
-       .base = 72,
-       .ngpio = 16,
-       .can_sleep = 1,
-       .owner = THIS_MODULE,
-};
-
-static int ltq_ebu_probe(struct platform_device *pdev)
-{
-       int ret = 0;
-       struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-
-       if (!res) {
-               dev_err(&pdev->dev, "failed to get memory resource\n");
-               return -ENOENT;
-       }
-
-       res = devm_request_mem_region(&pdev->dev, res->start,
-               resource_size(res), dev_name(&pdev->dev));
-       if (!res) {
-               dev_err(&pdev->dev, "failed to request memory resource\n");
-               return -EBUSY;
-       }
-
-       ltq_ebu_gpio_membase = devm_ioremap_nocache(&pdev->dev, res->start,
-               resource_size(res));
-       if (!ltq_ebu_gpio_membase) {
-               dev_err(&pdev->dev, "Failed to ioremap mem region\n");
-               return -ENOMEM;
-       }
-
-       /* grab the default shadow value passed form the platform code */
-       ltq_ebu_gpio_shadow = (unsigned int) pdev->dev.platform_data;
-
-       /* tell the ebu controller which memory address we will be using */
-       ltq_ebu_w32(pdev->resource->start | 0x1, LTQ_EBU_ADDRSEL1);
-
-       /* write protect the region */
-       ltq_ebu_w32(LTQ_EBU_BUSCON | LTQ_EBU_WP, LTQ_EBU_BUSCON1);
-
-       ret = gpiochip_add(&ltq_ebu_chip);
-       if (!ret)
-               ltq_ebu_apply();
-       return ret;
-}
-
-static struct platform_driver ltq_ebu_driver = {
-       .probe = ltq_ebu_probe,
-       .driver = {
-               .name = "ltq_ebu",
-               .owner = THIS_MODULE,
-       },
-};
-
-static int __init ltq_ebu_init(void)
-{
-       int ret = platform_driver_register(&ltq_ebu_driver);
-
-       if (ret)
-               pr_info("ltq_ebu : Error registering platform driver!");
-       return ret;
-}
-
-postcore_initcall(ltq_ebu_init);
diff --git a/arch/mips/lantiq/xway/gpio_stp.c b/arch/mips/lantiq/xway/gpio_stp.c
deleted file mode 100644 (file)
index fd07d87..0000000
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- *  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.
- *
- *  Copyright (C) 2007 John Crispin <blogic@openwrt.org>
- *
- */
-
-#include <linux/slab.h>
-#include <linux/init.h>
-#include <linux/export.h>
-#include <linux/types.h>
-#include <linux/platform_device.h>
-#include <linux/mutex.h>
-#include <linux/io.h>
-#include <linux/gpio.h>
-
-#include <lantiq_soc.h>
-
-#define LTQ_STP_CON0           0x00
-#define LTQ_STP_CON1           0x04
-#define LTQ_STP_CPU0           0x08
-#define LTQ_STP_CPU1           0x0C
-#define LTQ_STP_AR             0x10
-
-#define LTQ_STP_CON_SWU                (1 << 31)
-#define LTQ_STP_2HZ            0
-#define LTQ_STP_4HZ            (1 << 23)
-#define LTQ_STP_8HZ            (2 << 23)
-#define LTQ_STP_10HZ           (3 << 23)
-#define LTQ_STP_SPEED_MASK     (0xf << 23)
-#define LTQ_STP_UPD_FPI                (1 << 31)
-#define LTQ_STP_UPD_MASK       (3 << 30)
-#define LTQ_STP_ADSL_SRC       (3 << 24)
-
-#define LTQ_STP_GROUP0         (1 << 0)
-
-#define LTQ_STP_RISING         0
-#define LTQ_STP_FALLING                (1 << 26)
-#define LTQ_STP_EDGE_MASK      (1 << 26)
-
-#define ltq_stp_r32(reg)       __raw_readl(ltq_stp_membase + reg)
-#define ltq_stp_w32(val, reg)  __raw_writel(val, ltq_stp_membase + reg)
-#define ltq_stp_w32_mask(clear, set, reg) \
-               ltq_w32((ltq_r32(ltq_stp_membase + reg) & ~(clear)) | (set), \
-               ltq_stp_membase + (reg))
-
-static int ltq_stp_shadow = 0xffff;
-static void __iomem *ltq_stp_membase;
-
-static void ltq_stp_set(struct gpio_chip *chip, unsigned offset, int value)
-{
-       if (value)
-               ltq_stp_shadow |= (1 << offset);
-       else
-               ltq_stp_shadow &= ~(1 << offset);
-       ltq_stp_w32(ltq_stp_shadow, LTQ_STP_CPU0);
-}
-
-static int ltq_stp_direction_output(struct gpio_chip *chip, unsigned offset,
-       int value)
-{
-       ltq_stp_set(chip, offset, value);
-
-       return 0;
-}
-
-static struct gpio_chip ltq_stp_chip = {
-       .label = "ltq_stp",
-       .direction_output = ltq_stp_direction_output,
-       .set = ltq_stp_set,
-       .base = 48,
-       .ngpio = 24,
-       .can_sleep = 1,
-       .owner = THIS_MODULE,
-};
-
-static int ltq_stp_hw_init(void)
-{
-       /* the 3 pins used to control the external stp */
-       ltq_gpio_request(4, 1, 0, 1, "stp-st");
-       ltq_gpio_request(5, 1, 0, 1, "stp-d");
-       ltq_gpio_request(6, 1, 0, 1, "stp-sh");
-
-       /* sane defaults */
-       ltq_stp_w32(0, LTQ_STP_AR);
-       ltq_stp_w32(0, LTQ_STP_CPU0);
-       ltq_stp_w32(0, LTQ_STP_CPU1);
-       ltq_stp_w32(LTQ_STP_CON_SWU, LTQ_STP_CON0);
-       ltq_stp_w32(0, LTQ_STP_CON1);
-
-       /* rising or falling edge */
-       ltq_stp_w32_mask(LTQ_STP_EDGE_MASK, LTQ_STP_FALLING, LTQ_STP_CON0);
-
-       /* per default stp 15-0 are set */
-       ltq_stp_w32_mask(0, LTQ_STP_GROUP0, LTQ_STP_CON1);
-
-       /* stp are update periodically by the FPI bus */
-       ltq_stp_w32_mask(LTQ_STP_UPD_MASK, LTQ_STP_UPD_FPI, LTQ_STP_CON1);
-
-       /* set stp update speed */
-       ltq_stp_w32_mask(LTQ_STP_SPEED_MASK, LTQ_STP_8HZ, LTQ_STP_CON1);
-
-       /* tell the hardware that pin (led) 0 and 1 are controlled
-        *  by the dsl arc
-        */
-       ltq_stp_w32_mask(0, LTQ_STP_ADSL_SRC, LTQ_STP_CON0);
-
-       ltq_pmu_enable(PMU_LED);
-       return 0;
-}
-
-static int __devinit ltq_stp_probe(struct platform_device *pdev)
-{
-       struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       int ret = 0;
-
-       if (!res)
-               return -ENOENT;
-       res = devm_request_mem_region(&pdev->dev, res->start,
-               resource_size(res), dev_name(&pdev->dev));
-       if (!res) {
-               dev_err(&pdev->dev, "failed to request STP memory\n");
-               return -EBUSY;
-       }
-       ltq_stp_membase = devm_ioremap_nocache(&pdev->dev, res->start,
-               resource_size(res));
-       if (!ltq_stp_membase) {
-               dev_err(&pdev->dev, "failed to remap STP memory\n");
-               return -ENOMEM;
-       }
-       ret = gpiochip_add(&ltq_stp_chip);
-       if (!ret)
-               ret = ltq_stp_hw_init();
-
-       return ret;
-}
-
-static struct platform_driver ltq_stp_driver = {
-       .probe = ltq_stp_probe,
-       .driver = {
-               .name = "ltq_stp",
-               .owner = THIS_MODULE,
-       },
-};
-
-int __init ltq_stp_init(void)
-{
-       int ret = platform_driver_register(&ltq_stp_driver);
-
-       if (ret)
-               pr_info("ltq_stp: error registering platform driver");
-       return ret;
-}
-
-postcore_initcall(ltq_stp_init);
diff --git a/arch/mips/lantiq/xway/mach-easy50601.c b/arch/mips/lantiq/xway/mach-easy50601.c
deleted file mode 100644 (file)
index d5aaf63..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- *  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.
- *
- *  Copyright (C) 2010 John Crispin <blogic@openwrt.org>
- */
-
-#include <linux/init.h>
-#include <linux/platform_device.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/partitions.h>
-#include <linux/mtd/physmap.h>
-#include <linux/input.h>
-
-#include <lantiq.h>
-
-#include "../machtypes.h"
-#include "devices.h"
-
-static struct mtd_partition easy50601_partitions[] = {
-       {
-               .name   = "uboot",
-               .offset = 0x0,
-               .size   = 0x10000,
-       },
-       {
-               .name   = "uboot_env",
-               .offset = 0x10000,
-               .size   = 0x10000,
-       },
-       {
-               .name   = "linux",
-               .offset = 0x20000,
-               .size   = 0xE0000,
-       },
-       {
-               .name   = "rootfs",
-               .offset = 0x100000,
-               .size   = 0x300000,
-       },
-};
-
-static struct physmap_flash_data easy50601_flash_data = {
-       .nr_parts       = ARRAY_SIZE(easy50601_partitions),
-       .parts          = easy50601_partitions,
-};
-
-static void __init easy50601_init(void)
-{
-       ltq_register_nor(&easy50601_flash_data);
-}
-
-MIPS_MACHINE(LTQ_MACH_EASY50601,
-                       "EASY50601",
-                       "EASY50601 Eval Board",
-                       easy50601_init);
diff --git a/arch/mips/lantiq/xway/mach-easy50712.c b/arch/mips/lantiq/xway/mach-easy50712.c
deleted file mode 100644 (file)
index ea5027b..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- *  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.
- *
- *  Copyright (C) 2010 John Crispin <blogic@openwrt.org>
- */
-
-#include <linux/init.h>
-#include <linux/platform_device.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/partitions.h>
-#include <linux/mtd/physmap.h>
-#include <linux/input.h>
-#include <linux/phy.h>
-
-#include <lantiq_soc.h>
-#include <irq.h>
-
-#include "../machtypes.h"
-#include "devices.h"
-
-static struct mtd_partition easy50712_partitions[] = {
-       {
-               .name   = "uboot",
-               .offset = 0x0,
-               .size   = 0x10000,
-       },
-       {
-               .name   = "uboot_env",
-               .offset = 0x10000,
-               .size   = 0x10000,
-       },
-       {
-               .name   = "linux",
-               .offset = 0x20000,
-               .size   = 0xe0000,
-       },
-       {
-               .name   = "rootfs",
-               .offset = 0x100000,
-               .size   = 0x300000,
-       },
-};
-
-static struct physmap_flash_data easy50712_flash_data = {
-       .nr_parts       = ARRAY_SIZE(easy50712_partitions),
-       .parts          = easy50712_partitions,
-};
-
-static struct ltq_pci_data ltq_pci_data = {
-       .clock  = PCI_CLOCK_INT,
-       .gpio   = PCI_GNT1 | PCI_REQ1,
-       .irq    = {
-               [14] = INT_NUM_IM0_IRL0 + 22,
-       },
-};
-
-static struct ltq_eth_data ltq_eth_data = {
-       .mii_mode = PHY_INTERFACE_MODE_MII,
-};
-
-static void __init easy50712_init(void)
-{
-       ltq_register_gpio_stp();
-       ltq_register_nor(&easy50712_flash_data);
-       ltq_register_pci(&ltq_pci_data);
-       ltq_register_etop(&ltq_eth_data);
-}
-
-MIPS_MACHINE(LTQ_MACH_EASY50712,
-            "EASY50712",
-            "EASY50712 Eval Board",
-             easy50712_init);
diff --git a/arch/mips/lantiq/xway/pmu.c b/arch/mips/lantiq/xway/pmu.c
deleted file mode 100644 (file)
index fe85361..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- *  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.
- *
- *  Copyright (C) 2010 John Crispin <blogic@openwrt.org>
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/ioport.h>
-
-#include <lantiq_soc.h>
-
-/* PMU - the power management unit allows us to turn part of the core
- * on and off
- */
-
-/* the enable / disable registers */
-#define LTQ_PMU_PWDCR  0x1C
-#define LTQ_PMU_PWDSR  0x20
-
-#define ltq_pmu_w32(x, y)      ltq_w32((x), ltq_pmu_membase + (y))
-#define ltq_pmu_r32(x)         ltq_r32(ltq_pmu_membase + (x))
-
-static struct resource ltq_pmu_resource = {
-       .name   = "pmu",
-       .start  = LTQ_PMU_BASE_ADDR,
-       .end    = LTQ_PMU_BASE_ADDR + LTQ_PMU_SIZE - 1,
-       .flags  = IORESOURCE_MEM,
-};
-
-static void __iomem *ltq_pmu_membase;
-
-void ltq_pmu_enable(unsigned int module)
-{
-       int err = 1000000;
-
-       ltq_pmu_w32(ltq_pmu_r32(LTQ_PMU_PWDCR) & ~module, LTQ_PMU_PWDCR);
-       do {} while (--err && (ltq_pmu_r32(LTQ_PMU_PWDSR) & module));
-
-       if (!err)
-               panic("activating PMU module failed!");
-}
-EXPORT_SYMBOL(ltq_pmu_enable);
-
-void ltq_pmu_disable(unsigned int module)
-{
-       ltq_pmu_w32(ltq_pmu_r32(LTQ_PMU_PWDCR) | module, LTQ_PMU_PWDCR);
-}
-EXPORT_SYMBOL(ltq_pmu_disable);
-
-int __init ltq_pmu_init(void)
-{
-       if (insert_resource(&iomem_resource, &ltq_pmu_resource) < 0)
-               panic("Failed to insert pmu memory");
-
-       if (request_mem_region(ltq_pmu_resource.start,
-                       resource_size(&ltq_pmu_resource), "pmu") < 0)
-               panic("Failed to request pmu memory");
-
-       ltq_pmu_membase = ioremap_nocache(ltq_pmu_resource.start,
-                               resource_size(&ltq_pmu_resource));
-       if (!ltq_pmu_membase)
-               panic("Failed to remap pmu memory");
-       return 0;
-}
-
-core_initcall(ltq_pmu_init);
diff --git a/arch/mips/lantiq/xway/prom-ase.c b/arch/mips/lantiq/xway/prom-ase.c
deleted file mode 100644 (file)
index ae4959a..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- *  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.
- *
- *  Copyright (C) 2010 John Crispin <blogic@openwrt.org>
- */
-
-#include <linux/export.h>
-#include <linux/clk.h>
-#include <asm/bootinfo.h>
-#include <asm/time.h>
-
-#include <lantiq_soc.h>
-
-#include "../prom.h"
-
-#define SOC_AMAZON_SE  "Amazon_SE"
-
-#define PART_SHIFT     12
-#define PART_MASK      0x0FFFFFFF
-#define REV_SHIFT      28
-#define REV_MASK       0xF0000000
-
-void __init ltq_soc_detect(struct ltq_soc_info *i)
-{
-       i->partnum = (ltq_r32(LTQ_MPS_CHIPID) & PART_MASK) >> PART_SHIFT;
-       i->rev = (ltq_r32(LTQ_MPS_CHIPID) & REV_MASK) >> REV_SHIFT;
-       switch (i->partnum) {
-       case SOC_ID_AMAZON_SE:
-               i->name = SOC_AMAZON_SE;
-               i->type = SOC_TYPE_AMAZON_SE;
-               break;
-
-       default:
-               unreachable();
-               break;
-       }
-}
diff --git a/arch/mips/lantiq/xway/prom-xway.c b/arch/mips/lantiq/xway/prom-xway.c
deleted file mode 100644 (file)
index 2228133..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- *  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.
- *
- *  Copyright (C) 2010 John Crispin <blogic@openwrt.org>
- */
-
-#include <linux/export.h>
-#include <linux/clk.h>
-#include <asm/bootinfo.h>
-#include <asm/time.h>
-
-#include <lantiq_soc.h>
-
-#include "../prom.h"
-
-#define SOC_DANUBE     "Danube"
-#define SOC_TWINPASS   "Twinpass"
-#define SOC_AR9                "AR9"
-
-#define PART_SHIFT     12
-#define PART_MASK      0x0FFFFFFF
-#define REV_SHIFT      28
-#define REV_MASK       0xF0000000
-
-void __init ltq_soc_detect(struct ltq_soc_info *i)
-{
-       i->partnum = (ltq_r32(LTQ_MPS_CHIPID) & PART_MASK) >> PART_SHIFT;
-       i->rev = (ltq_r32(LTQ_MPS_CHIPID) & REV_MASK) >> REV_SHIFT;
-       switch (i->partnum) {
-       case SOC_ID_DANUBE1:
-       case SOC_ID_DANUBE2:
-               i->name = SOC_DANUBE;
-               i->type = SOC_TYPE_DANUBE;
-               break;
-
-       case SOC_ID_TWINPASS:
-               i->name = SOC_TWINPASS;
-               i->type = SOC_TYPE_DANUBE;
-               break;
-
-       case SOC_ID_ARX188:
-       case SOC_ID_ARX168:
-       case SOC_ID_ARX182:
-               i->name = SOC_AR9;
-               i->type = SOC_TYPE_AR9;
-               break;
-
-       default:
-               unreachable();
-               break;
-       }
-}
diff --git a/arch/mips/lantiq/xway/prom.c b/arch/mips/lantiq/xway/prom.c
new file mode 100644 (file)
index 0000000..248429a
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ *  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.
+ *
+ *  Copyright (C) 2010 John Crispin <blogic@openwrt.org>
+ */
+
+#include <linux/export.h>
+#include <linux/clk.h>
+#include <asm/bootinfo.h>
+#include <asm/time.h>
+
+#include <lantiq_soc.h>
+
+#include "../prom.h"
+
+#define SOC_DANUBE     "Danube"
+#define SOC_TWINPASS   "Twinpass"
+#define SOC_AMAZON_SE  "Amazon_SE"
+#define SOC_AR9                "AR9"
+#define SOC_GR9                "GR9"
+#define SOC_VR9                "VR9"
+
+#define COMP_DANUBE    "lantiq,danube"
+#define COMP_TWINPASS  "lantiq,twinpass"
+#define COMP_AMAZON_SE "lantiq,ase"
+#define COMP_AR9       "lantiq,ar9"
+#define COMP_GR9       "lantiq,gr9"
+#define COMP_VR9       "lantiq,vr9"
+
+#define PART_SHIFT     12
+#define PART_MASK      0x0FFFFFFF
+#define REV_SHIFT      28
+#define REV_MASK       0xF0000000
+
+void __init ltq_soc_detect(struct ltq_soc_info *i)
+{
+       i->partnum = (ltq_r32(LTQ_MPS_CHIPID) & PART_MASK) >> PART_SHIFT;
+       i->rev = (ltq_r32(LTQ_MPS_CHIPID) & REV_MASK) >> REV_SHIFT;
+       sprintf(i->rev_type, "1.%d", i->rev);
+       switch (i->partnum) {
+       case SOC_ID_DANUBE1:
+       case SOC_ID_DANUBE2:
+               i->name = SOC_DANUBE;
+               i->type = SOC_TYPE_DANUBE;
+               i->compatible = COMP_DANUBE;
+               break;
+
+       case SOC_ID_TWINPASS:
+               i->name = SOC_TWINPASS;
+               i->type = SOC_TYPE_DANUBE;
+               i->compatible = COMP_TWINPASS;
+               break;
+
+       case SOC_ID_ARX188:
+       case SOC_ID_ARX168_1:
+       case SOC_ID_ARX168_2:
+       case SOC_ID_ARX182:
+               i->name = SOC_AR9;
+               i->type = SOC_TYPE_AR9;
+               i->compatible = COMP_AR9;
+               break;
+
+       case SOC_ID_GRX188:
+       case SOC_ID_GRX168:
+               i->name = SOC_GR9;
+               i->type = SOC_TYPE_AR9;
+               i->compatible = COMP_GR9;
+               break;
+
+       case SOC_ID_AMAZON_SE_1:
+       case SOC_ID_AMAZON_SE_2:
+#ifdef CONFIG_PCI
+               panic("ase is only supported for non pci kernels");
+#endif
+               i->name = SOC_AMAZON_SE;
+               i->type = SOC_TYPE_AMAZON_SE;
+               i->compatible = COMP_AMAZON_SE;
+               break;
+
+       case SOC_ID_VRX282:
+       case SOC_ID_VRX268:
+       case SOC_ID_VRX288:
+               i->name = SOC_VR9;
+               i->type = SOC_TYPE_VR9;
+               i->compatible = COMP_VR9;
+               break;
+
+       case SOC_ID_GRX268:
+       case SOC_ID_GRX288:
+               i->name = SOC_GR9;
+               i->type = SOC_TYPE_VR9;
+               i->compatible = COMP_GR9;
+               break;
+
+       case SOC_ID_VRX268_2:
+       case SOC_ID_VRX288_2:
+               i->name = SOC_VR9;
+               i->type = SOC_TYPE_VR9_2;
+               i->compatible = COMP_VR9;
+               break;
+
+       case SOC_ID_GRX282_2:
+       case SOC_ID_GRX288_2:
+               i->name = SOC_GR9;
+               i->type = SOC_TYPE_VR9_2;
+               i->compatible = COMP_GR9;
+               break;
+
+       default:
+               unreachable();
+               break;
+       }
+}
index 8b66bd87f0c1cbf4a698c47cf78721da799aee0f..22c55f73aa9d646c535560a9938e27663970a9d9 100644 (file)
 #include <linux/ioport.h>
 #include <linux/pm.h>
 #include <linux/export.h>
+#include <linux/delay.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+
 #include <asm/reboot.h>
 
 #include <lantiq_soc.h>
 
+#include "../prom.h"
+
 #define ltq_rcu_w32(x, y)      ltq_w32((x), ltq_rcu_membase + (y))
 #define ltq_rcu_r32(x)         ltq_r32(ltq_rcu_membase + (x))
 
-/* register definitions */
-#define LTQ_RCU_RST            0x0010
-#define LTQ_RCU_RST_ALL                0x40000000
-
-#define LTQ_RCU_RST_STAT       0x0014
-#define LTQ_RCU_STAT_SHIFT     26
+/* reset request register */
+#define RCU_RST_REQ            0x0010
+/* reset status register */
+#define RCU_RST_STAT           0x0014
 
-static struct resource ltq_rcu_resource = {
-       .name   = "rcu",
-       .start  = LTQ_RCU_BASE_ADDR,
-       .end    = LTQ_RCU_BASE_ADDR + LTQ_RCU_SIZE - 1,
-       .flags  = IORESOURCE_MEM,
-};
+/* reboot bit */
+#define RCU_RD_SRST            BIT(30)
+/* reset cause */
+#define RCU_STAT_SHIFT         26
+/* boot selection */
+#define RCU_BOOT_SEL_SHIFT     26
+#define RCU_BOOT_SEL_MASK      0x7
 
 /* remapped base addr of the reset control unit */
 static void __iomem *ltq_rcu_membase;
@@ -38,48 +43,64 @@ static void __iomem *ltq_rcu_membase;
 /* This function is used by the watchdog driver */
 int ltq_reset_cause(void)
 {
-       u32 val = ltq_rcu_r32(LTQ_RCU_RST_STAT);
-       return val >> LTQ_RCU_STAT_SHIFT;
+       u32 val = ltq_rcu_r32(RCU_RST_STAT);
+       return val >> RCU_STAT_SHIFT;
 }
 EXPORT_SYMBOL_GPL(ltq_reset_cause);
 
+/* allow platform code to find out what source we booted from */
+unsigned char ltq_boot_select(void)
+{
+       u32 val = ltq_rcu_r32(RCU_RST_STAT);
+       return (val >> RCU_BOOT_SEL_SHIFT) & RCU_BOOT_SEL_MASK;
+}
+
+/* reset a io domain for u micro seconds */
+void ltq_reset_once(unsigned int module, ulong u)
+{
+       ltq_rcu_w32(ltq_rcu_r32(RCU_RST_REQ) | module, RCU_RST_REQ);
+       udelay(u);
+       ltq_rcu_w32(ltq_rcu_r32(RCU_RST_REQ) & ~module, RCU_RST_REQ);
+}
+
 static void ltq_machine_restart(char *command)
 {
-       pr_notice("System restart\n");
        local_irq_disable();
-       ltq_rcu_w32(ltq_rcu_r32(LTQ_RCU_RST) | LTQ_RCU_RST_ALL, LTQ_RCU_RST);
+       ltq_rcu_w32(ltq_rcu_r32(RCU_RST_REQ) | RCU_RD_SRST, RCU_RST_REQ);
        unreachable();
 }
 
 static void ltq_machine_halt(void)
 {
-       pr_notice("System halted.\n");
        local_irq_disable();
        unreachable();
 }
 
 static void ltq_machine_power_off(void)
 {
-       pr_notice("Please turn off the power now.\n");
        local_irq_disable();
        unreachable();
 }
 
 static int __init mips_reboot_setup(void)
 {
-       /* insert and request the memory region */
-       if (insert_resource(&iomem_resource, &ltq_rcu_resource) < 0)
-               panic("Failed to insert rcu memory");
+       struct resource res;
+       struct device_node *np =
+               of_find_compatible_node(NULL, NULL, "lantiq,rcu-xway");
+
+       /* check if all the reset register range is available */
+       if (!np)
+               panic("Failed to load reset resources from devicetree");
+
+       if (of_address_to_resource(np, 0, &res))
+               panic("Failed to get rcu memory range");
 
-       if (request_mem_region(ltq_rcu_resource.start,
-                       resource_size(&ltq_rcu_resource), "rcu") < 0)
-               panic("Failed to request rcu memory");
+       if (request_mem_region(res.start, resource_size(&res), res.name) < 0)
+               pr_err("Failed to request rcu memory");
 
-       /* remap rcu register range */
-       ltq_rcu_membase = ioremap_nocache(ltq_rcu_resource.start,
-                               resource_size(&ltq_rcu_resource));
+       ltq_rcu_membase = ioremap_nocache(res.start, resource_size(&res));
        if (!ltq_rcu_membase)
-               panic("Failed to remap rcu memory");
+               panic("Failed to remap core memory");
 
        _machine_restart = ltq_machine_restart;
        _machine_halt = ltq_machine_halt;
diff --git a/arch/mips/lantiq/xway/setup-ase.c b/arch/mips/lantiq/xway/setup-ase.c
deleted file mode 100644 (file)
index f6f3267..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- *  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.
- *
- *  Copyright (C) 2011 John Crispin <blogic@openwrt.org>
- */
-
-#include <lantiq_soc.h>
-
-#include "../prom.h"
-#include "devices.h"
-
-void __init ltq_soc_setup(void)
-{
-       ltq_register_ase_asc();
-       ltq_register_gpio();
-       ltq_register_wdt();
-}
diff --git a/arch/mips/lantiq/xway/setup-xway.c b/arch/mips/lantiq/xway/setup-xway.c
deleted file mode 100644 (file)
index c292f64..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *  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.
- *
- *  Copyright (C) 2011 John Crispin <blogic@openwrt.org>
- */
-
-#include <lantiq_soc.h>
-
-#include "../prom.h"
-#include "devices.h"
-
-void __init ltq_soc_setup(void)
-{
-       ltq_register_asc(0);
-       ltq_register_asc(1);
-       ltq_register_gpio();
-       ltq_register_wdt();
-}
diff --git a/arch/mips/lantiq/xway/sysctrl.c b/arch/mips/lantiq/xway/sysctrl.c
new file mode 100644 (file)
index 0000000..83780f7
--- /dev/null
@@ -0,0 +1,371 @@
+/*
+ *  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.
+ *
+ *  Copyright (C) 2011-2012 John Crispin <blogic@openwrt.org>
+ */
+
+#include <linux/ioport.h>
+#include <linux/export.h>
+#include <linux/clkdev.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/of_address.h>
+
+#include <lantiq_soc.h>
+
+#include "../clk.h"
+#include "../prom.h"
+
+/* clock control register */
+#define CGU_IFCCR      0x0018
+/* system clock register */
+#define CGU_SYS                0x0010
+/* pci control register */
+#define CGU_PCICR      0x0034
+/* ephy configuration register */
+#define CGU_EPHY       0x10
+/* power control register */
+#define PMU_PWDCR      0x1C
+/* power status register */
+#define PMU_PWDSR      0x20
+/* power control register */
+#define PMU_PWDCR1     0x24
+/* power status register */
+#define PMU_PWDSR1     0x28
+/* power control register */
+#define PWDCR(x) ((x) ? (PMU_PWDCR1) : (PMU_PWDCR))
+/* power status register */
+#define PWDSR(x) ((x) ? (PMU_PWDSR1) : (PMU_PWDSR))
+
+/* clock gates that we can en/disable */
+#define PMU_USB0_P     BIT(0)
+#define PMU_PCI                BIT(4)
+#define PMU_DMA                BIT(5)
+#define PMU_USB0       BIT(6)
+#define PMU_ASC0       BIT(7)
+#define PMU_EPHY       BIT(7)  /* ase */
+#define PMU_SPI                BIT(8)
+#define PMU_DFE                BIT(9)
+#define PMU_EBU                BIT(10)
+#define PMU_STP                BIT(11)
+#define PMU_GPT                BIT(12)
+#define PMU_AHBS       BIT(13) /* vr9 */
+#define PMU_FPI                BIT(14)
+#define PMU_AHBM       BIT(15)
+#define PMU_ASC1       BIT(17)
+#define PMU_PPE_QSB    BIT(18)
+#define PMU_PPE_SLL01  BIT(19)
+#define PMU_PPE_TC     BIT(21)
+#define PMU_PPE_EMA    BIT(22)
+#define PMU_PPE_DPLUM  BIT(23)
+#define PMU_PPE_DPLUS  BIT(24)
+#define PMU_USB1_P     BIT(26)
+#define PMU_USB1       BIT(27)
+#define PMU_SWITCH     BIT(28)
+#define PMU_PPE_TOP    BIT(29)
+#define PMU_GPHY       BIT(30)
+#define PMU_PCIE_CLK   BIT(31)
+
+#define PMU1_PCIE_PHY  BIT(0)
+#define PMU1_PCIE_CTL  BIT(1)
+#define PMU1_PCIE_PDI  BIT(4)
+#define PMU1_PCIE_MSI  BIT(5)
+
+#define pmu_w32(x, y)  ltq_w32((x), pmu_membase + (y))
+#define pmu_r32(x)     ltq_r32(pmu_membase + (x))
+
+static void __iomem *pmu_membase;
+void __iomem *ltq_cgu_membase;
+void __iomem *ltq_ebu_membase;
+
+/* legacy function kept alive to ease clkdev transition */
+void ltq_pmu_enable(unsigned int module)
+{
+       int err = 1000000;
+
+       pmu_w32(pmu_r32(PMU_PWDCR) & ~module, PMU_PWDCR);
+       do {} while (--err && (pmu_r32(PMU_PWDSR) & module));
+
+       if (!err)
+               panic("activating PMU module failed!");
+}
+EXPORT_SYMBOL(ltq_pmu_enable);
+
+/* legacy function kept alive to ease clkdev transition */
+void ltq_pmu_disable(unsigned int module)
+{
+       pmu_w32(pmu_r32(PMU_PWDCR) | module, PMU_PWDCR);
+}
+EXPORT_SYMBOL(ltq_pmu_disable);
+
+/* enable a hw clock */
+static int cgu_enable(struct clk *clk)
+{
+       ltq_cgu_w32(ltq_cgu_r32(CGU_IFCCR) | clk->bits, CGU_IFCCR);
+       return 0;
+}
+
+/* disable a hw clock */
+static void cgu_disable(struct clk *clk)
+{
+       ltq_cgu_w32(ltq_cgu_r32(CGU_IFCCR) & ~clk->bits, CGU_IFCCR);
+}
+
+/* enable a clock gate */
+static int pmu_enable(struct clk *clk)
+{
+       int retry = 1000000;
+
+       pmu_w32(pmu_r32(PWDCR(clk->module)) & ~clk->bits,
+               PWDCR(clk->module));
+       do {} while (--retry && (pmu_r32(PWDSR(clk->module)) & clk->bits));
+
+       if (!retry)
+               panic("activating PMU module failed!\n");
+
+       return 0;
+}
+
+/* disable a clock gate */
+static void pmu_disable(struct clk *clk)
+{
+       pmu_w32(pmu_r32(PWDCR(clk->module)) | clk->bits,
+               PWDCR(clk->module));
+}
+
+/* the pci enable helper */
+static int pci_enable(struct clk *clk)
+{
+       unsigned int ifccr = ltq_cgu_r32(CGU_IFCCR);
+       /* set bus clock speed */
+       if (of_machine_is_compatible("lantiq,ar9")) {
+               ifccr &= ~0x1f00000;
+               if (clk->rate == CLOCK_33M)
+                       ifccr |= 0xe00000;
+               else
+                       ifccr |= 0x700000; /* 62.5M */
+       } else {
+               ifccr &= ~0xf00000;
+               if (clk->rate == CLOCK_33M)
+                       ifccr |= 0x800000;
+               else
+                       ifccr |= 0x400000; /* 62.5M */
+       }
+       ltq_cgu_w32(ifccr, CGU_IFCCR);
+       pmu_enable(clk);
+       return 0;
+}
+
+/* enable the external clock as a source */
+static int pci_ext_enable(struct clk *clk)
+{
+       ltq_cgu_w32(ltq_cgu_r32(CGU_IFCCR) & ~(1 << 16),
+               CGU_IFCCR);
+       ltq_cgu_w32((1 << 30), CGU_PCICR);
+       return 0;
+}
+
+/* disable the external clock as a source */
+static void pci_ext_disable(struct clk *clk)
+{
+       ltq_cgu_w32(ltq_cgu_r32(CGU_IFCCR) | (1 << 16),
+               CGU_IFCCR);
+       ltq_cgu_w32((1 << 31) | (1 << 30), CGU_PCICR);
+}
+
+/* enable a clockout source */
+static int clkout_enable(struct clk *clk)
+{
+       int i;
+
+       /* get the correct rate */
+       for (i = 0; i < 4; i++) {
+               if (clk->rates[i] == clk->rate) {
+                       int shift = 14 - (2 * clk->module);
+                       unsigned int ifccr = ltq_cgu_r32(CGU_IFCCR);
+
+                       ifccr &= ~(3 << shift);
+                       ifccr |= i << shift;
+                       ltq_cgu_w32(ifccr, CGU_IFCCR);
+                       return 0;
+               }
+       }
+       return -1;
+}
+
+/* manage the clock gates via PMU */
+static void clkdev_add_pmu(const char *dev, const char *con,
+                                       unsigned int module, unsigned int bits)
+{
+       struct clk *clk = kzalloc(sizeof(struct clk), GFP_KERNEL);
+
+       clk->cl.dev_id = dev;
+       clk->cl.con_id = con;
+       clk->cl.clk = clk;
+       clk->enable = pmu_enable;
+       clk->disable = pmu_disable;
+       clk->module = module;
+       clk->bits = bits;
+       clkdev_add(&clk->cl);
+}
+
+/* manage the clock generator */
+static void clkdev_add_cgu(const char *dev, const char *con,
+                                       unsigned int bits)
+{
+       struct clk *clk = kzalloc(sizeof(struct clk), GFP_KERNEL);
+
+       clk->cl.dev_id = dev;
+       clk->cl.con_id = con;
+       clk->cl.clk = clk;
+       clk->enable = cgu_enable;
+       clk->disable = cgu_disable;
+       clk->bits = bits;
+       clkdev_add(&clk->cl);
+}
+
+/* pci needs its own enable function as the setup is a bit more complex */
+static unsigned long valid_pci_rates[] = {CLOCK_33M, CLOCK_62_5M, 0};
+
+static void clkdev_add_pci(void)
+{
+       struct clk *clk = kzalloc(sizeof(struct clk), GFP_KERNEL);
+       struct clk *clk_ext = kzalloc(sizeof(struct clk), GFP_KERNEL);
+
+       /* main pci clock */
+       clk->cl.dev_id = "17000000.pci";
+       clk->cl.con_id = NULL;
+       clk->cl.clk = clk;
+       clk->rate = CLOCK_33M;
+       clk->rates = valid_pci_rates;
+       clk->enable = pci_enable;
+       clk->disable = pmu_disable;
+       clk->module = 0;
+       clk->bits = PMU_PCI;
+       clkdev_add(&clk->cl);
+
+       /* use internal/external bus clock */
+       clk_ext->cl.dev_id = "17000000.pci";
+       clk_ext->cl.con_id = "external";
+       clk_ext->cl.clk = clk_ext;
+       clk_ext->enable = pci_ext_enable;
+       clk_ext->disable = pci_ext_disable;
+       clkdev_add(&clk_ext->cl);
+}
+
+/* xway socs can generate clocks on gpio pins */
+static unsigned long valid_clkout_rates[4][5] = {
+       {CLOCK_32_768K, CLOCK_1_536M, CLOCK_2_5M, CLOCK_12M, 0},
+       {CLOCK_40M, CLOCK_12M, CLOCK_24M, CLOCK_48M, 0},
+       {CLOCK_25M, CLOCK_40M, CLOCK_30M, CLOCK_60M, 0},
+       {CLOCK_12M, CLOCK_50M, CLOCK_32_768K, CLOCK_25M, 0},
+};
+
+static void clkdev_add_clkout(void)
+{
+       int i;
+
+       for (i = 0; i < 4; i++) {
+               struct clk *clk;
+               char *name;
+
+               name = kzalloc(sizeof("clkout0"), GFP_KERNEL);
+               sprintf(name, "clkout%d", i);
+
+               clk = kzalloc(sizeof(struct clk), GFP_KERNEL);
+               clk->cl.dev_id = "1f103000.cgu";
+               clk->cl.con_id = name;
+               clk->cl.clk = clk;
+               clk->rate = 0;
+               clk->rates = valid_clkout_rates[i];
+               clk->enable = clkout_enable;
+               clk->module = i;
+               clkdev_add(&clk->cl);
+       }
+}
+
+/* bring up all register ranges that we need for basic system control */
+void __init ltq_soc_init(void)
+{
+       struct resource res_pmu, res_cgu, res_ebu;
+       struct device_node *np_pmu =
+                       of_find_compatible_node(NULL, NULL, "lantiq,pmu-xway");
+       struct device_node *np_cgu =
+                       of_find_compatible_node(NULL, NULL, "lantiq,cgu-xway");
+       struct device_node *np_ebu =
+                       of_find_compatible_node(NULL, NULL, "lantiq,ebu-xway");
+
+       /* check if all the core register ranges are available */
+       if (!np_pmu || !np_cgu || !np_ebu)
+               panic("Failed to load core nodess from devicetree");
+
+       if (of_address_to_resource(np_pmu, 0, &res_pmu) ||
+                       of_address_to_resource(np_cgu, 0, &res_cgu) ||
+                       of_address_to_resource(np_ebu, 0, &res_ebu))
+               panic("Failed to get core resources");
+
+       if ((request_mem_region(res_pmu.start, resource_size(&res_pmu),
+                               res_pmu.name) < 0) ||
+               (request_mem_region(res_cgu.start, resource_size(&res_cgu),
+                               res_cgu.name) < 0) ||
+               (request_mem_region(res_ebu.start, resource_size(&res_ebu),
+                               res_ebu.name) < 0))
+               pr_err("Failed to request core reources");
+
+       pmu_membase = ioremap_nocache(res_pmu.start, resource_size(&res_pmu));
+       ltq_cgu_membase = ioremap_nocache(res_cgu.start,
+                                               resource_size(&res_cgu));
+       ltq_ebu_membase = ioremap_nocache(res_ebu.start,
+                                               resource_size(&res_ebu));
+       if (!pmu_membase || !ltq_cgu_membase || !ltq_ebu_membase)
+               panic("Failed to remap core resources");
+
+       /* make sure to unprotect the memory region where flash is located */
+       ltq_ebu_w32(ltq_ebu_r32(LTQ_EBU_BUSCON0) & ~EBU_WRDIS, LTQ_EBU_BUSCON0);
+
+       /* add our generic xway clocks */
+       clkdev_add_pmu("10000000.fpi", NULL, 0, PMU_FPI);
+       clkdev_add_pmu("1e100400.serial", NULL, 0, PMU_ASC0);
+       clkdev_add_pmu("1e100a00.gptu", NULL, 0, PMU_GPT);
+       clkdev_add_pmu("1e100bb0.stp", NULL, 0, PMU_STP);
+       clkdev_add_pmu("1e104100.dma", NULL, 0, PMU_DMA);
+       clkdev_add_pmu("1e100800.spi", NULL, 0, PMU_SPI);
+       clkdev_add_pmu("1e105300.ebu", NULL, 0, PMU_EBU);
+       clkdev_add_clkout();
+
+       /* add the soc dependent clocks */
+       if (!of_machine_is_compatible("lantiq,vr9"))
+               clkdev_add_pmu("1e180000.etop", NULL, 0, PMU_PPE);
+
+       if (!of_machine_is_compatible("lantiq,ase")) {
+               clkdev_add_pmu("1e100c00.serial", NULL, 0, PMU_ASC1);
+               clkdev_add_pci();
+       }
+
+       if (of_machine_is_compatible("lantiq,ase")) {
+               if (ltq_cgu_r32(CGU_SYS) & (1 << 5))
+                       clkdev_add_static(CLOCK_266M, CLOCK_133M, CLOCK_133M);
+               else
+                       clkdev_add_static(CLOCK_133M, CLOCK_133M, CLOCK_133M);
+               clkdev_add_cgu("1e180000.etop", "ephycgu", CGU_EPHY),
+               clkdev_add_pmu("1e180000.etop", "ephy", 0, PMU_EPHY);
+       } else if (of_machine_is_compatible("lantiq,vr9")) {
+               clkdev_add_static(ltq_vr9_cpu_hz(), ltq_vr9_fpi_hz(),
+                               ltq_vr9_fpi_hz());
+               clkdev_add_pmu("1d900000.pcie", "phy", 1, PMU1_PCIE_PHY);
+               clkdev_add_pmu("1d900000.pcie", "bus", 0, PMU_PCIE_CLK);
+               clkdev_add_pmu("1d900000.pcie", "msi", 1, PMU1_PCIE_MSI);
+               clkdev_add_pmu("1d900000.pcie", "pdi", 1, PMU1_PCIE_PDI);
+               clkdev_add_pmu("1d900000.pcie", "ctl", 1, PMU1_PCIE_CTL);
+               clkdev_add_pmu("1d900000.pcie", "ahb", 0, PMU_AHBM | PMU_AHBS);
+       } else if (of_machine_is_compatible("lantiq,ar9")) {
+               clkdev_add_static(ltq_ar9_cpu_hz(), ltq_ar9_fpi_hz(),
+                               ltq_ar9_fpi_hz());
+               clkdev_add_pmu("1e180000.etop", "switch", 0, PMU_SWITCH);
+       } else {
+               clkdev_add_static(ltq_danube_cpu_hz(), ltq_danube_fpi_hz(),
+                               ltq_danube_fpi_hz());
+       }
+}
index 47037ec5589b88bca92df21c81a3a890f937bd36..44e69e7a4519110b3efd1e30a755a8ec3e0692ea 100644 (file)
@@ -21,6 +21,7 @@
 #include <asm/page.h>
 #include <asm/pgtable.h>
 #include <asm/r4kcache.h>
+#include <asm/traps.h>
 #include <asm/mmu_context.h>
 #include <asm/war.h>
 
@@ -248,6 +249,11 @@ static void __cpuinit probe_octeon(void)
        }
 }
 
+static void  __cpuinit octeon_cache_error_setup(void)
+{
+       extern char except_vec2_octeon;
+       set_handler(0x100, &except_vec2_octeon, 0x80);
+}
 
 /**
  * Setup the Octeon cache flush routines
@@ -255,12 +261,6 @@ static void __cpuinit probe_octeon(void)
  */
 void __cpuinit octeon_cache_init(void)
 {
-       extern unsigned long ebase;
-       extern char except_vec2_octeon;
-
-       memcpy((void *)(ebase + 0x100), &except_vec2_octeon, 0x80);
-       octeon_flush_cache_sigtramp(ebase + 0x100);
-
        probe_octeon();
 
        shm_align_mask = PAGE_SIZE - 1;
@@ -280,6 +280,8 @@ void __cpuinit octeon_cache_init(void)
 
        build_clear_page();
        build_copy_page();
+
+       board_cache_error_setup = octeon_cache_error_setup;
 }
 
 /**
index bda8eb26ece74098ecaa49c2693ab40dd2bcb772..5109be96d98d099ec8509dd1de6a9048f5c4b82d 100644 (file)
@@ -32,7 +32,7 @@
 #include <asm/mmu_context.h>
 #include <asm/war.h>
 #include <asm/cacheflush.h> /* for run_uncached() */
-
+#include <asm/traps.h>
 
 /*
  * Special Variant of smp_call_function for use by cache functions:
@@ -1385,10 +1385,8 @@ static int __init setcoherentio(char *str)
 __setup("coherentio", setcoherentio);
 #endif
 
-void __cpuinit r4k_cache_init(void)
+static void __cpuinit r4k_cache_error_setup(void)
 {
-       extern void build_clear_page(void);
-       extern void build_copy_page(void);
        extern char __weak except_vec2_generic;
        extern char __weak except_vec2_sb1;
        struct cpuinfo_mips *c = &current_cpu_data;
@@ -1403,6 +1401,13 @@ void __cpuinit r4k_cache_init(void)
                set_uncached_handler(0x100, &except_vec2_generic, 0x80);
                break;
        }
+}
+
+void __cpuinit r4k_cache_init(void)
+{
+       extern void build_clear_page(void);
+       extern void build_copy_page(void);
+       struct cpuinfo_mips *c = &current_cpu_data;
 
        probe_pcache();
        setup_scache();
@@ -1465,4 +1470,5 @@ void __cpuinit r4k_cache_init(void)
        local_r4k___flush_cache_all(NULL);
 #endif
        coherency_setup();
+       board_cache_error_setup = r4k_cache_error_setup;
 }
index 29f2f13eb31c433a0a6a79208dabee4706fdcf3a..1208c280f77dbb0cc2596890dbccab3f1c45c3e0 100644 (file)
@@ -1,5 +1,3 @@
-ccflags-y := -Werror
-
 obj-$(CONFIG_OPROFILE) += oprofile.o
 
 DRIVER_OBJS = $(addprefix ../../../drivers/oprofile/, \
index 54759f1669d3a00aca9e07fb213842b397b4b800..baba3bcaa3c28100067a39d9e1a1132a5a8d02b4 100644 (file)
@@ -298,6 +298,11 @@ static void reset_counters(void *arg)
        }
 }
 
+static irqreturn_t mipsxx_perfcount_int(int irq, void *dev_id)
+{
+       return mipsxx_perfcount_handler();
+}
+
 static int __init mipsxx_init(void)
 {
        int counters;
@@ -374,6 +379,10 @@ static int __init mipsxx_init(void)
        save_perf_irq = perf_irq;
        perf_irq = mipsxx_perfcount_handler;
 
+       if ((cp0_perfcount_irq >= 0) && (cp0_compare_irq != cp0_perfcount_irq))
+               return request_irq(cp0_perfcount_irq, mipsxx_perfcount_int,
+                       0, "Perfcounter", save_perf_irq);
+
        return 0;
 }
 
@@ -381,6 +390,9 @@ static void mipsxx_exit(void)
 {
        int counters = op_model_mipsxx_ops.num_counters;
 
+       if ((cp0_perfcount_irq >= 0) && (cp0_compare_irq != cp0_perfcount_irq))
+               free_irq(cp0_perfcount_irq, save_perf_irq);
+
        counters = counters_per_cpu_to_total(counters);
        on_each_cpu(reset_counters, (void *)(long)counters, 1);
 
index c3ac4b086eb203738d2c2bfdbd32b61171ded9ab..c703f43a9914bee08452c9db536b53b82fc9f037 100644 (file)
@@ -19,7 +19,8 @@ obj-$(CONFIG_BCM47XX)         += pci-bcm47xx.o
 obj-$(CONFIG_BCM63XX)          += pci-bcm63xx.o fixup-bcm63xx.o \
                                        ops-bcm63xx.o
 obj-$(CONFIG_MIPS_ALCHEMY)     += pci-alchemy.o
-obj-$(CONFIG_SOC_AR724X)       += pci-ath724x.o
+obj-$(CONFIG_SOC_AR71XX)       += pci-ar71xx.o
+obj-$(CONFIG_PCI_AR724X)       += pci-ar724x.o
 
 #
 # These are still pretty much in the old state, watch, go blind.
@@ -41,7 +42,8 @@ obj-$(CONFIG_SIBYTE_SB1250)   += fixup-sb1250.o pci-sb1250.o
 obj-$(CONFIG_SIBYTE_BCM112X)   += fixup-sb1250.o pci-sb1250.o
 obj-$(CONFIG_SIBYTE_BCM1x80)   += pci-bcm1480.o pci-bcm1480ht.o
 obj-$(CONFIG_SNI_RM)           += fixup-sni.o ops-sni.o
-obj-$(CONFIG_SOC_XWAY)         += pci-lantiq.o ops-lantiq.o
+obj-$(CONFIG_LANTIQ)           += fixup-lantiq.o
+obj-$(CONFIG_PCI_LANTIQ)       += pci-lantiq.o ops-lantiq.o
 obj-$(CONFIG_TANBAC_TB0219)    += fixup-tb0219.o
 obj-$(CONFIG_TANBAC_TB0226)    += fixup-tb0226.o
 obj-$(CONFIG_TANBAC_TB0287)    += fixup-tb0287.o
diff --git a/arch/mips/pci/fixup-lantiq.c b/arch/mips/pci/fixup-lantiq.c
new file mode 100644 (file)
index 0000000..6c829df
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ *  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.
+ *
+ *  Copyright (C) 2012 John Crispin <blogic@openwrt.org>
+ */
+
+#include <linux/of_irq.h>
+#include <linux/of_pci.h>
+
+int (*ltq_pci_plat_arch_init)(struct pci_dev *dev) = NULL;
+int (*ltq_pci_plat_dev_init)(struct pci_dev *dev) = NULL;
+
+int pcibios_plat_dev_init(struct pci_dev *dev)
+{
+       if (ltq_pci_plat_arch_init)
+               return ltq_pci_plat_arch_init(dev);
+
+       if (ltq_pci_plat_dev_init)
+               return ltq_pci_plat_dev_init(dev);
+
+       return 0;
+}
+
+int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
+{
+       struct of_irq dev_irq;
+       int irq;
+
+       if (of_irq_map_pci(dev, &dev_irq)) {
+               dev_err(&dev->dev, "trying to map irq for unknown slot:%d pin:%d\n",
+                       slot, pin);
+               return 0;
+       }
+       irq = irq_create_of_mapping(dev_irq.controller, dev_irq.specifier,
+                                       dev_irq.size);
+       dev_info(&dev->dev, "SLOT:%d PIN:%d IRQ:%d\n", slot, pin, irq);
+       return irq;
+}
index d657ee0bc131c8c3c26c1095b35a312c666ddd94..afd221122d222722d355c6723547ae3657853c5f 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/pci.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
+#include <linux/export.h>
 
 #include <loongson.h>
 
diff --git a/arch/mips/pci/pci-ar71xx.c b/arch/mips/pci/pci-ar71xx.c
new file mode 100644 (file)
index 0000000..1552522
--- /dev/null
@@ -0,0 +1,375 @@
+/*
+ *  Atheros AR71xx PCI host controller driver
+ *
+ *  Copyright (C) 2008-2011 Gabor Juhos <juhosg@openwrt.org>
+ *  Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
+ *
+ *  Parts of this file are based on Atheros' 2.6.15 BSP
+ *
+ *  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/resource.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+#include <linux/pci.h>
+#include <linux/pci_regs.h>
+#include <linux/interrupt.h>
+
+#include <asm/mach-ath79/ar71xx_regs.h>
+#include <asm/mach-ath79/ath79.h>
+#include <asm/mach-ath79/pci.h>
+
+#define AR71XX_PCI_MEM_BASE    0x10000000
+#define AR71XX_PCI_MEM_SIZE    0x08000000
+
+#define AR71XX_PCI_WIN0_OFFS           0x10000000
+#define AR71XX_PCI_WIN1_OFFS           0x11000000
+#define AR71XX_PCI_WIN2_OFFS           0x12000000
+#define AR71XX_PCI_WIN3_OFFS           0x13000000
+#define AR71XX_PCI_WIN4_OFFS           0x14000000
+#define AR71XX_PCI_WIN5_OFFS           0x15000000
+#define AR71XX_PCI_WIN6_OFFS           0x16000000
+#define AR71XX_PCI_WIN7_OFFS           0x07000000
+
+#define AR71XX_PCI_CFG_BASE            \
+       (AR71XX_PCI_MEM_BASE + AR71XX_PCI_WIN7_OFFS + 0x10000)
+#define AR71XX_PCI_CFG_SIZE            0x100
+
+#define AR71XX_PCI_REG_CRP_AD_CBE      0x00
+#define AR71XX_PCI_REG_CRP_WRDATA      0x04
+#define AR71XX_PCI_REG_CRP_RDDATA      0x08
+#define AR71XX_PCI_REG_CFG_AD          0x0c
+#define AR71XX_PCI_REG_CFG_CBE         0x10
+#define AR71XX_PCI_REG_CFG_WRDATA      0x14
+#define AR71XX_PCI_REG_CFG_RDDATA      0x18
+#define AR71XX_PCI_REG_PCI_ERR         0x1c
+#define AR71XX_PCI_REG_PCI_ERR_ADDR    0x20
+#define AR71XX_PCI_REG_AHB_ERR         0x24
+#define AR71XX_PCI_REG_AHB_ERR_ADDR    0x28
+
+#define AR71XX_PCI_CRP_CMD_WRITE       0x00010000
+#define AR71XX_PCI_CRP_CMD_READ                0x00000000
+#define AR71XX_PCI_CFG_CMD_READ                0x0000000a
+#define AR71XX_PCI_CFG_CMD_WRITE       0x0000000b
+
+#define AR71XX_PCI_INT_CORE            BIT(4)
+#define AR71XX_PCI_INT_DEV2            BIT(2)
+#define AR71XX_PCI_INT_DEV1            BIT(1)
+#define AR71XX_PCI_INT_DEV0            BIT(0)
+
+#define AR71XX_PCI_IRQ_COUNT           5
+
+static DEFINE_SPINLOCK(ar71xx_pci_lock);
+static void __iomem *ar71xx_pcicfg_base;
+
+/* Byte lane enable bits */
+static const u8 ar71xx_pci_ble_table[4][4] = {
+       {0x0, 0xf, 0xf, 0xf},
+       {0xe, 0xd, 0xb, 0x7},
+       {0xc, 0xf, 0x3, 0xf},
+       {0xf, 0xf, 0xf, 0xf},
+};
+
+static const u32 ar71xx_pci_read_mask[8] = {
+       0, 0xff, 0xffff, 0, 0xffffffff, 0, 0, 0
+};
+
+static inline u32 ar71xx_pci_get_ble(int where, int size, int local)
+{
+       u32 t;
+
+       t = ar71xx_pci_ble_table[size & 3][where & 3];
+       BUG_ON(t == 0xf);
+       t <<= (local) ? 20 : 4;
+
+       return t;
+}
+
+static inline u32 ar71xx_pci_bus_addr(struct pci_bus *bus, unsigned int devfn,
+                                     int where)
+{
+       u32 ret;
+
+       if (!bus->number) {
+               /* type 0 */
+               ret = (1 << PCI_SLOT(devfn)) | (PCI_FUNC(devfn) << 8) |
+                     (where & ~3);
+       } else {
+               /* type 1 */
+               ret = (bus->number << 16) | (PCI_SLOT(devfn) << 11) |
+                     (PCI_FUNC(devfn) << 8) | (where & ~3) | 1;
+       }
+
+       return ret;
+}
+
+static int ar71xx_pci_check_error(int quiet)
+{
+       void __iomem *base = ar71xx_pcicfg_base;
+       u32 pci_err;
+       u32 ahb_err;
+
+       pci_err = __raw_readl(base + AR71XX_PCI_REG_PCI_ERR) & 3;
+       if (pci_err) {
+               if (!quiet) {
+                       u32 addr;
+
+                       addr = __raw_readl(base + AR71XX_PCI_REG_PCI_ERR_ADDR);
+                       pr_crit("ar71xx: %s bus error %d at addr 0x%x\n",
+                               "PCI", pci_err, addr);
+               }
+
+               /* clear PCI error status */
+               __raw_writel(pci_err, base + AR71XX_PCI_REG_PCI_ERR);
+       }
+
+       ahb_err = __raw_readl(base + AR71XX_PCI_REG_AHB_ERR) & 1;
+       if (ahb_err) {
+               if (!quiet) {
+                       u32 addr;
+
+                       addr = __raw_readl(base + AR71XX_PCI_REG_AHB_ERR_ADDR);
+                       pr_crit("ar71xx: %s bus error %d at addr 0x%x\n",
+                               "AHB", ahb_err, addr);
+               }
+
+               /* clear AHB error status */
+               __raw_writel(ahb_err, base + AR71XX_PCI_REG_AHB_ERR);
+       }
+
+       return !!(ahb_err | pci_err);
+}
+
+static inline void ar71xx_pci_local_write(int where, int size, u32 value)
+{
+       void __iomem *base = ar71xx_pcicfg_base;
+       u32 ad_cbe;
+
+       value = value << (8 * (where & 3));
+
+       ad_cbe = AR71XX_PCI_CRP_CMD_WRITE | (where & ~3);
+       ad_cbe |= ar71xx_pci_get_ble(where, size, 1);
+
+       __raw_writel(ad_cbe, base + AR71XX_PCI_REG_CRP_AD_CBE);
+       __raw_writel(value, base + AR71XX_PCI_REG_CRP_WRDATA);
+}
+
+static inline int ar71xx_pci_set_cfgaddr(struct pci_bus *bus,
+                                        unsigned int devfn,
+                                        int where, int size, u32 cmd)
+{
+       void __iomem *base = ar71xx_pcicfg_base;
+       u32 addr;
+
+       addr = ar71xx_pci_bus_addr(bus, devfn, where);
+
+       __raw_writel(addr, base + AR71XX_PCI_REG_CFG_AD);
+       __raw_writel(cmd | ar71xx_pci_get_ble(where, size, 0),
+                    base + AR71XX_PCI_REG_CFG_CBE);
+
+       return ar71xx_pci_check_error(1);
+}
+
+static int ar71xx_pci_read_config(struct pci_bus *bus, unsigned int devfn,
+                                 int where, int size, u32 *value)
+{
+       void __iomem *base = ar71xx_pcicfg_base;
+       unsigned long flags;
+       u32 data;
+       int err;
+       int ret;
+
+       ret = PCIBIOS_SUCCESSFUL;
+       data = ~0;
+
+       spin_lock_irqsave(&ar71xx_pci_lock, flags);
+
+       err = ar71xx_pci_set_cfgaddr(bus, devfn, where, size,
+                                    AR71XX_PCI_CFG_CMD_READ);
+       if (err)
+               ret = PCIBIOS_DEVICE_NOT_FOUND;
+       else
+               data = __raw_readl(base + AR71XX_PCI_REG_CFG_RDDATA);
+
+       spin_unlock_irqrestore(&ar71xx_pci_lock, flags);
+
+       *value = (data >> (8 * (where & 3))) & ar71xx_pci_read_mask[size & 7];
+
+       return ret;
+}
+
+static int ar71xx_pci_write_config(struct pci_bus *bus, unsigned int devfn,
+                                  int where, int size, u32 value)
+{
+       void __iomem *base = ar71xx_pcicfg_base;
+       unsigned long flags;
+       int err;
+       int ret;
+
+       value = value << (8 * (where & 3));
+       ret = PCIBIOS_SUCCESSFUL;
+
+       spin_lock_irqsave(&ar71xx_pci_lock, flags);
+
+       err = ar71xx_pci_set_cfgaddr(bus, devfn, where, size,
+                                    AR71XX_PCI_CFG_CMD_WRITE);
+       if (err)
+               ret = PCIBIOS_DEVICE_NOT_FOUND;
+       else
+               __raw_writel(value, base + AR71XX_PCI_REG_CFG_WRDATA);
+
+       spin_unlock_irqrestore(&ar71xx_pci_lock, flags);
+
+       return ret;
+}
+
+static struct pci_ops ar71xx_pci_ops = {
+       .read   = ar71xx_pci_read_config,
+       .write  = ar71xx_pci_write_config,
+};
+
+static struct resource ar71xx_pci_io_resource = {
+       .name           = "PCI IO space",
+       .start          = 0,
+       .end            = 0,
+       .flags          = IORESOURCE_IO,
+};
+
+static struct resource ar71xx_pci_mem_resource = {
+       .name           = "PCI memory space",
+       .start          = AR71XX_PCI_MEM_BASE,
+       .end            = AR71XX_PCI_MEM_BASE + AR71XX_PCI_MEM_SIZE - 1,
+       .flags          = IORESOURCE_MEM
+};
+
+static struct pci_controller ar71xx_pci_controller = {
+       .pci_ops        = &ar71xx_pci_ops,
+       .mem_resource   = &ar71xx_pci_mem_resource,
+       .io_resource    = &ar71xx_pci_io_resource,
+};
+
+static void ar71xx_pci_irq_handler(unsigned int irq, struct irq_desc *desc)
+{
+       void __iomem *base = ath79_reset_base;
+       u32 pending;
+
+       pending = __raw_readl(base + AR71XX_RESET_REG_PCI_INT_STATUS) &
+                 __raw_readl(base + AR71XX_RESET_REG_PCI_INT_ENABLE);
+
+       if (pending & AR71XX_PCI_INT_DEV0)
+               generic_handle_irq(ATH79_PCI_IRQ(0));
+
+       else if (pending & AR71XX_PCI_INT_DEV1)
+               generic_handle_irq(ATH79_PCI_IRQ(1));
+
+       else if (pending & AR71XX_PCI_INT_DEV2)
+               generic_handle_irq(ATH79_PCI_IRQ(2));
+
+       else if (pending & AR71XX_PCI_INT_CORE)
+               generic_handle_irq(ATH79_PCI_IRQ(4));
+
+       else
+               spurious_interrupt();
+}
+
+static void ar71xx_pci_irq_unmask(struct irq_data *d)
+{
+       unsigned int irq = d->irq - ATH79_PCI_IRQ_BASE;
+       void __iomem *base = ath79_reset_base;
+       u32 t;
+
+       t = __raw_readl(base + AR71XX_RESET_REG_PCI_INT_ENABLE);
+       __raw_writel(t | (1 << irq), base + AR71XX_RESET_REG_PCI_INT_ENABLE);
+
+       /* flush write */
+       __raw_readl(base + AR71XX_RESET_REG_PCI_INT_ENABLE);
+}
+
+static void ar71xx_pci_irq_mask(struct irq_data *d)
+{
+       unsigned int irq = d->irq - ATH79_PCI_IRQ_BASE;
+       void __iomem *base = ath79_reset_base;
+       u32 t;
+
+       t = __raw_readl(base + AR71XX_RESET_REG_PCI_INT_ENABLE);
+       __raw_writel(t & ~(1 << irq), base + AR71XX_RESET_REG_PCI_INT_ENABLE);
+
+       /* flush write */
+       __raw_readl(base + AR71XX_RESET_REG_PCI_INT_ENABLE);
+}
+
+static struct irq_chip ar71xx_pci_irq_chip = {
+       .name           = "AR71XX PCI",
+       .irq_mask       = ar71xx_pci_irq_mask,
+       .irq_unmask     = ar71xx_pci_irq_unmask,
+       .irq_mask_ack   = ar71xx_pci_irq_mask,
+};
+
+static __init void ar71xx_pci_irq_init(void)
+{
+       void __iomem *base = ath79_reset_base;
+       int i;
+
+       __raw_writel(0, base + AR71XX_RESET_REG_PCI_INT_ENABLE);
+       __raw_writel(0, base + AR71XX_RESET_REG_PCI_INT_STATUS);
+
+       BUILD_BUG_ON(ATH79_PCI_IRQ_COUNT < AR71XX_PCI_IRQ_COUNT);
+
+       for (i = ATH79_PCI_IRQ_BASE;
+            i < ATH79_PCI_IRQ_BASE + AR71XX_PCI_IRQ_COUNT; i++)
+               irq_set_chip_and_handler(i, &ar71xx_pci_irq_chip,
+                                        handle_level_irq);
+
+       irq_set_chained_handler(ATH79_CPU_IRQ_IP2, ar71xx_pci_irq_handler);
+}
+
+static __init void ar71xx_pci_reset(void)
+{
+       void __iomem *ddr_base = ath79_ddr_base;
+
+       ath79_device_reset_set(AR71XX_RESET_PCI_BUS | AR71XX_RESET_PCI_CORE);
+       mdelay(100);
+
+       ath79_device_reset_clear(AR71XX_RESET_PCI_BUS | AR71XX_RESET_PCI_CORE);
+       mdelay(100);
+
+       __raw_writel(AR71XX_PCI_WIN0_OFFS, ddr_base + AR71XX_DDR_REG_PCI_WIN0);
+       __raw_writel(AR71XX_PCI_WIN1_OFFS, ddr_base + AR71XX_DDR_REG_PCI_WIN1);
+       __raw_writel(AR71XX_PCI_WIN2_OFFS, ddr_base + AR71XX_DDR_REG_PCI_WIN2);
+       __raw_writel(AR71XX_PCI_WIN3_OFFS, ddr_base + AR71XX_DDR_REG_PCI_WIN3);
+       __raw_writel(AR71XX_PCI_WIN4_OFFS, ddr_base + AR71XX_DDR_REG_PCI_WIN4);
+       __raw_writel(AR71XX_PCI_WIN5_OFFS, ddr_base + AR71XX_DDR_REG_PCI_WIN5);
+       __raw_writel(AR71XX_PCI_WIN6_OFFS, ddr_base + AR71XX_DDR_REG_PCI_WIN6);
+       __raw_writel(AR71XX_PCI_WIN7_OFFS, ddr_base + AR71XX_DDR_REG_PCI_WIN7);
+
+       mdelay(100);
+}
+
+__init int ar71xx_pcibios_init(void)
+{
+       u32 t;
+
+       ar71xx_pcicfg_base = ioremap(AR71XX_PCI_CFG_BASE, AR71XX_PCI_CFG_SIZE);
+       if (ar71xx_pcicfg_base == NULL)
+               return -ENOMEM;
+
+       ar71xx_pci_reset();
+
+       /* setup COMMAND register */
+       t = PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE
+         | PCI_COMMAND_PARITY | PCI_COMMAND_SERR | PCI_COMMAND_FAST_BACK;
+       ar71xx_pci_local_write(PCI_COMMAND, 4, t);
+
+       /* clear bus errors */
+       ar71xx_pci_check_error(1);
+
+       ar71xx_pci_irq_init();
+
+       register_pci_controller(&ar71xx_pci_controller);
+
+       return 0;
+}
diff --git a/arch/mips/pci/pci-ar724x.c b/arch/mips/pci/pci-ar724x.c
new file mode 100644 (file)
index 0000000..414a745
--- /dev/null
@@ -0,0 +1,292 @@
+/*
+ *  Atheros AR724X PCI host controller driver
+ *
+ *  Copyright (C) 2011 René Bolldorf <xsecute@googlemail.com>
+ *  Copyright (C) 2009-2011 Gabor Juhos <juhosg@openwrt.org>
+ *
+ *  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/irq.h>
+#include <linux/pci.h>
+#include <asm/mach-ath79/ath79.h>
+#include <asm/mach-ath79/ar71xx_regs.h>
+#include <asm/mach-ath79/pci.h>
+
+#define AR724X_PCI_CFG_BASE    0x14000000
+#define AR724X_PCI_CFG_SIZE    0x1000
+#define AR724X_PCI_CTRL_BASE   (AR71XX_APB_BASE + 0x000f0000)
+#define AR724X_PCI_CTRL_SIZE   0x100
+
+#define AR724X_PCI_MEM_BASE    0x10000000
+#define AR724X_PCI_MEM_SIZE    0x08000000
+
+#define AR724X_PCI_REG_INT_STATUS      0x4c
+#define AR724X_PCI_REG_INT_MASK                0x50
+
+#define AR724X_PCI_INT_DEV0            BIT(14)
+
+#define AR724X_PCI_IRQ_COUNT           1
+
+#define AR7240_BAR0_WAR_VALUE  0xffff
+
+static DEFINE_SPINLOCK(ar724x_pci_lock);
+static void __iomem *ar724x_pci_devcfg_base;
+static void __iomem *ar724x_pci_ctrl_base;
+
+static u32 ar724x_pci_bar0_value;
+static bool ar724x_pci_bar0_is_cached;
+
+static int ar724x_pci_read(struct pci_bus *bus, unsigned int devfn, int where,
+                           int size, uint32_t *value)
+{
+       unsigned long flags;
+       void __iomem *base;
+       u32 data;
+
+       if (devfn)
+               return PCIBIOS_DEVICE_NOT_FOUND;
+
+       base = ar724x_pci_devcfg_base;
+
+       spin_lock_irqsave(&ar724x_pci_lock, flags);
+       data = __raw_readl(base + (where & ~3));
+
+       switch (size) {
+       case 1:
+               if (where & 1)
+                       data >>= 8;
+               if (where & 2)
+                       data >>= 16;
+               data &= 0xff;
+               break;
+       case 2:
+               if (where & 2)
+                       data >>= 16;
+               data &= 0xffff;
+               break;
+       case 4:
+               break;
+       default:
+               spin_unlock_irqrestore(&ar724x_pci_lock, flags);
+
+               return PCIBIOS_BAD_REGISTER_NUMBER;
+       }
+
+       spin_unlock_irqrestore(&ar724x_pci_lock, flags);
+
+       if (where == PCI_BASE_ADDRESS_0 && size == 4 &&
+           ar724x_pci_bar0_is_cached) {
+               /* use the cached value */
+               *value = ar724x_pci_bar0_value;
+       } else {
+               *value = data;
+       }
+
+       return PCIBIOS_SUCCESSFUL;
+}
+
+static int ar724x_pci_write(struct pci_bus *bus, unsigned int devfn, int where,
+                            int size, uint32_t value)
+{
+       unsigned long flags;
+       void __iomem *base;
+       u32 data;
+       int s;
+
+       if (devfn)
+               return PCIBIOS_DEVICE_NOT_FOUND;
+
+       if (soc_is_ar7240() && where == PCI_BASE_ADDRESS_0 && size == 4) {
+               if (value != 0xffffffff) {
+                       /*
+                        * WAR for a hw issue. If the BAR0 register of the
+                        * device is set to the proper base address, the
+                        * memory space of the device is not accessible.
+                        *
+                        * Cache the intended value so it can be read back,
+                        * and write a SoC specific constant value to the
+                        * BAR0 register in order to make the device memory
+                        * accessible.
+                        */
+                       ar724x_pci_bar0_is_cached = true;
+                       ar724x_pci_bar0_value = value;
+
+                       value = AR7240_BAR0_WAR_VALUE;
+               } else {
+                       ar724x_pci_bar0_is_cached = false;
+               }
+       }
+
+       base = ar724x_pci_devcfg_base;
+
+       spin_lock_irqsave(&ar724x_pci_lock, flags);
+       data = __raw_readl(base + (where & ~3));
+
+       switch (size) {
+       case 1:
+               s = ((where & 3) * 8);
+               data &= ~(0xff << s);
+               data |= ((value & 0xff) << s);
+               break;
+       case 2:
+               s = ((where & 2) * 8);
+               data &= ~(0xffff << s);
+               data |= ((value & 0xffff) << s);
+               break;
+       case 4:
+               data = value;
+               break;
+       default:
+               spin_unlock_irqrestore(&ar724x_pci_lock, flags);
+
+               return PCIBIOS_BAD_REGISTER_NUMBER;
+       }
+
+       __raw_writel(data, base + (where & ~3));
+       /* flush write */
+       __raw_readl(base + (where & ~3));
+       spin_unlock_irqrestore(&ar724x_pci_lock, flags);
+
+       return PCIBIOS_SUCCESSFUL;
+}
+
+static struct pci_ops ar724x_pci_ops = {
+       .read   = ar724x_pci_read,
+       .write  = ar724x_pci_write,
+};
+
+static struct resource ar724x_io_resource = {
+       .name   = "PCI IO space",
+       .start  = 0,
+       .end    = 0,
+       .flags  = IORESOURCE_IO,
+};
+
+static struct resource ar724x_mem_resource = {
+       .name   = "PCI memory space",
+       .start  = AR724X_PCI_MEM_BASE,
+       .end    = AR724X_PCI_MEM_BASE + AR724X_PCI_MEM_SIZE - 1,
+       .flags  = IORESOURCE_MEM,
+};
+
+static struct pci_controller ar724x_pci_controller = {
+       .pci_ops        = &ar724x_pci_ops,
+       .io_resource    = &ar724x_io_resource,
+       .mem_resource   = &ar724x_mem_resource,
+};
+
+static void ar724x_pci_irq_handler(unsigned int irq, struct irq_desc *desc)
+{
+       void __iomem *base;
+       u32 pending;
+
+       base = ar724x_pci_ctrl_base;
+
+       pending = __raw_readl(base + AR724X_PCI_REG_INT_STATUS) &
+                 __raw_readl(base + AR724X_PCI_REG_INT_MASK);
+
+       if (pending & AR724X_PCI_INT_DEV0)
+               generic_handle_irq(ATH79_PCI_IRQ(0));
+
+       else
+               spurious_interrupt();
+}
+
+static void ar724x_pci_irq_unmask(struct irq_data *d)
+{
+       void __iomem *base;
+       u32 t;
+
+       base = ar724x_pci_ctrl_base;
+
+       switch (d->irq) {
+       case ATH79_PCI_IRQ(0):
+               t = __raw_readl(base + AR724X_PCI_REG_INT_MASK);
+               __raw_writel(t | AR724X_PCI_INT_DEV0,
+                            base + AR724X_PCI_REG_INT_MASK);
+               /* flush write */
+               __raw_readl(base + AR724X_PCI_REG_INT_MASK);
+       }
+}
+
+static void ar724x_pci_irq_mask(struct irq_data *d)
+{
+       void __iomem *base;
+       u32 t;
+
+       base = ar724x_pci_ctrl_base;
+
+       switch (d->irq) {
+       case ATH79_PCI_IRQ(0):
+               t = __raw_readl(base + AR724X_PCI_REG_INT_MASK);
+               __raw_writel(t & ~AR724X_PCI_INT_DEV0,
+                            base + AR724X_PCI_REG_INT_MASK);
+
+               /* flush write */
+               __raw_readl(base + AR724X_PCI_REG_INT_MASK);
+
+               t = __raw_readl(base + AR724X_PCI_REG_INT_STATUS);
+               __raw_writel(t | AR724X_PCI_INT_DEV0,
+                            base + AR724X_PCI_REG_INT_STATUS);
+
+               /* flush write */
+               __raw_readl(base + AR724X_PCI_REG_INT_STATUS);
+       }
+}
+
+static struct irq_chip ar724x_pci_irq_chip = {
+       .name           = "AR724X PCI ",
+       .irq_mask       = ar724x_pci_irq_mask,
+       .irq_unmask     = ar724x_pci_irq_unmask,
+       .irq_mask_ack   = ar724x_pci_irq_mask,
+};
+
+static void __init ar724x_pci_irq_init(int irq)
+{
+       void __iomem *base;
+       int i;
+
+       base = ar724x_pci_ctrl_base;
+
+       __raw_writel(0, base + AR724X_PCI_REG_INT_MASK);
+       __raw_writel(0, base + AR724X_PCI_REG_INT_STATUS);
+
+       BUILD_BUG_ON(ATH79_PCI_IRQ_COUNT < AR724X_PCI_IRQ_COUNT);
+
+       for (i = ATH79_PCI_IRQ_BASE;
+            i < ATH79_PCI_IRQ_BASE + AR724X_PCI_IRQ_COUNT; i++)
+               irq_set_chip_and_handler(i, &ar724x_pci_irq_chip,
+                                        handle_level_irq);
+
+       irq_set_chained_handler(irq, ar724x_pci_irq_handler);
+}
+
+int __init ar724x_pcibios_init(int irq)
+{
+       int ret;
+
+       ret = -ENOMEM;
+
+       ar724x_pci_devcfg_base = ioremap(AR724X_PCI_CFG_BASE,
+                                        AR724X_PCI_CFG_SIZE);
+       if (ar724x_pci_devcfg_base == NULL)
+               goto err;
+
+       ar724x_pci_ctrl_base = ioremap(AR724X_PCI_CTRL_BASE,
+                                      AR724X_PCI_CTRL_SIZE);
+       if (ar724x_pci_ctrl_base == NULL)
+               goto err_unmap_devcfg;
+
+       ar724x_pci_irq_init(irq);
+       register_pci_controller(&ar724x_pci_controller);
+
+       return PCIBIOS_SUCCESSFUL;
+
+err_unmap_devcfg:
+       iounmap(ar724x_pci_devcfg_base);
+err:
+       return ret;
+}
diff --git a/arch/mips/pci/pci-ath724x.c b/arch/mips/pci/pci-ath724x.c
deleted file mode 100644 (file)
index a4dd24a..0000000
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- *  Atheros 724x PCI support
- *
- *  Copyright (C) 2011 René Bolldorf <xsecute@googlemail.com>
- *
- *  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/pci.h>
-#include <asm/mach-ath79/pci-ath724x.h>
-
-#define reg_read(_phys)                (*(unsigned int *) KSEG1ADDR(_phys))
-#define reg_write(_phys, _val) ((*(unsigned int *) KSEG1ADDR(_phys)) = (_val))
-
-#define ATH724X_PCI_DEV_BASE   0x14000000
-#define ATH724X_PCI_MEM_BASE   0x10000000
-#define ATH724X_PCI_MEM_SIZE   0x08000000
-
-static DEFINE_SPINLOCK(ath724x_pci_lock);
-static struct ath724x_pci_data *pci_data;
-static int pci_data_size;
-
-static int ath724x_pci_read(struct pci_bus *bus, unsigned int devfn, int where,
-                           int size, uint32_t *value)
-{
-       unsigned long flags, addr, tval, mask;
-
-       if (devfn)
-               return PCIBIOS_DEVICE_NOT_FOUND;
-
-       if (where & (size - 1))
-               return PCIBIOS_BAD_REGISTER_NUMBER;
-
-       spin_lock_irqsave(&ath724x_pci_lock, flags);
-
-       switch (size) {
-       case 1:
-               addr = where & ~3;
-               mask = 0xff000000 >> ((where % 4) * 8);
-               tval = reg_read(ATH724X_PCI_DEV_BASE + addr);
-               tval = tval & ~mask;
-               *value = (tval >> ((4 - (where % 4))*8));
-               break;
-       case 2:
-               addr = where & ~3;
-               mask = 0xffff0000 >> ((where % 4)*8);
-               tval = reg_read(ATH724X_PCI_DEV_BASE + addr);
-               tval = tval & ~mask;
-               *value = (tval >> ((4 - (where % 4))*8));
-               break;
-       case 4:
-               *value = reg_read(ATH724X_PCI_DEV_BASE + where);
-               break;
-       default:
-               spin_unlock_irqrestore(&ath724x_pci_lock, flags);
-
-               return PCIBIOS_BAD_REGISTER_NUMBER;
-       }
-
-       spin_unlock_irqrestore(&ath724x_pci_lock, flags);
-
-       return PCIBIOS_SUCCESSFUL;
-}
-
-static int ath724x_pci_write(struct pci_bus *bus, unsigned int devfn, int where,
-                            int size, uint32_t value)
-{
-       unsigned long flags, tval, addr, mask;
-
-       if (devfn)
-               return PCIBIOS_DEVICE_NOT_FOUND;
-
-       if (where & (size - 1))
-               return PCIBIOS_BAD_REGISTER_NUMBER;
-
-       spin_lock_irqsave(&ath724x_pci_lock, flags);
-
-       switch (size) {
-       case 1:
-               addr = (ATH724X_PCI_DEV_BASE + where) & ~3;
-               mask = 0xff000000 >> ((where % 4)*8);
-               tval = reg_read(addr);
-               tval = tval & ~mask;
-               tval |= (value << ((4 - (where % 4))*8)) & mask;
-               reg_write(addr, tval);
-               break;
-       case 2:
-               addr = (ATH724X_PCI_DEV_BASE + where) & ~3;
-               mask = 0xffff0000 >> ((where % 4)*8);
-               tval = reg_read(addr);
-               tval = tval & ~mask;
-               tval |= (value << ((4 - (where % 4))*8)) & mask;
-               reg_write(addr, tval);
-               break;
-       case 4:
-               reg_write((ATH724X_PCI_DEV_BASE + where), value);
-               break;
-       default:
-               spin_unlock_irqrestore(&ath724x_pci_lock, flags);
-
-               return PCIBIOS_BAD_REGISTER_NUMBER;
-       }
-
-       spin_unlock_irqrestore(&ath724x_pci_lock, flags);
-
-       return PCIBIOS_SUCCESSFUL;
-}
-
-static struct pci_ops ath724x_pci_ops = {
-       .read   = ath724x_pci_read,
-       .write  = ath724x_pci_write,
-};
-
-static struct resource ath724x_io_resource = {
-       .name   = "PCI IO space",
-       .start  = 0,
-       .end    = 0,
-       .flags  = IORESOURCE_IO,
-};
-
-static struct resource ath724x_mem_resource = {
-       .name   = "PCI memory space",
-       .start  = ATH724X_PCI_MEM_BASE,
-       .end    = ATH724X_PCI_MEM_BASE + ATH724X_PCI_MEM_SIZE - 1,
-       .flags  = IORESOURCE_MEM,
-};
-
-static struct pci_controller ath724x_pci_controller = {
-       .pci_ops        = &ath724x_pci_ops,
-       .io_resource    = &ath724x_io_resource,
-       .mem_resource   = &ath724x_mem_resource,
-};
-
-void ath724x_pci_add_data(struct ath724x_pci_data *data, int size)
-{
-       pci_data        = data;
-       pci_data_size   = size;
-}
-
-int __init pcibios_map_irq(const struct pci_dev *dev, uint8_t slot, uint8_t pin)
-{
-       unsigned int devfn = dev->devfn;
-       int irq = -1;
-
-       if (devfn > pci_data_size - 1)
-               return irq;
-
-       irq = pci_data[devfn].irq;
-
-       return irq;
-}
-
-int pcibios_plat_dev_init(struct pci_dev *dev)
-{
-       unsigned int devfn = dev->devfn;
-
-       if (devfn > pci_data_size - 1)
-               return PCIBIOS_DEVICE_NOT_FOUND;
-
-       dev->dev.platform_data = pci_data[devfn].pdata;
-
-       return PCIBIOS_SUCCESSFUL;
-}
-
-static int __init ath724x_pcibios_init(void)
-{
-       register_pci_controller(&ath724x_pci_controller);
-
-       return PCIBIOS_SUCCESSFUL;
-}
-
-arch_initcall(ath724x_pcibios_init);
index 030c77e7926e5395111913da53ef884e5ac64899..ea453532a33c6dfc0eeb49659b2dc9036494713a 100644 (file)
 #include <linux/delay.h>
 #include <linux/mm.h>
 #include <linux/vmalloc.h>
-#include <linux/export.h>
-#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/of_platform.h>
+#include <linux/of_gpio.h>
+#include <linux/of_irq.h>
+#include <linux/of_pci.h>
 
 #include <asm/pci.h>
 #include <asm/gpio.h>
 
 #include <lantiq_soc.h>
 #include <lantiq_irq.h>
-#include <lantiq_platform.h>
 
 #include "pci-lantiq.h"
 
-#define LTQ_PCI_CFG_BASE               0x17000000
-#define LTQ_PCI_CFG_SIZE               0x00008000
-#define LTQ_PCI_MEM_BASE               0x18000000
-#define LTQ_PCI_MEM_SIZE               0x02000000
-#define LTQ_PCI_IO_BASE                        0x1AE00000
-#define LTQ_PCI_IO_SIZE                        0x00200000
-
 #define PCI_CR_FCI_ADDR_MAP0           0x00C0
 #define PCI_CR_FCI_ADDR_MAP1           0x00C4
 #define PCI_CR_FCI_ADDR_MAP2           0x00C8
 #define ltq_pci_cfg_w32(x, y)  ltq_w32((x), ltq_pci_mapped_cfg + (y))
 #define ltq_pci_cfg_r32(x)     ltq_r32(ltq_pci_mapped_cfg + (x))
 
-struct ltq_pci_gpio_map {
-       int pin;
-       int alt0;
-       int alt1;
-       int dir;
-       char *name;
-};
-
-/* the pci core can make use of the following gpios */
-static struct ltq_pci_gpio_map ltq_pci_gpio_map[] = {
-       { 0, 1, 0, 0, "pci-exin0" },
-       { 1, 1, 0, 0, "pci-exin1" },
-       { 2, 1, 0, 0, "pci-exin2" },
-       { 39, 1, 0, 0, "pci-exin3" },
-       { 10, 1, 0, 0, "pci-exin4" },
-       { 9, 1, 0, 0, "pci-exin5" },
-       { 30, 1, 0, 1, "pci-gnt1" },
-       { 23, 1, 0, 1, "pci-gnt2" },
-       { 19, 1, 0, 1, "pci-gnt3" },
-       { 38, 1, 0, 1, "pci-gnt4" },
-       { 29, 1, 0, 0, "pci-req1" },
-       { 31, 1, 0, 0, "pci-req2" },
-       { 3, 1, 0, 0, "pci-req3" },
-       { 37, 1, 0, 0, "pci-req4" },
-};
-
 __iomem void *ltq_pci_mapped_cfg;
 static __iomem void *ltq_pci_membase;
 
-int (*ltqpci_plat_dev_init)(struct pci_dev *dev) = NULL;
-
-/* Since the PCI REQ pins can be reused for other functionality, make it
-   possible to exclude those from interpretation by the PCI controller */
-static int ltq_pci_req_mask = 0xf;
-
-static int *ltq_pci_irq_map;
-
-struct pci_ops ltq_pci_ops = {
+static int reset_gpio;
+static struct clk *clk_pci, *clk_external;
+static struct resource pci_io_resource;
+static struct resource pci_mem_resource;
+static struct pci_ops pci_ops = {
        .read   = ltq_pci_read_config_dword,
        .write  = ltq_pci_write_config_dword
 };
 
-static struct resource pci_io_resource = {
-       .name   = "pci io space",
-       .start  = LTQ_PCI_IO_BASE,
-       .end    = LTQ_PCI_IO_BASE + LTQ_PCI_IO_SIZE - 1,
-       .flags  = IORESOURCE_IO
-};
-
-static struct resource pci_mem_resource = {
-       .name   = "pci memory space",
-       .start  = LTQ_PCI_MEM_BASE,
-       .end    = LTQ_PCI_MEM_BASE + LTQ_PCI_MEM_SIZE - 1,
-       .flags  = IORESOURCE_MEM
-};
-
-static struct pci_controller ltq_pci_controller = {
-       .pci_ops        = &ltq_pci_ops,
+static struct pci_controller pci_controller = {
+       .pci_ops        = &pci_ops,
        .mem_resource   = &pci_mem_resource,
        .mem_offset     = 0x00000000UL,
        .io_resource    = &pci_io_resource,
        .io_offset      = 0x00000000UL,
 };
 
-int pcibios_plat_dev_init(struct pci_dev *dev)
-{
-       if (ltqpci_plat_dev_init)
-               return ltqpci_plat_dev_init(dev);
-
-       return 0;
-}
-
-static u32 ltq_calc_bar11mask(void)
+static inline u32 ltq_calc_bar11mask(void)
 {
        u32 mem, bar11mask;
 
@@ -151,48 +95,42 @@ static u32 ltq_calc_bar11mask(void)
        return bar11mask;
 }
 
-static void ltq_pci_setup_gpio(int gpio)
-{
-       int i;
-       for (i = 0; i < ARRAY_SIZE(ltq_pci_gpio_map); i++) {
-               if (gpio & (1 << i)) {
-                       ltq_gpio_request(ltq_pci_gpio_map[i].pin,
-                               ltq_pci_gpio_map[i].alt0,
-                               ltq_pci_gpio_map[i].alt1,
-                               ltq_pci_gpio_map[i].dir,
-                               ltq_pci_gpio_map[i].name);
-               }
-       }
-       ltq_gpio_request(21, 0, 0, 1, "pci-reset");
-       ltq_pci_req_mask = (gpio >> PCI_REQ_SHIFT) & PCI_REQ_MASK;
-}
-
-static int __devinit ltq_pci_startup(struct ltq_pci_data *conf)
+static int __devinit ltq_pci_startup(struct platform_device *pdev)
 {
+       struct device_node *node = pdev->dev.of_node;
+       const __be32 *req_mask, *bus_clk;
        u32 temp_buffer;
 
-       /* set clock to 33Mhz */
-       if (ltq_is_ar9()) {
-               ltq_cgu_w32(ltq_cgu_r32(LTQ_CGU_IFCCR) & ~0x1f00000, LTQ_CGU_IFCCR);
-               ltq_cgu_w32(ltq_cgu_r32(LTQ_CGU_IFCCR) | 0xe00000, LTQ_CGU_IFCCR);
-       } else {
-               ltq_cgu_w32(ltq_cgu_r32(LTQ_CGU_IFCCR) & ~0xf00000, LTQ_CGU_IFCCR);
-               ltq_cgu_w32(ltq_cgu_r32(LTQ_CGU_IFCCR) | 0x800000, LTQ_CGU_IFCCR);
+       /* get our clocks */
+       clk_pci = clk_get(&pdev->dev, NULL);
+       if (IS_ERR(clk_pci)) {
+               dev_err(&pdev->dev, "failed to get pci clock\n");
+               return PTR_ERR(clk_pci);
        }
 
-       /* external or internal clock ? */
-       if (conf->clock) {
-               ltq_cgu_w32(ltq_cgu_r32(LTQ_CGU_IFCCR) & ~(1 << 16),
-                       LTQ_CGU_IFCCR);
-               ltq_cgu_w32((1 << 30), LTQ_CGU_PCICR);
-       } else {
-               ltq_cgu_w32(ltq_cgu_r32(LTQ_CGU_IFCCR) | (1 << 16),
-                       LTQ_CGU_IFCCR);
-               ltq_cgu_w32((1 << 31) | (1 << 30), LTQ_CGU_PCICR);
+       clk_external = clk_get(&pdev->dev, "external");
+       if (IS_ERR(clk_external)) {
+               clk_put(clk_pci);
+               dev_err(&pdev->dev, "failed to get external pci clock\n");
+               return PTR_ERR(clk_external);
        }
 
-       /* setup pci clock and gpis used by pci */
-       ltq_pci_setup_gpio(conf->gpio);
+       /* read the bus speed that we want */
+       bus_clk = of_get_property(node, "lantiq,bus-clock", NULL);
+       if (bus_clk)
+               clk_set_rate(clk_pci, *bus_clk);
+
+       /* and enable the clocks */
+       clk_enable(clk_pci);
+       if (of_find_property(node, "lantiq,external-clock", NULL))
+               clk_enable(clk_external);
+       else
+               clk_disable(clk_external);
+
+       /* setup reset gpio used by pci */
+       reset_gpio = of_get_named_gpio(node, "gpio-reset", 0);
+       if (reset_gpio > 0)
+               devm_gpio_request(&pdev->dev, reset_gpio, "pci-reset");
 
        /* enable auto-switching between PCI and EBU */
        ltq_pci_w32(0xa, PCI_CR_CLK_CTRL);
@@ -205,7 +143,12 @@ static int __devinit ltq_pci_startup(struct ltq_pci_data *conf)
 
        /* enable external 2 PCI masters */
        temp_buffer = ltq_pci_r32(PCI_CR_PC_ARB);
-       temp_buffer &= (~(ltq_pci_req_mask << 16));
+       /* setup the request mask */
+       req_mask = of_get_property(node, "req-mask", NULL);
+       if (req_mask)
+               temp_buffer &= ~((*req_mask & 0xf) << 16);
+       else
+               temp_buffer &= ~0xf0000;
        /* enable internal arbiter */
        temp_buffer |= (1 << INTERNAL_ARB_ENABLE_BIT);
        /* enable internal PCI master reqest */
@@ -249,47 +192,55 @@ static int __devinit ltq_pci_startup(struct ltq_pci_data *conf)
        ltq_ebu_w32(ltq_ebu_r32(LTQ_EBU_PCC_IEN) | 0x10, LTQ_EBU_PCC_IEN);
 
        /* toggle reset pin */
-       __gpio_set_value(21, 0);
-       wmb();
-       mdelay(1);
-       __gpio_set_value(21, 1);
-       return 0;
-}
-
-int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
-{
-       if (ltq_pci_irq_map[slot])
-               return ltq_pci_irq_map[slot];
-       printk(KERN_ERR "lq_pci: trying to map irq for unknown slot %d\n",
-               slot);
-
+       if (reset_gpio > 0) {
+               __gpio_set_value(reset_gpio, 0);
+               wmb();
+               mdelay(1);
+               __gpio_set_value(reset_gpio, 1);
+       }
        return 0;
 }
 
 static int __devinit ltq_pci_probe(struct platform_device *pdev)
 {
-       struct ltq_pci_data *ltq_pci_data =
-               (struct ltq_pci_data *) pdev->dev.platform_data;
+       struct resource *res_cfg, *res_bridge;
 
        pci_clear_flags(PCI_PROBE_ONLY);
-       ltq_pci_irq_map = ltq_pci_data->irq;
-       ltq_pci_membase = ioremap_nocache(PCI_CR_BASE_ADDR, PCI_CR_SIZE);
-       ltq_pci_mapped_cfg =
-               ioremap_nocache(LTQ_PCI_CFG_BASE, LTQ_PCI_CFG_BASE);
-       ltq_pci_controller.io_map_base =
-               (unsigned long)ioremap(LTQ_PCI_IO_BASE, LTQ_PCI_IO_SIZE - 1);
-       ltq_pci_startup(ltq_pci_data);
-       register_pci_controller(&ltq_pci_controller);
 
+       res_cfg = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       res_bridge = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+       if (!res_cfg || !res_bridge) {
+               dev_err(&pdev->dev, "missing memory reources\n");
+               return -EINVAL;
+       }
+
+       ltq_pci_membase = devm_request_and_ioremap(&pdev->dev, res_bridge);
+       ltq_pci_mapped_cfg = devm_request_and_ioremap(&pdev->dev, res_cfg);
+
+       if (!ltq_pci_membase || !ltq_pci_mapped_cfg) {
+               dev_err(&pdev->dev, "failed to remap resources\n");
+               return -ENOMEM;
+       }
+
+       ltq_pci_startup(pdev);
+
+       pci_load_of_ranges(&pci_controller, pdev->dev.of_node);
+       register_pci_controller(&pci_controller);
        return 0;
 }
 
-static struct platform_driver
-ltq_pci_driver = {
+static const struct of_device_id ltq_pci_match[] = {
+       { .compatible = "lantiq,pci-xway" },
+       {},
+};
+MODULE_DEVICE_TABLE(of, ltq_pci_match);
+
+static struct platform_driver ltq_pci_driver = {
        .probe = ltq_pci_probe,
        .driver = {
-               .name = "ltq_pci",
+               .name = "pci-xway",
                .owner = THIS_MODULE,
+               .of_match_table = ltq_pci_match,
        },
 };
 
@@ -297,7 +248,7 @@ int __init pcibios_init(void)
 {
        int ret = platform_driver_register(&ltq_pci_driver);
        if (ret)
-               printk(KERN_INFO "ltq_pci: Error registering platfom driver!");
+               pr_info("pci-xway: Error registering platform driver!");
        return ret;
 }
 
index 0514866fa9255f13f8cc2578c840b22ad7626482..271e8c4a54c7f2304101020d8d8bb0ab50043437 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/init.h>
 #include <linux/types.h>
 #include <linux/pci.h>
+#include <linux/of_address.h>
 
 #include <asm/cpu-info.h>
 
@@ -114,9 +115,63 @@ static void __devinit pcibios_scanbus(struct pci_controller *hose)
                        pci_bus_assign_resources(bus);
                        pci_enable_bridges(bus);
                }
+               bus->dev.of_node = hose->of_node;
        }
 }
 
+#ifdef CONFIG_OF
+void __devinit pci_load_of_ranges(struct pci_controller *hose,
+                               struct device_node *node)
+{
+       const __be32 *ranges;
+       int rlen;
+       int pna = of_n_addr_cells(node);
+       int np = pna + 5;
+
+       pr_info("PCI host bridge %s ranges:\n", node->full_name);
+       ranges = of_get_property(node, "ranges", &rlen);
+       if (ranges == NULL)
+               return;
+       hose->of_node = node;
+
+       while ((rlen -= np * 4) >= 0) {
+               u32 pci_space;
+               struct resource *res = NULL;
+               u64 addr, size;
+
+               pci_space = be32_to_cpup(&ranges[0]);
+               addr = of_translate_address(node, ranges + 3);
+               size = of_read_number(ranges + pna + 3, 2);
+               ranges += np;
+               switch ((pci_space >> 24) & 0x3) {
+               case 1:         /* PCI IO space */
+                       pr_info("  IO 0x%016llx..0x%016llx\n",
+                                       addr, addr + size - 1);
+                       hose->io_map_base =
+                               (unsigned long)ioremap(addr, size);
+                       res = hose->io_resource;
+                       res->flags = IORESOURCE_IO;
+                       break;
+               case 2:         /* PCI Memory space */
+               case 3:         /* PCI 64 bits Memory space */
+                       pr_info(" MEM 0x%016llx..0x%016llx\n",
+                                       addr, addr + size - 1);
+                       res = hose->mem_resource;
+                       res->flags = IORESOURCE_MEM;
+                       break;
+               }
+               if (res != NULL) {
+                       res->start = addr;
+                       res->name = node->full_name;
+                       res->end = res->start + size - 1;
+                       res->parent = NULL;
+                       res->sibling = NULL;
+                       res->child = NULL;
+               }
+       }
+}
+#endif
+
 static DEFINE_MUTEX(pci_scan_mutex);
 
 void __devinit register_pci_controller(struct pci_controller *hose)
index 02f5fb94ea2808a5bf580679421598e0659961d2..5af95ec3319d70e6a8e5d7f28d1ad516c64f08bf 100644 (file)
@@ -5,5 +5,3 @@
 obj-y    += irq.o prom.o py-console.o setup.o
 
 obj-$(CONFIG_SMP)              += smp.o
-
-ccflags-y := -Werror
index 3498ac9c35af026a4f8205ccef17223b9024cdc9..b6472fc88a991cb3a53aa3d8cae9e2077ace4f29 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/bcd.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
+#include <linux/export.h>
 #include <linux/types.h>
 #include <linux/mm.h>
 #include <linux/bootmem.h>
index 348d2e850ef5128d42a34fe761fe357fdb5fc5ff..39ca9f8d63ae43c6ffe2bdd0e3e61abefaeb9000 100644 (file)
@@ -27,5 +27,3 @@ obj-y += init.o ioremap.o memory.o powertv_setup.o reset.o time.o \
        asic/ pci/
 
 obj-$(CONFIG_USB) += powertv-usb.o
-
-ccflags-y := -Wall
index d810a33182a4a6b76a250e86532930b2f4d23f98..35dcc53eb25f3960eed05f3a936c6dd7d9cc1c1d 100644 (file)
@@ -19,5 +19,3 @@
 obj-y += asic-calliope.o asic-cronus.o asic-gaia.o asic-zeus.o \
        asic_devices.o asic_int.o irq_asic.o prealloc-calliope.o \
        prealloc-cronus.o prealloc-cronuslite.o prealloc-gaia.o prealloc-zeus.o
-
-ccflags-y := -Wall -Werror
index 5783201cd2c81196659939f6ba495069118e6281..2610a6af5b2c6cf980ad327b3f9d9ee67a2d6953 100644 (file)
@@ -17,5 +17,3 @@
 #
 
 obj-$(CONFIG_PCI)      += fixup-powertv.o
-
-ccflags-y := -Wall -Werror
index a969eb8266340326c17509cfbba6522498f0d6cd..ea774285e6c500b43ab0d915b3c292aa091eb307 100644 (file)
@@ -15,6 +15,7 @@
  *  GNU General Public License for more details.
  */
 #include <linux/kernel.h>
+#include <linux/export.h>
 #include <linux/init.h>
 #include <linux/ctype.h>
 #include <linux/string.h>
index d16b462154c33834f996bc0e89726ae97a83b089..413f17f8e89289035ce836148c64cc56c31443a5 100644 (file)
@@ -10,6 +10,7 @@
  */
 #include <linux/eisa.h>
 #include <linux/init.h>
+#include <linux/export.h>
 #include <linux/console.h>
 #include <linux/fb.h>
 #include <linux/screen_info.h>
index ab506181ec3108ad98c2db77d73f01e2e2b9b134..d31eeea480cfdda8a4231351abbf61c76da08a5f 100644 (file)
@@ -20,9 +20,6 @@
 typedef unsigned short __kernel_mode_t;
 #define __kernel_mode_t __kernel_mode_t
 
-typedef unsigned short __kernel_nlink_t;
-#define __kernel_nlink_t __kernel_nlink_t
-
 typedef unsigned short __kernel_ipc_pid_t;
 #define __kernel_ipc_pid_t __kernel_ipc_pid_t
 
index 890cf91767cc8486b4fd004cf8ca6e44c9b94a28..6ab0bee2a54fd38efeccbaa683b4437f26406a34 100644 (file)
@@ -31,8 +31,6 @@
 
 #define DEBUG_SIG 0
 
-#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
-
 /*
  * atomically swap in the new signal mask, and wait for a signal.
  */
@@ -163,7 +161,6 @@ asmlinkage long sys_sigreturn(void)
                             sizeof(frame->extramask)))
                goto badframe;
 
-       sigdelsetmask(&set, ~_BLOCKABLE);
        set_current_blocked(&set);
 
        if (restore_sigcontext(current_frame(), &frame->sc, &d0))
@@ -191,7 +188,6 @@ asmlinkage long sys_rt_sigreturn(void)
        if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
                goto badframe;
 
-       sigdelsetmask(&set, ~_BLOCKABLE);
        set_current_blocked(&set);
 
        if (restore_sigcontext(current_frame(), &frame->uc.uc_mcontext, &d0))
@@ -430,8 +426,9 @@ static inline void stepback(struct pt_regs *regs)
  */
 static int handle_signal(int sig,
                         siginfo_t *info, struct k_sigaction *ka,
-                        sigset_t *oldset, struct pt_regs *regs)
+                        struct pt_regs *regs)
 {
+       sigset_t *oldset = sigmask_to_save();
        int ret;
 
        /* Are we from a system call? */
@@ -461,11 +458,11 @@ static int handle_signal(int sig,
                ret = setup_rt_frame(sig, ka, info, oldset, regs);
        else
                ret = setup_frame(sig, ka, oldset, regs);
+       if (ret)
+               return;
 
-       if (ret == 0)
-               block_sigmask(ka, sig);
-
-       return ret;
+       signal_delivered(sig, info, ka, regs,
+                                test_thread_flag(TIF_SINGLESTEP));
 }
 
 /*
@@ -475,7 +472,6 @@ static void do_signal(struct pt_regs *regs)
 {
        struct k_sigaction ka;
        siginfo_t info;
-       sigset_t *oldset;
        int signr;
 
        /* we want the common case to go fast, which is why we may in certain
@@ -483,23 +479,9 @@ static void do_signal(struct pt_regs *regs)
        if (!user_mode(regs))
                return;
 
-       if (test_thread_flag(TIF_RESTORE_SIGMASK))
-               oldset = &current->saved_sigmask;
-       else
-               oldset = &current->blocked;
-
        signr = get_signal_to_deliver(&info, &ka, regs, NULL);
        if (signr > 0) {
-               if (handle_signal(signr, &info, &ka, oldset, regs) == 0) {
-                       /* a signal was successfully delivered; the saved
-                        * sigmask will have been stored in the signal frame,
-                        * and will be restored by sigreturn, so we can simply
-                        * clear the TIF_RESTORE_SIGMASK flag */
-                       if (test_thread_flag(TIF_RESTORE_SIGMASK))
-                               clear_thread_flag(TIF_RESTORE_SIGMASK);
-
-                       tracehook_signal_handler(signr, &info, &ka, regs,
-                                                test_thread_flag(TIF_SINGLESTEP));
+               if (handle_signal(signr, &info, &ka, regs) == 0) {
                }
 
                return;
@@ -525,10 +507,7 @@ static void do_signal(struct pt_regs *regs)
 
        /* if there's no signal to deliver, we just put the saved sigmask
         * back */
-       if (test_thread_flag(TIF_RESTORE_SIGMASK)) {
-               clear_thread_flag(TIF_RESTORE_SIGMASK);
-               sigprocmask(SIG_SETMASK, &current->saved_sigmask, NULL);
-       }
+       restore_saved_sigmask();
 }
 
 /*
@@ -548,13 +527,11 @@ asmlinkage void do_notify_resume(struct pt_regs *regs, u32 thread_info_flags)
        }
 
        /* deal with pending signal delivery */
-       if (thread_info_flags & (_TIF_SIGPENDING | _TIF_RESTORE_SIGMASK))
+       if (thread_info_flags & _TIF_SIGPENDING)
                do_signal(regs);
 
        if (thread_info_flags & _TIF_NOTIFY_RESUME) {
                clear_thread_flag(TIF_NOTIFY_RESUME);
                tracehook_notify_resume(current_frame());
-               if (current->replacement_session_keyring)
-                       key_replace_session_keyring();
        }
 }
index e970743251ae3c922fb4ff4f9722f4dc596f1f6f..30110297f4f9509d6437c8f5c3e80220add6e2a9 100644 (file)
@@ -33,8 +33,6 @@
 
 #define DEBUG_SIG 0
 
-#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
-
 asmlinkage long
 _sys_sigaltstack(const stack_t *uss, stack_t *uoss, struct pt_regs *regs)
 {
@@ -101,7 +99,6 @@ asmlinkage long _sys_rt_sigreturn(struct pt_regs *regs)
        if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
                goto badframe;
 
-       sigdelsetmask(&set, ~_BLOCKABLE);
        set_current_blocked(&set);
 
        if (restore_sigcontext(regs, &frame->uc.uc_mcontext))
@@ -251,20 +248,19 @@ give_sigsegv:
        return -EFAULT;
 }
 
-static inline int
+static inline void
 handle_signal(unsigned long sig,
              siginfo_t *info, struct k_sigaction *ka,
-             sigset_t *oldset, struct pt_regs *regs)
+             struct pt_regs *regs)
 {
        int ret;
 
-       ret = setup_rt_frame(sig, ka, info, oldset, regs);
+       ret = setup_rt_frame(sig, ka, info, sigmask_to_save(), regs);
        if (ret)
-               return ret;
-
-       block_sigmask(ka, sig);
+               return;
 
-       return 0;
+       signal_delivered(sig, info, ka, regs,
+                                test_thread_flag(TIF_SINGLESTEP));
 }
 
 /*
@@ -339,30 +335,10 @@ void do_signal(struct pt_regs *regs)
        if (signr <= 0) {
                /* no signal to deliver so we just put the saved sigmask
                 * back */
-               if (test_thread_flag(TIF_RESTORE_SIGMASK)) {
-                       clear_thread_flag(TIF_RESTORE_SIGMASK);
-                       sigprocmask(SIG_SETMASK, &current->saved_sigmask, NULL);
-               }
-
+               restore_saved_sigmask();
        } else {                /* signr > 0 */
-               sigset_t *oldset;
-
-               if (current_thread_info()->flags & _TIF_RESTORE_SIGMASK)
-                       oldset = &current->saved_sigmask;
-               else
-                       oldset = &current->blocked;
-
                /* Whee!  Actually deliver the signal.  */
-               if (!handle_signal(signr, &info, &ka, oldset, regs)) {
-                       /* a signal was successfully delivered; the saved
-                        * sigmask will have been stored in the signal frame,
-                        * and will be restored by sigreturn, so we can simply
-                        * clear the TIF_RESTORE_SIGMASK flag */
-                       clear_thread_flag(TIF_RESTORE_SIGMASK);
-               }
-
-               tracehook_signal_handler(signr, &info, &ka, regs,
-                                        test_thread_flag(TIF_SINGLESTEP));
+               handle_signal(signr, &info, &ka, regs);
        }
 
        return;
@@ -376,7 +352,5 @@ asmlinkage void do_notify_resume(struct pt_regs *regs)
        if (current_thread_info()->flags & _TIF_NOTIFY_RESUME) {
                clear_thread_flag(TIF_NOTIFY_RESUME);
                tracehook_notify_resume(regs);
-               if (current->replacement_session_keyring)
-                       key_replace_session_keyring();
        }
 }
index ddb8b24b823d1c77881f80cc019570c537ea6c62..3ff21b536f28f6c1e84b06b665e6d7589579f6c9 100644 (file)
@@ -18,6 +18,7 @@ config PARISC
        select IRQ_PER_CPU
        select ARCH_HAVE_NMI_SAFE_CMPXCHG
        select GENERIC_SMP_IDLE_THREAD
+       select GENERIC_STRNCPY_FROM_USER
 
        help
          The PA-RISC microprocessor is designed by Hewlett-Packard and used
index 5212b0357daf15aaf454b751b0eb146fc47b34ac..b9344256f76b365db1c6a9a99b08b337f706970c 100644 (file)
@@ -10,9 +10,6 @@
 typedef unsigned short         __kernel_mode_t;
 #define __kernel_mode_t __kernel_mode_t
 
-typedef unsigned short         __kernel_nlink_t;
-#define __kernel_nlink_t __kernel_nlink_t
-
 typedef unsigned short         __kernel_ipc_pid_t;
 #define __kernel_ipc_pid_t __kernel_ipc_pid_t
 
index e8f8037d872bc91c794f50ba5fd7a0f8eb996232..a5dc9066c6d8d50cb35f2e4cf88509fa4621b84b 100644 (file)
@@ -25,7 +25,6 @@ typedef unsigned long address_t;
 #define cpu_number_map(cpu)    (cpu)
 #define cpu_logical_map(cpu)   (cpu)
 
-extern void smp_send_reschedule(int cpu);
 extern void smp_send_all_nop(void);
 
 extern void arch_send_call_function_single_ipi(int cpu);
@@ -50,6 +49,5 @@ static inline void __cpu_die (unsigned int cpu) {
   while(1)
     ;
 }
-extern int __cpu_up (unsigned int cpu);
 
 #endif /*  __ASM_SMP_H */
index 9d5fbbc5c31f14df4791b1005e83ade0cd0505f4..d76fbda5d62c0437f5fb52c389e144597f054b12 100644 (file)
@@ -7,7 +7,7 @@ struct stat {
        unsigned int    st_dev;         /* dev_t is 32 bits on parisc */
        ino_t           st_ino;         /* 32 bits */
        mode_t          st_mode;        /* 16 bits */
-       nlink_t         st_nlink;       /* 16 bits */
+       unsigned short  st_nlink;       /* 16 bits */
        unsigned short  st_reserved1;   /* old st_uid */
        unsigned short  st_reserved2;   /* old st_gid */
        unsigned int    st_rdev;
@@ -42,7 +42,7 @@ struct hpux_stat64 {
        unsigned int    st_dev;         /* dev_t is 32 bits on parisc */
        ino_t           st_ino;         /* 32 bits */
        mode_t          st_mode;        /* 16 bits */
-       nlink_t         st_nlink;       /* 16 bits */
+       unsigned short  st_nlink;       /* 16 bits */
        unsigned short  st_reserved1;   /* old st_uid */
        unsigned short  st_reserved2;   /* old st_gid */
        unsigned int    st_rdev;
index 83ae7dd4d99ea721adbdc16a751435db902da354..22b4726dee494403c80bdf2f88c583ba32043b94 100644 (file)
@@ -74,7 +74,7 @@ struct thread_info {
 #define _TIF_BLOCKSTEP         (1 << TIF_BLOCKSTEP)
 
 #define _TIF_USER_WORK_MASK     (_TIF_SIGPENDING | _TIF_NOTIFY_RESUME | \
-                                 _TIF_NEED_RESCHED | _TIF_RESTORE_SIGMASK)
+                                 _TIF_NEED_RESCHED)
 
 #endif /* __KERNEL__ */
 
index 9ac066086f030fc4080ea24be0d557afd7c8aae8..4ba2c93770f1f47c83226dc97b6e25c89d9b0bd6 100644 (file)
@@ -218,15 +218,14 @@ struct exception_data {
 extern unsigned long lcopy_to_user(void __user *, const void *, unsigned long);
 extern unsigned long lcopy_from_user(void *, const void __user *, unsigned long);
 extern unsigned long lcopy_in_user(void __user *, const void __user *, unsigned long);
-extern long lstrncpy_from_user(char *, const char __user *, long);
+extern long strncpy_from_user(char *, const char __user *, long);
 extern unsigned lclear_user(void __user *,unsigned long);
 extern long lstrnlen_user(const char __user *,long);
-
 /*
  * Complex access routines -- macros
  */
+#define user_addr_max() (~0UL)
 
-#define strncpy_from_user lstrncpy_from_user
 #define strnlen_user lstrnlen_user
 #define strlen_user(str) lstrnlen_user(str, 0x7fffffffL)
 #define clear_user lclear_user
index 5350342170218f635e4e231a4d10b1147210a1bf..18670a078849b96d92d4dd4ff406a79c2caa28a4 100644 (file)
         * entry (identifying the physical page) and %r23 up with
         * the from tlb entry (or nothing if only a to entry---for
         * clear_user_page_asm) */
-       .macro          do_alias        spc,tmp,tmp1,va,pte,prot,fault
+       .macro          do_alias        spc,tmp,tmp1,va,pte,prot,fault,patype
        cmpib,COND(<>),n 0,\spc,\fault
        ldil            L%(TMPALIAS_MAP_START),\tmp
 #if defined(CONFIG_64BIT) && (TMPALIAS_MAP_START >= 0x80000000)
         */
        cmpiclr,=       0x01,\tmp,%r0
        ldi             (_PAGE_DIRTY|_PAGE_READ|_PAGE_WRITE),\prot
-#ifdef CONFIG_64BIT
+.ifc \patype,20
        depd,z          \prot,8,7,\prot
-#else
+.else
+.ifc \patype,11
        depw,z          \prot,8,7,\prot
-#endif
+.else
+       .error "undefined PA type to do_alias"
+.endif
+.endif
        /*
         * OK, it is in the temp alias region, check whether "from" or "to".
         * Check "subtle" note in pacache.S re: r23/r26.
@@ -920,7 +924,7 @@ intr_check_sig:
        /* As above */
        mfctl   %cr30,%r1
        LDREG   TI_FLAGS(%r1),%r19
-       ldi     (_TIF_SIGPENDING|_TIF_RESTORE_SIGMASK|_TIF_NOTIFY_RESUME), %r20
+       ldi     (_TIF_SIGPENDING|_TIF_NOTIFY_RESUME), %r20
        and,COND(<>)    %r19, %r20, %r0
        b,n     intr_restore    /* skip past if we've nothing to do */
 
@@ -1189,7 +1193,7 @@ dtlb_miss_20w:
        nop
 
 dtlb_check_alias_20w:
-       do_alias        spc,t0,t1,va,pte,prot,dtlb_fault
+       do_alias        spc,t0,t1,va,pte,prot,dtlb_fault,20
 
        idtlbt          pte,prot
 
@@ -1213,7 +1217,7 @@ nadtlb_miss_20w:
        nop
 
 nadtlb_check_alias_20w:
-       do_alias        spc,t0,t1,va,pte,prot,nadtlb_emulate
+       do_alias        spc,t0,t1,va,pte,prot,nadtlb_emulate,20
 
        idtlbt          pte,prot
 
@@ -1245,7 +1249,7 @@ dtlb_miss_11:
        nop
 
 dtlb_check_alias_11:
-       do_alias        spc,t0,t1,va,pte,prot,dtlb_fault
+       do_alias        spc,t0,t1,va,pte,prot,dtlb_fault,11
 
        idtlba          pte,(va)
        idtlbp          prot,(va)
@@ -1277,7 +1281,7 @@ nadtlb_miss_11:
        nop
 
 nadtlb_check_alias_11:
-       do_alias        spc,t0,t1,va,pte,prot,nadtlb_emulate
+       do_alias        spc,t0,t1,va,pte,prot,nadtlb_emulate,11
 
        idtlba          pte,(va)
        idtlbp          prot,(va)
@@ -1304,7 +1308,7 @@ dtlb_miss_20:
        nop
 
 dtlb_check_alias_20:
-       do_alias        spc,t0,t1,va,pte,prot,dtlb_fault
+       do_alias        spc,t0,t1,va,pte,prot,dtlb_fault,20
        
        idtlbt          pte,prot
 
@@ -1330,7 +1334,7 @@ nadtlb_miss_20:
        nop
 
 nadtlb_check_alias_20:
-       do_alias        spc,t0,t1,va,pte,prot,nadtlb_emulate
+       do_alias        spc,t0,t1,va,pte,prot,nadtlb_emulate,20
 
        idtlbt          pte,prot
 
@@ -1457,7 +1461,7 @@ naitlb_miss_20w:
        nop
 
 naitlb_check_alias_20w:
-       do_alias        spc,t0,t1,va,pte,prot,naitlb_fault
+       do_alias        spc,t0,t1,va,pte,prot,naitlb_fault,20
 
        iitlbt          pte,prot
 
@@ -1511,7 +1515,7 @@ naitlb_miss_11:
        nop
 
 naitlb_check_alias_11:
-       do_alias        spc,t0,t1,va,pte,prot,itlb_fault
+       do_alias        spc,t0,t1,va,pte,prot,itlb_fault,11
 
        iitlba          pte,(%sr0, va)
        iitlbp          prot,(%sr0, va)
@@ -1557,7 +1561,7 @@ naitlb_miss_20:
        nop
 
 naitlb_check_alias_20:
-       do_alias        spc,t0,t1,va,pte,prot,naitlb_fault
+       do_alias        spc,t0,t1,va,pte,prot,naitlb_fault,20
 
        iitlbt          pte,prot
 
@@ -2028,7 +2032,7 @@ syscall_check_resched:
        .import do_signal,code
 syscall_check_sig:
        LDREG   TI_FLAGS-THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r19
-       ldi     (_TIF_SIGPENDING|_TIF_RESTORE_SIGMASK), %r26
+       ldi     (_TIF_SIGPENDING|_TIF_NOTIFY_RESUME), %r26
        and,COND(<>)    %r19, %r26, %r0
        b,n     syscall_restore /* skip past if we've nothing to do */
 
index a7bb757a5497137d894518d1dc9d252cb845bb49..ceec85de62904a1892c0fb30eca7eb034e627e53 100644 (file)
@@ -44,7 +44,6 @@ EXPORT_SYMBOL(__cmpxchg_u64);
 #endif
 
 #include <asm/uaccess.h>
-EXPORT_SYMBOL(lstrncpy_from_user);
 EXPORT_SYMBOL(lclear_user);
 EXPORT_SYMBOL(lstrnlen_user);
 
index 4b9cb0d546d132fb39c5ba57d3986f3b109dff48..594459bde14ed3c927b7dddce88e1d9cf9863e74 100644 (file)
@@ -48,9 +48,6 @@
 #define DBG(LEVEL, ...)
 #endif
        
-
-#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
-
 /* gcc will complain if a pointer is cast to an integer of different
  * size.  If you really need to do this (and we do for an ELF32 user
  * application in an ELF64 kernel) then you have to do a cast to an
@@ -131,7 +128,6 @@ sys_rt_sigreturn(struct pt_regs *regs, int in_syscall)
                        goto give_sigsegv;
        }
                
-       sigdelsetmask(&set, ~_BLOCKABLE);
        set_current_blocked(&set);
 
        /* Good thing we saved the old gr[30], eh? */
@@ -443,8 +439,9 @@ give_sigsegv:
 
 static long
 handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka,
-               sigset_t *oldset, struct pt_regs *regs, int in_syscall)
+               struct pt_regs *regs, int in_syscall)
 {
+       sigset_t *oldset = sigmask_to_save();
        DBG(1,"handle_signal: sig=%ld, ka=%p, info=%p, oldset=%p, regs=%p\n",
               sig, ka, info, oldset, regs);
        
@@ -452,12 +449,13 @@ handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka,
        if (!setup_rt_frame(sig, ka, info, oldset, regs, in_syscall))
                return 0;
 
-       block_sigmask(ka, sig);
-
-       tracehook_signal_handler(sig, info, ka, regs, 
+       signal_delivered(sig, info, ka, regs, 
                test_thread_flag(TIF_SINGLESTEP) ||
                test_thread_flag(TIF_BLOCKSTEP));
 
+       DBG(1,KERN_DEBUG "do_signal: Exit (success), regs->gr[28] = %ld\n",
+               regs->gr[28]);
+
        return 1;
 }
 
@@ -568,28 +566,17 @@ do_signal(struct pt_regs *regs, long in_syscall)
        siginfo_t info;
        struct k_sigaction ka;
        int signr;
-       sigset_t *oldset;
 
-       DBG(1,"\ndo_signal: oldset=0x%p, regs=0x%p, sr7 %#lx, in_syscall=%d\n",
-              oldset, regs, regs->sr[7], in_syscall);
+       DBG(1,"\ndo_signal: regs=0x%p, sr7 %#lx, in_syscall=%d\n",
+              regs, regs->sr[7], in_syscall);
 
        /* Everyone else checks to see if they are in kernel mode at
           this point and exits if that's the case.  I'm not sure why
           we would be called in that case, but for some reason we
           are. */
 
-       if (test_thread_flag(TIF_RESTORE_SIGMASK))
-               oldset = &current->saved_sigmask;
-       else
-               oldset = &current->blocked;
-
-       DBG(1,"do_signal: oldset %08lx / %08lx\n", 
-               oldset->sig[0], oldset->sig[1]);
-
-
        /* May need to force signal if handle_signal failed to deliver */
        while (1) {
-         
                signr = get_signal_to_deliver(&info, &ka, regs, NULL);
                DBG(3,"do_signal: signr = %d, regs->gr[28] = %ld\n", signr, regs->gr[28]); 
        
@@ -603,14 +590,8 @@ do_signal(struct pt_regs *regs, long in_syscall)
                /* Whee!  Actually deliver the signal.  If the
                   delivery failed, we need to continue to iterate in
                   this loop so we can deliver the SIGSEGV... */
-               if (handle_signal(signr, &info, &ka, oldset,
-                                 regs, in_syscall)) {
-                       DBG(1,KERN_DEBUG "do_signal: Exit (success), regs->gr[28] = %ld\n",
-                               regs->gr[28]);
-                       if (test_thread_flag(TIF_RESTORE_SIGMASK))
-                               clear_thread_flag(TIF_RESTORE_SIGMASK);
+               if (handle_signal(signr, &info, &ka, regs, in_syscall))
                        return;
-               }
        }
        /* end of while(1) looping forever if we can't force a signal */
 
@@ -621,24 +602,16 @@ do_signal(struct pt_regs *regs, long in_syscall)
        DBG(1,"do_signal: Exit (not delivered), regs->gr[28] = %ld\n", 
                regs->gr[28]);
 
-       if (test_thread_flag(TIF_RESTORE_SIGMASK)) {
-               clear_thread_flag(TIF_RESTORE_SIGMASK);
-               sigprocmask(SIG_SETMASK, &current->saved_sigmask, NULL);
-       }
-
-       return;
+       restore_saved_sigmask();
 }
 
 void do_notify_resume(struct pt_regs *regs, long in_syscall)
 {
-       if (test_thread_flag(TIF_SIGPENDING) ||
-           test_thread_flag(TIF_RESTORE_SIGMASK))
+       if (test_thread_flag(TIF_SIGPENDING))
                do_signal(regs, in_syscall);
 
        if (test_thread_flag(TIF_NOTIFY_RESUME)) {
                clear_thread_flag(TIF_NOTIFY_RESUME);
                tracehook_notify_resume(regs);
-               if (current->replacement_session_keyring)
-                       key_replace_session_keyring();
        }
 }
index e14132430762414166a729ae57e647ec5b9f1316..fd49aeda9eb8b0c4fef656e3dc00228c59251b99 100644 (file)
@@ -47,8 +47,6 @@
 #define DBG(LEVEL, ...)
 #endif
 
-#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
-
 inline void
 sigset_32to64(sigset_t *s64, compat_sigset_t *s32)
 {
index fa6f2b8163e03cc1bdc953bbb1e2b8282b826c7a..64a999882e4fb8d0d584da223c7b1f43842e8d2c 100644 (file)
@@ -50,8 +50,10 @@ SECTIONS
        . = KERNEL_BINARY_TEXT_START;
 
        _text = .;              /* Text and read-only data */
-       .text ALIGN(16) : {
+       .head ALIGN(16) : {
                HEAD_TEXT
+       } = 0
+       .text ALIGN(16) : {
                TEXT_TEXT
                SCHED_TEXT
                LOCK_TEXT
@@ -65,7 +67,7 @@ SECTIONS
                *(.fixup)
                *(.lock.text)           /* out-of-line lock text */
                *(.gnu.warning)
-       } = 0
+       }
        /* End of text section */
        _etext = .;
 
index 1bd23ccec17b9a53fb838fdfdf5eb9ab62ccf739..6f2d9355efe25af6ab90d4205a216c1c649c39a9 100644 (file)
        bv          %r0(%r1)
        .endm
 
-       /*
-        * long lstrncpy_from_user(char *dst, const char *src, long n)
-        *
-        * Returns -EFAULT if exception before terminator,
-        *         N if the entire buffer filled,
-        *         otherwise strlen (i.e. excludes zero byte)
-        */
-
-ENTRY(lstrncpy_from_user)
-       .proc
-       .callinfo NO_CALLS
-       .entry
-       comib,=     0,%r24,$lsfu_done
-       copy        %r24,%r23
-       get_sr
-1:      ldbs,ma     1(%sr1,%r25),%r1
-$lsfu_loop:
-       stbs,ma     %r1,1(%r26)
-       comib,=,n   0,%r1,$lsfu_done
-       addib,<>,n  -1,%r24,$lsfu_loop
-2:      ldbs,ma     1(%sr1,%r25),%r1
-$lsfu_done:
-       sub         %r23,%r24,%r28
-$lsfu_exit:
-       bv          %r0(%r2)
-       nop
-       .exit
-ENDPROC(lstrncpy_from_user)
-
-       .section .fixup,"ax"
-3:      fixup_branch $lsfu_exit
-       ldi         -EFAULT,%r28
-       .previous
-
-       .section __ex_table,"aw"
-       ASM_ULONG_INSN 1b,3b
-       ASM_ULONG_INSN 2b,3b
-       .previous
-
-       .procend
-
        /*
         * unsigned long lclear_user(void *to, unsigned long n)
         *
index f1393252bbdad837c97b794c8534328a9c912ee3..2958c5b97b2dd4100ac5907129b4736d94458cf7 100644 (file)
@@ -16,9 +16,6 @@ typedef int           __kernel_ssize_t;
 typedef long           __kernel_ptrdiff_t;
 #define __kernel_size_t __kernel_size_t
 
-typedef unsigned short __kernel_nlink_t;
-#define __kernel_nlink_t __kernel_nlink_t
-
 typedef short          __kernel_ipc_pid_t;
 #define __kernel_ipc_pid_t __kernel_ipc_pid_t
 #endif
index e4edc510b530cfed6420e96012a69dc424a34e79..10cfb558e0fd7d1a82df840dd67c3578be1cbe52 100644 (file)
@@ -30,11 +30,11 @@ struct stat {
        unsigned long   st_dev;
        ino_t           st_ino;
 #ifdef __powerpc64__
-       nlink_t         st_nlink;
+       unsigned short  st_nlink;
        mode_t          st_mode;
 #else
        mode_t          st_mode;
-       nlink_t         st_nlink;
+       unsigned short  st_nlink;
 #endif
        uid_t           st_uid;
        gid_t           st_gid;
index a556ccc16b58d4560b004c1ee797f8e92c7c6506..68831e9cf82f01ddf8f12164962d8db438b93af3 100644 (file)
@@ -140,7 +140,23 @@ static inline void set_restore_sigmask(void)
 {
        struct thread_info *ti = current_thread_info();
        ti->local_flags |= _TLF_RESTORE_SIGMASK;
-       set_bit(TIF_SIGPENDING, &ti->flags);
+       WARN_ON(!test_bit(TIF_SIGPENDING, &ti->flags));
+}
+static inline void clear_restore_sigmask(void)
+{
+       current_thread_info()->local_flags &= ~_TLF_RESTORE_SIGMASK;
+}
+static inline bool test_restore_sigmask(void)
+{
+       return current_thread_info()->local_flags & _TLF_RESTORE_SIGMASK;
+}
+static inline bool test_and_clear_restore_sigmask(void)
+{
+       struct thread_info *ti = current_thread_info();
+       if (!(ti->local_flags & _TLF_RESTORE_SIGMASK))
+               return false;
+       ti->local_flags &= ~_TLF_RESTORE_SIGMASK;
+       return true;
 }
 
 static inline bool test_thread_local_flags(unsigned int flags)
index 651c5963662b68ed098c04d1dbdc512f06f2fac1..5c023c9cf16ee70a7a3b281af2ad9807028c3b13 100644 (file)
@@ -51,16 +51,6 @@ void __user * get_sigframe(struct k_sigaction *ka, struct pt_regs *regs,
         return (void __user *)newsp;
 }
 
-
-/*
- * Restore the user process's signal mask
- */
-void restore_sigmask(sigset_t *set)
-{
-       sigdelsetmask(set, ~_BLOCKABLE);
-       set_current_blocked(set);
-}
-
 static void check_syscall_restart(struct pt_regs *regs, struct k_sigaction *ka,
                                  int has_handler)
 {
@@ -114,30 +104,21 @@ static void check_syscall_restart(struct pt_regs *regs, struct k_sigaction *ka,
 
 static int do_signal(struct pt_regs *regs)
 {
-       sigset_t *oldset;
+       sigset_t *oldset = sigmask_to_save();
        siginfo_t info;
        int signr;
        struct k_sigaction ka;
        int ret;
        int is32 = is_32bit_task();
 
-       if (current_thread_info()->local_flags & _TLF_RESTORE_SIGMASK)
-               oldset = &current->saved_sigmask;
-       else
-               oldset = &current->blocked;
-
        signr = get_signal_to_deliver(&info, &ka, regs, NULL);
 
        /* Is there any syscall restart business here ? */
        check_syscall_restart(regs, &ka, signr > 0);
 
        if (signr <= 0) {
-               struct thread_info *ti = current_thread_info();
                /* No signal to deliver -- put the saved sigmask back */
-               if (ti->local_flags & _TLF_RESTORE_SIGMASK) {
-                       ti->local_flags &= ~_TLF_RESTORE_SIGMASK;
-                       sigprocmask(SIG_SETMASK, &current->saved_sigmask, NULL);
-               }
+               restore_saved_sigmask();
                regs->trap = 0;
                return 0;               /* no signals delivered */
        }
@@ -167,18 +148,7 @@ static int do_signal(struct pt_regs *regs)
 
        regs->trap = 0;
        if (ret) {
-               block_sigmask(&ka, signr);
-
-               /*
-                * A signal was successfully delivered; the saved sigmask is in
-                * its frame, and we can clear the TLF_RESTORE_SIGMASK flag.
-                */
-               current_thread_info()->local_flags &= ~_TLF_RESTORE_SIGMASK;
-
-               /*
-                * Let tracing know that we've done the handler setup.
-                */
-               tracehook_signal_handler(signr, &info, &ka, regs,
+               signal_delivered(signr, &info, &ka, regs,
                                         test_thread_flag(TIF_SINGLESTEP));
        }
 
@@ -193,8 +163,6 @@ void do_notify_resume(struct pt_regs *regs, unsigned long thread_info_flags)
        if (thread_info_flags & _TIF_NOTIFY_RESUME) {
                clear_thread_flag(TIF_NOTIFY_RESUME);
                tracehook_notify_resume(regs);
-               if (current->replacement_session_keyring)
-                       key_replace_session_keyring();
        }
 }
 
index 8dde973aaaf513ffd4c39a41f0f3bf2bead0d7db..e00acb4139346074ddfbda1c6553010614a2d78c 100644 (file)
 #ifndef _POWERPC_ARCH_SIGNAL_H
 #define _POWERPC_ARCH_SIGNAL_H
 
-#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
-
 extern void do_notify_resume(struct pt_regs *regs, unsigned long thread_info_flags);
 
 extern void __user * get_sigframe(struct k_sigaction *ka, struct pt_regs *regs,
                                  size_t frame_size, int is_32);
-extern void restore_sigmask(sigset_t *set);
 
 extern int handle_signal32(unsigned long sig, struct k_sigaction *ka,
                           siginfo_t *info, sigset_t *oldset,
index 61f6aff25edc3f94010bda48710d6a1f27e65161..8b4c049aee20e8604fa83a9a017366213d0ab6d9 100644 (file)
@@ -919,7 +919,7 @@ static int do_setcontext(struct ucontext __user *ucp, struct pt_regs *regs, int
        if (!access_ok(VERIFY_READ, mcp, sizeof(*mcp)))
                return -EFAULT;
 #endif
-       restore_sigmask(&set);
+       set_current_blocked(&set);
        if (restore_user_regs(regs, mcp, sig))
                return -EFAULT;
 
@@ -1273,7 +1273,7 @@ long sys_sigreturn(int r3, int r4, int r5, int r6, int r7, int r8,
        set.sig[0] = sigctx.oldmask;
        set.sig[1] = sigctx._unused[3];
 #endif
-       restore_sigmask(&set);
+       set_current_blocked(&set);
 
        sr = (struct mcontext __user *)from_user_ptr(sigctx.regs);
        addr = sr;
index 2692efdb154e210a5aa787e021ed960cb3bde591..d183f8719a505ce18e4cc8cb06ee4df3e8fa8674 100644 (file)
@@ -335,7 +335,7 @@ int sys_swapcontext(struct ucontext __user *old_ctx,
 
        if (__copy_from_user(&set, &new_ctx->uc_sigmask, sizeof(set)))
                do_exit(SIGSEGV);
-       restore_sigmask(&set);
+       set_current_blocked(&set);
        if (restore_sigcontext(regs, NULL, 0, &new_ctx->uc_mcontext))
                do_exit(SIGSEGV);
 
@@ -364,7 +364,7 @@ int sys_rt_sigreturn(unsigned long r3, unsigned long r4, unsigned long r5,
 
        if (__copy_from_user(&set, &uc->uc_sigmask, sizeof(set)))
                goto badframe;
-       restore_sigmask(&set);
+       set_current_blocked(&set);
        if (restore_sigcontext(regs, NULL, 1, &uc->uc_mcontext))
                goto badframe;
 
index 5b63bd3da4a968fab738434d0a83a7db2f722dc8..e779642c25e5e3192a39a4b167d505a8cfb63254 100644 (file)
@@ -333,9 +333,7 @@ static int __cpuinit mmu_context_cpu_notify(struct notifier_block *self,
                                            unsigned long action, void *hcpu)
 {
        unsigned int cpu = (unsigned int)(long)hcpu;
-#ifdef CONFIG_HOTPLUG_CPU
-       struct task_struct *p;
-#endif
+
        /* We don't touch CPU 0 map, it's allocated at aboot and kept
         * around forever
         */
@@ -358,12 +356,7 @@ static int __cpuinit mmu_context_cpu_notify(struct notifier_block *self,
                stale_map[cpu] = NULL;
 
                /* We also clear the cpu_vm_mask bits of CPUs going away */
-               read_lock(&tasklist_lock);
-               for_each_process(p) {
-                       if (p->mm)
-                               cpumask_clear_cpu(cpu, mm_cpumask(p->mm));
-               }
-               read_unlock(&tasklist_lock);
+               clear_tasks_mm_cpumask(cpu);
        break;
 #endif /* CONFIG_HOTPLUG_CPU */
        }
index b403c533432c94438260df3b524434efdb6db4ce..a39b4690c171621e78e2183c6b1b97bd25f4afaf 100644 (file)
@@ -87,6 +87,7 @@ config S390
        select ARCH_SAVE_PAGE_KEYS if HIBERNATION
        select HAVE_MEMBLOCK
        select HAVE_MEMBLOCK_NODE_MAP
+       select HAVE_CMPXCHG_LOCAL
        select ARCH_DISCARD_MEMBLOCK
        select ARCH_INLINE_SPIN_TRYLOCK
        select ARCH_INLINE_SPIN_TRYLOCK_BH
index e5beb490959bcea55fce5e2051061db7d0885bea..a6ff5a83e227279fe6e49dd9b6420eeca3400606 100644 (file)
@@ -13,8 +13,6 @@
  *
  */
 
-#ifdef __KERNEL__
-
 #ifndef _LINUX_BITOPS_H
 #error only <linux/bitops.h> can be included directly
 #endif
@@ -63,7 +61,7 @@ extern const char _ni_bitmap[];
 extern const char _zb_findmap[];
 extern const char _sb_findmap[];
 
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
 
 #define __BITOPS_ALIGN         3
 #define __BITOPS_WORDSIZE      32
@@ -83,7 +81,7 @@ extern const char _sb_findmap[];
                : "d" (__val), "Q" (*(unsigned long *) __addr)  \
                : "cc");
 
-#else /* __s390x__ */
+#else /* CONFIG_64BIT */
 
 #define __BITOPS_ALIGN         7
 #define __BITOPS_WORDSIZE      64
@@ -103,7 +101,7 @@ extern const char _sb_findmap[];
                : "d" (__val), "Q" (*(unsigned long *) __addr)  \
                : "cc");
 
-#endif /* __s390x__ */
+#endif /* CONFIG_64BIT */
 
 #define __BITOPS_WORDS(bits) (((bits)+__BITOPS_WORDSIZE-1)/__BITOPS_WORDSIZE)
 #define __BITOPS_BARRIER() asm volatile("" : : : "memory")
@@ -412,7 +410,7 @@ static inline unsigned long __ffz_word_loop(const unsigned long *addr,
        unsigned long bytes = 0;
 
        asm volatile(
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
                "       ahi     %1,-1\n"
                "       sra     %1,5\n"
                "       jz      1f\n"
@@ -449,7 +447,7 @@ static inline unsigned long __ffs_word_loop(const unsigned long *addr,
        unsigned long bytes = 0;
 
        asm volatile(
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
                "       ahi     %1,-1\n"
                "       sra     %1,5\n"
                "       jz      1f\n"
@@ -481,7 +479,7 @@ static inline unsigned long __ffs_word_loop(const unsigned long *addr,
  */
 static inline unsigned long __ffz_word(unsigned long nr, unsigned long word)
 {
-#ifdef __s390x__
+#ifdef CONFIG_64BIT
        if ((word & 0xffffffff) == 0xffffffff) {
                word >>= 32;
                nr += 32;
@@ -505,7 +503,7 @@ static inline unsigned long __ffz_word(unsigned long nr, unsigned long word)
  */
 static inline unsigned long __ffs_word(unsigned long nr, unsigned long word)
 {
-#ifdef __s390x__
+#ifdef CONFIG_64BIT
        if ((word & 0xffffffff) == 0) {
                word >>= 32;
                nr += 32;
@@ -546,7 +544,7 @@ static inline unsigned long __load_ulong_le(const unsigned long *p,
        unsigned long word;
 
        p = (unsigned long *)((unsigned long) p + offset);
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
        asm volatile(
                "       ic      %0,%O1(%R1)\n"
                "       icm     %0,2,%O1+1(%R1)\n"
@@ -834,7 +832,4 @@ static inline int find_next_bit_le(void *vaddr, unsigned long size,
 
 #include <asm-generic/bitops/ext2-atomic-setbit.h>
 
-
-#endif /* __KERNEL__ */
-
 #endif /* _S390_BITOPS_H */
index fc50a3342da3ba726f35db37df75110897c50478..4c8d4d5b8bd2ca545a1e35dcacaa4f45b115c083 100644 (file)
@@ -10,8 +10,6 @@
 #include <linux/spinlock.h>
 #include <asm/types.h>
 
-#ifdef __KERNEL__
-
 #define LPM_ANYPATH 0xff
 #define __MAX_CSSID 0
 
@@ -291,5 +289,3 @@ int chsc_sstpc(void *page, unsigned int op, u16 ctrl);
 int chsc_sstpi(void *page, void *result, size_t size);
 
 #endif
-
-#endif
index 81d7908416cf769202a40541d6a1779560712df5..8d798e962b632c9a8aa426576077a48d1f6f0a38 100644 (file)
@@ -29,7 +29,7 @@ static inline unsigned long __xchg(unsigned long x, void *ptr, int size)
                        "       cs      %0,0,%4\n"
                        "       jl      0b\n"
                        : "=&d" (old), "=Q" (*(int *) addr)
-                       : "d" (x << shift), "d" (~(255 << shift)),
+                       : "d" ((x & 0xff) << shift), "d" (~(0xff << shift)),
                          "Q" (*(int *) addr) : "memory", "cc", "0");
                return old >> shift;
        case 2:
@@ -44,7 +44,7 @@ static inline unsigned long __xchg(unsigned long x, void *ptr, int size)
                        "       cs      %0,0,%4\n"
                        "       jl      0b\n"
                        : "=&d" (old), "=Q" (*(int *) addr)
-                       : "d" (x << shift), "d" (~(65535 << shift)),
+                       : "d" ((x & 0xffff) << shift), "d" (~(0xffff << shift)),
                          "Q" (*(int *) addr) : "memory", "cc", "0");
                return old >> shift;
        case 4:
@@ -113,9 +113,10 @@ static inline unsigned long __cmpxchg(void *ptr, unsigned long old,
                        "       nr      %1,%5\n"
                        "       jnz     0b\n"
                        "1:"
-                       : "=&d" (prev), "=&d" (tmp), "=Q" (*(int *) ptr)
-                       : "d" (old << shift), "d" (new << shift),
-                         "d" (~(255 << shift)), "Q" (*(int *) ptr)
+                       : "=&d" (prev), "=&d" (tmp), "+Q" (*(int *) addr)
+                       : "d" ((old & 0xff) << shift),
+                         "d" ((new & 0xff) << shift),
+                         "d" (~(0xff << shift))
                        : "memory", "cc");
                return prev >> shift;
        case 2:
@@ -134,9 +135,10 @@ static inline unsigned long __cmpxchg(void *ptr, unsigned long old,
                        "       nr      %1,%5\n"
                        "       jnz     0b\n"
                        "1:"
-                       : "=&d" (prev), "=&d" (tmp), "=Q" (*(int *) ptr)
-                       : "d" (old << shift), "d" (new << shift),
-                         "d" (~(65535 << shift)), "Q" (*(int *) ptr)
+                       : "=&d" (prev), "=&d" (tmp), "+Q" (*(int *) addr)
+                       : "d" ((old & 0xffff) << shift),
+                         "d" ((new & 0xffff) << shift),
+                         "d" (~(0xffff << shift))
                        : "memory", "cc");
                return prev >> shift;
        case 4:
@@ -160,9 +162,14 @@ static inline unsigned long __cmpxchg(void *ptr, unsigned long old,
        return old;
 }
 
-#define cmpxchg(ptr, o, n)                                             \
-       ((__typeof__(*(ptr)))__cmpxchg((ptr), (unsigned long)(o),       \
-                                      (unsigned long)(n), sizeof(*(ptr))))
+#define cmpxchg(ptr, o, n)                                              \
+({                                                                      \
+       __typeof__(*(ptr)) __ret;                                        \
+       __ret = (__typeof__(*(ptr)))                                     \
+               __cmpxchg((ptr), (unsigned long)(o), (unsigned long)(n), \
+                         sizeof(*(ptr)));                               \
+       __ret;                                                           \
+})
 
 #ifdef CONFIG_64BIT
 #define cmpxchg64(ptr, o, n)                                           \
@@ -181,13 +188,19 @@ static inline unsigned long long __cmpxchg64(void *ptr,
                "       cds     %0,%2,%1"
                : "+&d" (rp_old), "=Q" (ptr)
                : "d" (rp_new), "Q" (ptr)
-               : "cc");
+               : "memory", "cc");
        return rp_old.pair;
 }
-#define cmpxchg64(ptr, o, n)                                           \
-       ((__typeof__(*(ptr)))__cmpxchg64((ptr),                         \
-                                        (unsigned long long)(o),       \
-                                        (unsigned long long)(n)))
+
+#define cmpxchg64(ptr, o, n)                           \
+({                                                     \
+       __typeof__(*(ptr)) __ret;                       \
+       __ret = (__typeof__(*(ptr)))                    \
+               __cmpxchg64((ptr),                      \
+                           (unsigned long long)(o),    \
+                           (unsigned long long)(n));   \
+       __ret;                                          \
+})
 #endif /* CONFIG_64BIT */
 
 #include <asm-generic/cmpxchg-local.h>
@@ -216,8 +229,13 @@ static inline unsigned long __cmpxchg_local(void *ptr,
  * them available.
  */
 #define cmpxchg_local(ptr, o, n)                                       \
-       ((__typeof__(*(ptr)))__cmpxchg_local((ptr), (unsigned long)(o), \
-                       (unsigned long)(n), sizeof(*(ptr))))
+({                                                                     \
+       __typeof__(*(ptr)) __ret;                                       \
+       __ret = (__typeof__(*(ptr)))                                    \
+               __cmpxchg_local((ptr), (unsigned long)(o),              \
+                               (unsigned long)(n), sizeof(*(ptr)));    \
+       __ret;                                                          \
+})
 
 #define cmpxchg64_local(ptr, o, n)     cmpxchg64((ptr), (o), (n))
 
index 24ef186a1c4f6f0b7170834986aa01d320e45163..718374de9c7f3f75f658bbaedb54695760ed25eb 100644 (file)
@@ -21,15 +21,15 @@ typedef unsigned long long __nocast cputime64_t;
 
 static inline unsigned long __div(unsigned long long n, unsigned long base)
 {
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
        register_pair rp;
 
        rp.pair = n >> 1;
        asm ("dr %0,%1" : "+d" (rp) : "d" (base >> 1));
        return rp.subreg.odd;
-#else /* __s390x__ */
+#else /* CONFIG_64BIT */
        return n / base;
-#endif /* __s390x__ */
+#endif /* CONFIG_64BIT */
 }
 
 #define cputime_one_jiffy              jiffies_to_cputime(1)
@@ -100,7 +100,7 @@ static inline void cputime_to_timespec(const cputime_t cputime,
                                       struct timespec *value)
 {
        unsigned long long __cputime = (__force unsigned long long) cputime;
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
        register_pair rp;
 
        rp.pair = __cputime >> 1;
@@ -128,7 +128,7 @@ static inline void cputime_to_timeval(const cputime_t cputime,
                                      struct timeval *value)
 {
        unsigned long long __cputime = (__force unsigned long long) cputime;
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
        register_pair rp;
 
        rp.pair = __cputime >> 1;
index ecde9417d669f20ef06d1eddab491aaab2ffb076..debfda33d1f86d88a8b3bce89e6b23962c643489 100644 (file)
@@ -7,7 +7,7 @@
 #ifndef __ASM_CTL_REG_H
 #define __ASM_CTL_REG_H
 
-#ifdef __s390x__
+#ifdef CONFIG_64BIT
 
 #define __ctl_load(array, low, high) ({                                \
        typedef struct { char _[sizeof(array)]; } addrtype;     \
@@ -25,7 +25,7 @@
                : "i" (low), "i" (high));                       \
        })
 
-#else /* __s390x__ */
+#else /* CONFIG_64BIT */
 
 #define __ctl_load(array, low, high) ({                                \
        typedef struct { char _[sizeof(array)]; } addrtype;     \
@@ -43,7 +43,7 @@
                : "i" (low), "i" (high));                       \
        })
 
-#endif /* __s390x__ */
+#endif /* CONFIG_64BIT */
 
 #define __ctl_set_bit(cr, bit) ({      \
        unsigned long __dummy;          \
index 83cf36cde2da2cc9356bc12d6d9ad9689cc9d7e1..7a68084ec2f0aa0443ff671048082562ca63c346 100644 (file)
 #ifndef _S390_CURRENT_H
 #define _S390_CURRENT_H
 
-#ifdef __KERNEL__
 #include <asm/lowcore.h>
 
 struct task_struct;
 
 #define current ((struct task_struct *const)S390_lowcore.current_task)
 
-#endif
-
 #endif /* !(_S390_CURRENT_H) */
index c4ee39f7a4d6a12895f6f3a8bf8b862b83ea9e4d..06151e6a309889a16b46e62efbc26798d8b0e42d 100644 (file)
 /*
  * These are used to set parameters in the core dumps.
  */
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
 #define ELF_CLASS      ELFCLASS32
-#else /* __s390x__ */
+#else /* CONFIG_64BIT */
 #define ELF_CLASS      ELFCLASS64
-#endif /* __s390x__ */
+#endif /* CONFIG_64BIT */
 #define ELF_DATA       ELFDATA2MSB
 #define ELF_ARCH       EM_S390
 
@@ -181,9 +181,9 @@ extern unsigned long elf_hwcap;
 extern char elf_platform[];
 #define ELF_PLATFORM (elf_platform)
 
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
 #define SET_PERSONALITY(ex) set_personality(PER_LINUX)
-#else /* __s390x__ */
+#else /* CONFIG_64BIT */
 #define SET_PERSONALITY(ex)                                    \
 do {                                                           \
        if (personality(current->personality) != PER_LINUX32)   \
@@ -194,7 +194,7 @@ do {                                                                \
        else                                                    \
                clear_thread_flag(TIF_31BIT);                   \
 } while (0)
-#endif /* __s390x__ */
+#endif /* CONFIG_64BIT */
 
 #define STACK_RND_MASK 0x7ffUL
 
index 81cf36b691f1dfd42c2ed4f5a48f6bc42a0a7a0e..96bc83ea5c90e0a05f4b959167145b5ef1484e52 100644 (file)
@@ -1,8 +1,6 @@
 #ifndef _ASM_S390_FUTEX_H
 #define _ASM_S390_FUTEX_H
 
-#ifdef __KERNEL__
-
 #include <linux/futex.h>
 #include <linux/uaccess.h>
 #include <asm/errno.h>
@@ -48,5 +46,4 @@ static inline int futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
        return uaccess.futex_atomic_cmpxchg(uval, uaddr, oldval, newval);
 }
 
-#endif /* __KERNEL__ */
 #endif /* _ASM_S390_FUTEX_H */
index aae276d00383cc90660f1e1771d0aee11ae36949..aef0dde340d1f54b866874a9f14061b8237ef4dc 100644 (file)
@@ -20,7 +20,7 @@
 #include <asm/cio.h>
 #include <asm/uaccess.h>
 
-#ifdef __s390x__
+#ifdef CONFIG_64BIT
 #define IDA_SIZE_LOG 12 /* 11 for 2k , 12 for 4k */
 #else
 #define IDA_SIZE_LOG 11 /* 11 for 2k , 12 for 4k */
@@ -33,7 +33,7 @@
 static inline int
 idal_is_needed(void *vaddr, unsigned int length)
 {
-#ifdef __s390x__
+#ifdef CONFIG_64BIT
        return ((__pa(vaddr) + length - 1) >> 31) != 0;
 #else
        return 0;
@@ -78,7 +78,7 @@ static inline unsigned long *idal_create_words(unsigned long *idaws,
 static inline int
 set_normalized_cda(struct ccw1 * ccw, void *vaddr)
 {
-#ifdef __s390x__
+#ifdef CONFIG_64BIT
        unsigned int nridaws;
        unsigned long *idal;
 
@@ -105,7 +105,7 @@ set_normalized_cda(struct ccw1 * ccw, void *vaddr)
 static inline void
 clear_normalized_cda(struct ccw1 * ccw)
 {
-#ifdef __s390x__
+#ifdef CONFIG_64BIT
        if (ccw->flags & CCW_FLAG_IDA) {
                kfree((void *)(unsigned long) ccw->cda);
                ccw->flags &= ~CCW_FLAG_IDA;
@@ -182,7 +182,7 @@ idal_buffer_free(struct idal_buffer *ib)
 static inline int
 __idal_buffer_is_needed(struct idal_buffer *ib)
 {
-#ifdef __s390x__
+#ifdef CONFIG_64BIT
        return ib->size > (4096ul << ib->page_order) ||
                idal_is_needed(ib->data[0], ib->size);
 #else
index 27216d317991af2bc7d212731855a5bb2b12e37e..f81a0975cbea0efb88db7b8a123072f125f80e10 100644 (file)
@@ -11,8 +11,6 @@
 #ifndef _S390_IO_H
 #define _S390_IO_H
 
-#ifdef __KERNEL__
-
 #include <asm/page.h>
 
 #define IO_SPACE_LIMIT 0xffffffff
@@ -46,6 +44,4 @@ void unxlate_dev_mem_ptr(unsigned long phys, void *addr);
  */
 #define xlate_dev_kmem_ptr(p)  p
 
-#endif /* __KERNEL__ */
-
 #endif
index 5289cacd4861773928e5257b670a98525637460c..2b9d41899d21af3201c71916b27806c511f37267 100644 (file)
@@ -17,7 +17,8 @@ enum interruption_class {
        EXTINT_VRT,
        EXTINT_SCP,
        EXTINT_IUC,
-       EXTINT_CPM,
+       EXTINT_CMS,
+       EXTINT_CMC,
        IOINT_CIO,
        IOINT_QAI,
        IOINT_DAS,
index 3f30dac804ea7ee92808bb5a1f51e16482462951..f4f38826eebb3347ed5c5039db329e1d318b2ef1 100644 (file)
 #ifndef _S390_KEXEC_H
 #define _S390_KEXEC_H
 
-#ifdef __KERNEL__
-#include <asm/page.h>
-#endif
 #include <asm/processor.h>
+#include <asm/page.h>
 /*
  * KEXEC_SOURCE_MEMORY_LIMIT maximum page get_free_page can return.
  * I.e. Maximum page that is mapped directly into kernel memory,
index 94ec3ee07983f8e9b97c6d857b5e7702bbfa36ee..0a88622339ee363d0663d53390a2106e26e51038 100644 (file)
@@ -1,8 +1,6 @@
-#ifdef __KERNEL__
 #ifndef _ASM_KMAP_TYPES_H
 #define _ASM_KMAP_TYPES_H
 
 #include <asm-generic/kmap_types.h>
 
 #endif
-#endif /* __KERNEL__ */
index 5d09e405c54d504a8e3a243b54155d91eb8e18db..69bdf72e95ecfd7fd5134640a48b929952a74747 100644 (file)
@@ -49,7 +49,7 @@ static inline int init_new_context(struct task_struct *tsk,
 
 #define destroy_context(mm)             do { } while (0)
 
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
 #define LCTL_OPCODE "lctl"
 #else
 #define LCTL_OPCODE "lctlg"
index 1cc1c5af705aadb431dbd2dfbced6f25b5b3fa67..f0b6b26b6e59de846b260deef64d3d0b07e0a188 100644 (file)
@@ -28,7 +28,7 @@ struct mod_arch_specific
        struct mod_arch_syminfo *syminfo;
 };
 
-#ifdef __s390x__
+#ifdef CONFIG_64BIT
 #define ElfW(x) Elf64_ ## x
 #define ELFW(x) ELF64_ ## x
 #else
index d07518af09ea828e1e20973aca47434ebd09ba02..295f2c4f1c96ab2dbd333b6321475796fa8b820b 100644 (file)
@@ -13,7 +13,6 @@
 
 #define OS_INFO_VMCOREINFO     0
 #define OS_INFO_REIPL_BLOCK    1
-#define OS_INFO_INIT_FN                2
 
 struct os_info_entry {
        u64     addr;
@@ -28,8 +27,8 @@ struct os_info {
        u16     version_minor;
        u64     crashkernel_addr;
        u64     crashkernel_size;
-       struct os_info_entry entry[3];
-       u8      reserved[4004];
+       struct os_info_entry entry[2];
+       u8      reserved[4024];
 } __packed;
 
 void os_info_init(void);
index 0fbd1899c7b039fe6924704ab73871fb13f6b53d..6537e72e0853d01473fe57ab1d83257e7487b2af 100644 (file)
@@ -15,7 +15,7 @@
  * per cpu area, use weak definitions to force the compiler to
  * generate external references.
  */
-#if defined(CONFIG_SMP) && defined(__s390x__) && defined(MODULE)
+#if defined(CONFIG_SMP) && defined(CONFIG_64BIT) && defined(MODULE)
 #define ARCH_NEEDS_WEAK_PER_CPU
 #endif
 
index 78e3041919dedd11556ed359c40c1b2d875a197c..43078c1943948ca9b801dc60f684ef7025eed616 100644 (file)
@@ -48,7 +48,7 @@ static inline void crst_table_init(unsigned long *crst, unsigned long entry)
        clear_table(crst, entry, sizeof(unsigned long)*2048);
 }
 
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
 
 static inline unsigned long pgd_entry_type(struct mm_struct *mm)
 {
@@ -64,7 +64,7 @@ static inline unsigned long pgd_entry_type(struct mm_struct *mm)
 #define pgd_populate(mm, pgd, pud)             BUG()
 #define pud_populate(mm, pud, pmd)             BUG()
 
-#else /* __s390x__ */
+#else /* CONFIG_64BIT */
 
 static inline unsigned long pgd_entry_type(struct mm_struct *mm)
 {
@@ -106,7 +106,7 @@ static inline void pud_populate(struct mm_struct *mm, pud_t *pud, pmd_t *pmd)
        pud_val(*pud) = _REGION3_ENTRY | __pa(pmd);
 }
 
-#endif /* __s390x__ */
+#endif /* CONFIG_64BIT */
 
 static inline pgd_t *pgd_alloc(struct mm_struct *mm)
 {
index 011358c1b18e0d1145874eaf5929df48e89ed435..b3227415abdaca94d3f3dcc301cb7ce425cd6a9e 100644 (file)
@@ -74,15 +74,15 @@ static inline int is_zero_pfn(unsigned long pfn)
  * table can map
  * PGDIR_SHIFT determines what a third-level page table entry can map
  */
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
 # define PMD_SHIFT     20
 # define PUD_SHIFT     20
 # define PGDIR_SHIFT   20
-#else /* __s390x__ */
+#else /* CONFIG_64BIT */
 # define PMD_SHIFT     20
 # define PUD_SHIFT     31
 # define PGDIR_SHIFT   42
-#endif /* __s390x__ */
+#endif /* CONFIG_64BIT */
 
 #define PMD_SIZE        (1UL << PMD_SHIFT)
 #define PMD_MASK        (~(PMD_SIZE-1))
@@ -98,13 +98,13 @@ static inline int is_zero_pfn(unsigned long pfn)
  * that leads to 1024 pte per pgd
  */
 #define PTRS_PER_PTE   256
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
 #define PTRS_PER_PMD   1
 #define PTRS_PER_PUD   1
-#else /* __s390x__ */
+#else /* CONFIG_64BIT */
 #define PTRS_PER_PMD   2048
 #define PTRS_PER_PUD   2048
-#endif /* __s390x__ */
+#endif /* CONFIG_64BIT */
 #define PTRS_PER_PGD   2048
 
 #define FIRST_USER_ADDRESS  0
@@ -276,7 +276,7 @@ extern struct page *vmemmap;
  * swap pte is 1011 and 0001, 0011, 0101, 0111 are invalid.
  */
 
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
 
 /* Bits in the segment table address-space-control-element */
 #define _ASCE_SPACE_SWITCH     0x80000000UL    /* space switch event       */
@@ -308,7 +308,7 @@ extern struct page *vmemmap;
 #define KVM_UR_BIT     0x00008000UL
 #define KVM_UC_BIT     0x00004000UL
 
-#else /* __s390x__ */
+#else /* CONFIG_64BIT */
 
 /* Bits in the segment/region table address-space-control-element */
 #define _ASCE_ORIGIN           ~0xfffUL/* segment table origin             */
@@ -363,7 +363,7 @@ extern struct page *vmemmap;
 #define KVM_UR_BIT     0x0000800000000000UL
 #define KVM_UC_BIT     0x0000400000000000UL
 
-#endif /* __s390x__ */
+#endif /* CONFIG_64BIT */
 
 /*
  * A user page table pointer has the space-switch-event bit, the
@@ -424,7 +424,7 @@ static inline int mm_has_pgste(struct mm_struct *mm)
 /*
  * pgd/pmd/pte query functions
  */
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
 
 static inline int pgd_present(pgd_t pgd) { return 1; }
 static inline int pgd_none(pgd_t pgd)    { return 0; }
@@ -434,7 +434,7 @@ static inline int pud_present(pud_t pud) { return 1; }
 static inline int pud_none(pud_t pud)   { return 0; }
 static inline int pud_bad(pud_t pud)    { return 0; }
 
-#else /* __s390x__ */
+#else /* CONFIG_64BIT */
 
 static inline int pgd_present(pgd_t pgd)
 {
@@ -490,7 +490,7 @@ static inline int pud_bad(pud_t pud)
        return (pud_val(pud) & mask) != 0;
 }
 
-#endif /* __s390x__ */
+#endif /* CONFIG_64BIT */
 
 static inline int pmd_present(pmd_t pmd)
 {
@@ -741,7 +741,7 @@ static inline int pte_young(pte_t pte)
 
 static inline void pgd_clear(pgd_t *pgd)
 {
-#ifdef __s390x__
+#ifdef CONFIG_64BIT
        if ((pgd_val(*pgd) & _REGION_ENTRY_TYPE_MASK) == _REGION_ENTRY_TYPE_R2)
                pgd_val(*pgd) = _REGION2_ENTRY_EMPTY;
 #endif
@@ -749,7 +749,7 @@ static inline void pgd_clear(pgd_t *pgd)
 
 static inline void pud_clear(pud_t *pud)
 {
-#ifdef __s390x__
+#ifdef CONFIG_64BIT
        if ((pud_val(*pud) & _REGION_ENTRY_TYPE_MASK) == _REGION_ENTRY_TYPE_R3)
                pud_val(*pud) = _REGION3_ENTRY_EMPTY;
 #endif
@@ -921,7 +921,7 @@ static inline int ptep_clear_flush_young(struct vm_area_struct *vma,
 static inline void __ptep_ipte(unsigned long address, pte_t *ptep)
 {
        if (!(pte_val(*ptep) & _PAGE_INVALID)) {
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
                /* pto must point to the start of the segment table */
                pte_t *pto = (pte_t *) (((unsigned long) ptep) & 0x7ffffc00);
 #else
@@ -1116,7 +1116,7 @@ static inline pte_t mk_pte(struct page *page, pgprot_t pgprot)
 #define pgd_offset(mm, address) ((mm)->pgd + pgd_index(address))
 #define pgd_offset_k(address) pgd_offset(&init_mm, address)
 
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
 
 #define pmd_deref(pmd) (pmd_val(pmd) & _SEGMENT_ENTRY_ORIGIN)
 #define pud_deref(pmd) ({ BUG(); 0UL; })
@@ -1125,7 +1125,7 @@ static inline pte_t mk_pte(struct page *page, pgprot_t pgprot)
 #define pud_offset(pgd, address) ((pud_t *) pgd)
 #define pmd_offset(pud, address) ((pmd_t *) pud + pmd_index(address))
 
-#else /* __s390x__ */
+#else /* CONFIG_64BIT */
 
 #define pmd_deref(pmd) (pmd_val(pmd) & _SEGMENT_ENTRY_ORIGIN)
 #define pud_deref(pud) (pud_val(pud) & _REGION_ENTRY_ORIGIN)
@@ -1147,7 +1147,7 @@ static inline pmd_t *pmd_offset(pud_t *pud, unsigned long address)
        return pmd + pmd_index(address);
 }
 
-#endif /* __s390x__ */
+#endif /* CONFIG_64BIT */
 
 #define pfn_pte(pfn,pgprot) mk_pte_phys(__pa((pfn) << PAGE_SHIFT),(pgprot))
 #define pte_pfn(x) (pte_val(x) >> PAGE_SHIFT)
@@ -1196,7 +1196,7 @@ static inline pmd_t *pmd_offset(pud_t *pud, unsigned long address)
  *  0000000000111111111122222222223333333333444444444455 5555 5 55566 66
  *  0123456789012345678901234567890123456789012345678901 2345 6 78901 23
  */
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
 #define __SWP_OFFSET_MASK (~0UL >> 12)
 #else
 #define __SWP_OFFSET_MASK (~0UL >> 11)
@@ -1217,11 +1217,11 @@ static inline pte_t mk_swap_pte(unsigned long type, unsigned long offset)
 #define __pte_to_swp_entry(pte)        ((swp_entry_t) { pte_val(pte) })
 #define __swp_entry_to_pte(x)  ((pte_t) { (x).val })
 
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
 # define PTE_FILE_MAX_BITS     26
-#else /* __s390x__ */
+#else /* CONFIG_64BIT */
 # define PTE_FILE_MAX_BITS     59
-#endif /* __s390x__ */
+#endif /* CONFIG_64BIT */
 
 #define pte_to_pgoff(__pte) \
        ((((__pte).pte >> 12) << 7) + (((__pte).pte >> 1) & 0x7f))
index edf8527ff08d9bdf9e5f0468e24faebfdd372c34..7be104c0f19230e157d569efb9a18002dfa4709d 100644 (file)
@@ -24,7 +24,6 @@ typedef unsigned short        __kernel_old_dev_t;
 
 typedef unsigned long   __kernel_ino_t;
 typedef unsigned short  __kernel_mode_t;
-typedef unsigned short  __kernel_nlink_t;
 typedef unsigned short  __kernel_ipc_pid_t;
 typedef unsigned short  __kernel_uid_t;
 typedef unsigned short  __kernel_gid_t;
@@ -35,7 +34,6 @@ typedef int             __kernel_ptrdiff_t;
 
 typedef unsigned int    __kernel_ino_t;
 typedef unsigned int    __kernel_mode_t;
-typedef unsigned int    __kernel_nlink_t;
 typedef int             __kernel_ipc_pid_t;
 typedef unsigned int    __kernel_uid_t;
 typedef unsigned int    __kernel_gid_t;
@@ -47,7 +45,6 @@ typedef unsigned long   __kernel_sigset_t;      /* at least 32 bits */
 
 #define __kernel_ino_t  __kernel_ino_t
 #define __kernel_mode_t __kernel_mode_t
-#define __kernel_nlink_t __kernel_nlink_t
 #define __kernel_ipc_pid_t __kernel_ipc_pid_t
 #define __kernel_uid_t __kernel_uid_t
 #define __kernel_gid_t __kernel_gid_t
index 6cbf31311673a37bc83de2be5add15bbc42ece5e..20d0585cf905675422ad406d406401eba2bd6f82 100644 (file)
@@ -20,7 +20,6 @@
 #include <asm/ptrace.h>
 #include <asm/setup.h>
 
-#ifdef __KERNEL__
 /*
  * Default implementation of macro that returns current
  * instruction pointer ("program counter").
@@ -33,39 +32,33 @@ static inline void get_cpu_id(struct cpuid *ptr)
 }
 
 extern void s390_adjust_jiffies(void);
-extern int get_cpu_capability(unsigned int *);
 extern const struct seq_operations cpuinfo_op;
 extern int sysctl_ieee_emulation_warnings;
 
 /*
  * User space process size: 2GB for 31 bit, 4TB or 8PT for 64 bit.
  */
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
 
 #define TASK_SIZE              (1UL << 31)
 #define TASK_UNMAPPED_BASE     (1UL << 30)
 
-#else /* __s390x__ */
+#else /* CONFIG_64BIT */
 
 #define TASK_SIZE_OF(tsk)      ((tsk)->mm->context.asce_limit)
 #define TASK_UNMAPPED_BASE     (test_thread_flag(TIF_31BIT) ? \
                                        (1UL << 30) : (1UL << 41))
 #define TASK_SIZE              TASK_SIZE_OF(current)
 
-#endif /* __s390x__ */
+#endif /* CONFIG_64BIT */
 
-#ifdef __KERNEL__
-
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
 #define STACK_TOP              (1UL << 31)
 #define STACK_TOP_MAX          (1UL << 31)
-#else /* __s390x__ */
+#else /* CONFIG_64BIT */
 #define STACK_TOP              (1UL << (test_thread_flag(TIF_31BIT) ? 31:42))
 #define STACK_TOP_MAX          (1UL << 42)
-#endif /* __s390x__ */
-
-
-#endif
+#endif /* CONFIG_64BIT */
 
 #define HAVE_ARCH_PICK_MMAP_LAYOUT
 
@@ -182,7 +175,7 @@ static inline void psw_set_key(unsigned int key)
  */
 static inline void __load_psw(psw_t psw)
 {
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
        asm volatile("lpsw  %0" : : "Q" (psw) : "cc");
 #else
        asm volatile("lpswe %0" : : "Q" (psw) : "cc");
@@ -200,7 +193,7 @@ static inline void __load_psw_mask (unsigned long mask)
 
        psw.mask = mask;
 
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
        asm volatile(
                "       basr    %0,0\n"
                "0:     ahi     %0,1f-0b\n"
@@ -208,14 +201,14 @@ static inline void __load_psw_mask (unsigned long mask)
                "       lpsw    %1\n"
                "1:"
                : "=&d" (addr), "=Q" (psw) : "Q" (psw) : "memory", "cc");
-#else /* __s390x__ */
+#else /* CONFIG_64BIT */
        asm volatile(
                "       larl    %0,1f\n"
                "       stg     %0,%O1+8(%R1)\n"
                "       lpswe   %1\n"
                "1:"
                : "=&d" (addr), "=Q" (psw) : "Q" (psw) : "memory", "cc");
-#endif /* __s390x__ */
+#endif /* CONFIG_64BIT */
 }
 
 /*
@@ -223,7 +216,7 @@ static inline void __load_psw_mask (unsigned long mask)
  */
 static inline unsigned long __rewind_psw(psw_t psw, unsigned long ilc)
 {
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
        if (psw.addr & PSW_ADDR_AMODE)
                /* 31 bit mode */
                return (psw.addr - ilc) | PSW_ADDR_AMODE;
@@ -253,7 +246,7 @@ static inline void __noreturn disabled_wait(unsigned long code)
          * Store status and then load disabled wait psw,
          * the processor is dead afterwards
          */
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
        asm volatile(
                "       stctl   0,0,0(%2)\n"
                "       ni      0(%2),0xef\n"   /* switch off protection */
@@ -272,7 +265,7 @@ static inline void __noreturn disabled_wait(unsigned long code)
                "       lpsw    0(%1)"
                : "=m" (ctl_buf)
                : "a" (&dw_psw), "a" (&ctl_buf), "m" (dw_psw) : "cc");
-#else /* __s390x__ */
+#else /* CONFIG_64BIT */
        asm volatile(
                "       stctg   0,0,0(%2)\n"
                "       ni      4(%2),0xef\n"   /* switch off protection */
@@ -305,7 +298,7 @@ static inline void __noreturn disabled_wait(unsigned long code)
                "       lpswe   0(%1)"
                : "=m" (ctl_buf)
                : "a" (&dw_psw), "a" (&ctl_buf), "m" (dw_psw) : "cc", "0", "1");
-#endif /* __s390x__ */
+#endif /* CONFIG_64BIT */
        while (1);
 }
 
@@ -338,12 +331,10 @@ extern void (*s390_base_ext_handler_fn)(void);
 
 #define ARCH_LOW_ADDRESS_LIMIT 0x7fffffffUL
 
-#endif
-
 /*
  * Helper macro for exception table entries
  */
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
 #define EX_TABLE(_fault,_target)                       \
        ".section __ex_table,\"a\"\n"                   \
        "       .align 4\n"                             \
index d0eb4653cebdb0d7bf0eab014cfb142904ad89b7..1ceee10264c3832bce52f2cae57070d549afc35c 100644 (file)
 #error "please don't include asm/rwsem.h directly, use linux/rwsem.h instead"
 #endif
 
-#ifdef __KERNEL__
-
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
 #define RWSEM_UNLOCKED_VALUE   0x00000000
 #define RWSEM_ACTIVE_BIAS      0x00000001
 #define RWSEM_ACTIVE_MASK      0x0000ffff
 #define RWSEM_WAITING_BIAS     (-0x00010000)
-#else /* __s390x__ */
+#else /* CONFIG_64BIT */
 #define RWSEM_UNLOCKED_VALUE   0x0000000000000000L
 #define RWSEM_ACTIVE_BIAS      0x0000000000000001L
 #define RWSEM_ACTIVE_MASK      0x00000000ffffffffL
 #define RWSEM_WAITING_BIAS     (-0x0000000100000000L)
-#endif /* __s390x__ */
+#endif /* CONFIG_64BIT */
 #define RWSEM_ACTIVE_READ_BIAS RWSEM_ACTIVE_BIAS
 #define RWSEM_ACTIVE_WRITE_BIAS        (RWSEM_WAITING_BIAS + RWSEM_ACTIVE_BIAS)
 
@@ -65,19 +63,19 @@ static inline void __down_read(struct rw_semaphore *sem)
        signed long old, new;
 
        asm volatile(
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
                "       l       %0,%2\n"
                "0:     lr      %1,%0\n"
                "       ahi     %1,%4\n"
                "       cs      %0,%1,%2\n"
                "       jl      0b"
-#else /* __s390x__ */
+#else /* CONFIG_64BIT */
                "       lg      %0,%2\n"
                "0:     lgr     %1,%0\n"
                "       aghi    %1,%4\n"
                "       csg     %0,%1,%2\n"
                "       jl      0b"
-#endif /* __s390x__ */
+#endif /* CONFIG_64BIT */
                : "=&d" (old), "=&d" (new), "=Q" (sem->count)
                : "Q" (sem->count), "i" (RWSEM_ACTIVE_READ_BIAS)
                : "cc", "memory");
@@ -93,7 +91,7 @@ static inline int __down_read_trylock(struct rw_semaphore *sem)
        signed long old, new;
 
        asm volatile(
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
                "       l       %0,%2\n"
                "0:     ltr     %1,%0\n"
                "       jm      1f\n"
@@ -101,7 +99,7 @@ static inline int __down_read_trylock(struct rw_semaphore *sem)
                "       cs      %0,%1,%2\n"
                "       jl      0b\n"
                "1:"
-#else /* __s390x__ */
+#else /* CONFIG_64BIT */
                "       lg      %0,%2\n"
                "0:     ltgr    %1,%0\n"
                "       jm      1f\n"
@@ -109,7 +107,7 @@ static inline int __down_read_trylock(struct rw_semaphore *sem)
                "       csg     %0,%1,%2\n"
                "       jl      0b\n"
                "1:"
-#endif /* __s390x__ */
+#endif /* CONFIG_64BIT */
                : "=&d" (old), "=&d" (new), "=Q" (sem->count)
                : "Q" (sem->count), "i" (RWSEM_ACTIVE_READ_BIAS)
                : "cc", "memory");
@@ -125,19 +123,19 @@ static inline void __down_write_nested(struct rw_semaphore *sem, int subclass)
 
        tmp = RWSEM_ACTIVE_WRITE_BIAS;
        asm volatile(
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
                "       l       %0,%2\n"
                "0:     lr      %1,%0\n"
                "       a       %1,%4\n"
                "       cs      %0,%1,%2\n"
                "       jl      0b"
-#else /* __s390x__ */
+#else /* CONFIG_64BIT */
                "       lg      %0,%2\n"
                "0:     lgr     %1,%0\n"
                "       ag      %1,%4\n"
                "       csg     %0,%1,%2\n"
                "       jl      0b"
-#endif /* __s390x__ */
+#endif /* CONFIG_64BIT */
                : "=&d" (old), "=&d" (new), "=Q" (sem->count)
                : "Q" (sem->count), "m" (tmp)
                : "cc", "memory");
@@ -158,19 +156,19 @@ static inline int __down_write_trylock(struct rw_semaphore *sem)
        signed long old;
 
        asm volatile(
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
                "       l       %0,%1\n"
                "0:     ltr     %0,%0\n"
                "       jnz     1f\n"
                "       cs      %0,%3,%1\n"
                "       jl      0b\n"
-#else /* __s390x__ */
+#else /* CONFIG_64BIT */
                "       lg      %0,%1\n"
                "0:     ltgr    %0,%0\n"
                "       jnz     1f\n"
                "       csg     %0,%3,%1\n"
                "       jl      0b\n"
-#endif /* __s390x__ */
+#endif /* CONFIG_64BIT */
                "1:"
                : "=&d" (old), "=Q" (sem->count)
                : "Q" (sem->count), "d" (RWSEM_ACTIVE_WRITE_BIAS)
@@ -186,19 +184,19 @@ static inline void __up_read(struct rw_semaphore *sem)
        signed long old, new;
 
        asm volatile(
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
                "       l       %0,%2\n"
                "0:     lr      %1,%0\n"
                "       ahi     %1,%4\n"
                "       cs      %0,%1,%2\n"
                "       jl      0b"
-#else /* __s390x__ */
+#else /* CONFIG_64BIT */
                "       lg      %0,%2\n"
                "0:     lgr     %1,%0\n"
                "       aghi    %1,%4\n"
                "       csg     %0,%1,%2\n"
                "       jl      0b"
-#endif /* __s390x__ */
+#endif /* CONFIG_64BIT */
                : "=&d" (old), "=&d" (new), "=Q" (sem->count)
                : "Q" (sem->count), "i" (-RWSEM_ACTIVE_READ_BIAS)
                : "cc", "memory");
@@ -216,19 +214,19 @@ static inline void __up_write(struct rw_semaphore *sem)
 
        tmp = -RWSEM_ACTIVE_WRITE_BIAS;
        asm volatile(
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
                "       l       %0,%2\n"
                "0:     lr      %1,%0\n"
                "       a       %1,%4\n"
                "       cs      %0,%1,%2\n"
                "       jl      0b"
-#else /* __s390x__ */
+#else /* CONFIG_64BIT */
                "       lg      %0,%2\n"
                "0:     lgr     %1,%0\n"
                "       ag      %1,%4\n"
                "       csg     %0,%1,%2\n"
                "       jl      0b"
-#endif /* __s390x__ */
+#endif /* CONFIG_64BIT */
                : "=&d" (old), "=&d" (new), "=Q" (sem->count)
                : "Q" (sem->count), "m" (tmp)
                : "cc", "memory");
@@ -246,19 +244,19 @@ static inline void __downgrade_write(struct rw_semaphore *sem)
 
        tmp = -RWSEM_WAITING_BIAS;
        asm volatile(
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
                "       l       %0,%2\n"
                "0:     lr      %1,%0\n"
                "       a       %1,%4\n"
                "       cs      %0,%1,%2\n"
                "       jl      0b"
-#else /* __s390x__ */
+#else /* CONFIG_64BIT */
                "       lg      %0,%2\n"
                "0:     lgr     %1,%0\n"
                "       ag      %1,%4\n"
                "       csg     %0,%1,%2\n"
                "       jl      0b"
-#endif /* __s390x__ */
+#endif /* CONFIG_64BIT */
                : "=&d" (old), "=&d" (new), "=Q" (sem->count)
                : "Q" (sem->count), "m" (tmp)
                : "cc", "memory");
@@ -274,19 +272,19 @@ static inline void rwsem_atomic_add(long delta, struct rw_semaphore *sem)
        signed long old, new;
 
        asm volatile(
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
                "       l       %0,%2\n"
                "0:     lr      %1,%0\n"
                "       ar      %1,%4\n"
                "       cs      %0,%1,%2\n"
                "       jl      0b"
-#else /* __s390x__ */
+#else /* CONFIG_64BIT */
                "       lg      %0,%2\n"
                "0:     lgr     %1,%0\n"
                "       agr     %1,%4\n"
                "       csg     %0,%1,%2\n"
                "       jl      0b"
-#endif /* __s390x__ */
+#endif /* CONFIG_64BIT */
                : "=&d" (old), "=&d" (new), "=Q" (sem->count)
                : "Q" (sem->count), "d" (delta)
                : "cc", "memory");
@@ -300,24 +298,23 @@ static inline long rwsem_atomic_update(long delta, struct rw_semaphore *sem)
        signed long old, new;
 
        asm volatile(
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
                "       l       %0,%2\n"
                "0:     lr      %1,%0\n"
                "       ar      %1,%4\n"
                "       cs      %0,%1,%2\n"
                "       jl      0b"
-#else /* __s390x__ */
+#else /* CONFIG_64BIT */
                "       lg      %0,%2\n"
                "0:     lgr     %1,%0\n"
                "       agr     %1,%4\n"
                "       csg     %0,%1,%2\n"
                "       jl      0b"
-#endif /* __s390x__ */
+#endif /* CONFIG_64BIT */
                : "=&d" (old), "=&d" (new), "=Q" (sem->count)
                : "Q" (sem->count), "d" (delta)
                : "cc", "memory");
        return new;
 }
 
-#endif /* __KERNEL__ */
 #endif /* _S390_RWSEM_H */
index 7244e1f6412669f4f0be00e6ec9ab9e31005ed9f..40eb2ff88e9e59766cc7e1ba931e65a303e06a16 100644 (file)
 #include <asm/lowcore.h>
 #include <asm/types.h>
 
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
 #define IPL_DEVICE        (*(unsigned long *)  (0x10404))
 #define INITRD_START      (*(unsigned long *)  (0x1040C))
 #define INITRD_SIZE       (*(unsigned long *)  (0x10414))
 #define OLDMEM_BASE      (*(unsigned long *)  (0x1041C))
 #define OLDMEM_SIZE      (*(unsigned long *)  (0x10424))
-#else /* __s390x__ */
+#else /* CONFIG_64BIT */
 #define IPL_DEVICE        (*(unsigned long *)  (0x10400))
 #define INITRD_START      (*(unsigned long *)  (0x10408))
 #define INITRD_SIZE       (*(unsigned long *)  (0x10410))
 #define OLDMEM_BASE      (*(unsigned long *)  (0x10418))
 #define OLDMEM_SIZE      (*(unsigned long *)  (0x10420))
-#endif /* __s390x__ */
+#endif /* CONFIG_64BIT */
 #define COMMAND_LINE      ((char *)            (0x10480))
 
 #define CHUNK_READ_WRITE 0
@@ -89,7 +89,7 @@ extern unsigned int user_mode;
 
 #define MACHINE_HAS_DIAG9C     (S390_lowcore.machine_flags & MACHINE_FLAG_DIAG9C)
 
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
 #define MACHINE_HAS_IEEE       (S390_lowcore.machine_flags & MACHINE_FLAG_IEEE)
 #define MACHINE_HAS_CSP                (S390_lowcore.machine_flags & MACHINE_FLAG_CSP)
 #define MACHINE_HAS_IDTE       (0)
@@ -100,7 +100,7 @@ extern unsigned int user_mode;
 #define MACHINE_HAS_PFMF       (0)
 #define MACHINE_HAS_SPP                (0)
 #define MACHINE_HAS_TOPOLOGY   (0)
-#else /* __s390x__ */
+#else /* CONFIG_64BIT */
 #define MACHINE_HAS_IEEE       (1)
 #define MACHINE_HAS_CSP                (1)
 #define MACHINE_HAS_IDTE       (S390_lowcore.machine_flags & MACHINE_FLAG_IDTE)
@@ -111,7 +111,7 @@ extern unsigned int user_mode;
 #define MACHINE_HAS_PFMF       (S390_lowcore.machine_flags & MACHINE_FLAG_PFMF)
 #define MACHINE_HAS_SPP                (S390_lowcore.machine_flags & MACHINE_FLAG_SPP)
 #define MACHINE_HAS_TOPOLOGY   (S390_lowcore.machine_flags & MACHINE_FLAG_TOPOLOGY)
-#endif /* __s390x__ */
+#endif /* CONFIG_64BIT */
 
 #define ZFCPDUMP_HSA_SIZE      (32UL<<20)
 #define ZFCPDUMP_HSA_SIZE_MAX  (64UL<<20)
@@ -153,19 +153,19 @@ extern void (*_machine_power_off)(void);
 
 #else /* __ASSEMBLY__ */
 
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
 #define IPL_DEVICE        0x10404
 #define INITRD_START      0x1040C
 #define INITRD_SIZE       0x10414
 #define OLDMEM_BASE      0x1041C
 #define OLDMEM_SIZE      0x10424
-#else /* __s390x__ */
+#else /* CONFIG_64BIT */
 #define IPL_DEVICE        0x10400
 #define INITRD_START      0x10408
 #define INITRD_SIZE       0x10410
 #define OLDMEM_BASE      0x10418
 #define OLDMEM_SIZE      0x10420
-#endif /* __s390x__ */
+#endif /* CONFIG_64BIT */
 #define COMMAND_LINE      0x10480
 
 #endif /* __ASSEMBLY__ */
index ca3f8814e3614050d5714843fea3a97c90db8bca..5959bfb3b693ce79def5ccfe2bb548b4dad6378b 100644 (file)
@@ -51,7 +51,7 @@
        wl = __wl;                                      \
 })
 
-#ifdef __s390x__
+#ifdef CONFIG_64BIT
 #define udiv_qrnnd(q, r, n1, n0, d)                    \
   do { unsigned long __n;                              \
        unsigned int __r, __d;                          \
index cd0241db5a4688b754d6838497cfc06768b4025e..8cc160c9e1cb108c2ce9829dc4c5684a34a3b4ae 100644 (file)
@@ -9,8 +9,6 @@
 #ifndef _S390_STRING_H_
 #define _S390_STRING_H_
 
-#ifdef __KERNEL__
-
 #ifndef _LINUX_TYPES_H
 #include <linux/types.h>
 #endif
@@ -152,6 +150,4 @@ size_t strlen(const char *s);
 size_t strnlen(const char * s, size_t n);
 #endif /* !IN_ARCH_STRING_C */
 
-#endif /* __KERNEL__ */
-
 #endif /* __S390_STRING_H_ */
index 003b04edcff6636f1e5c23a04054dbd7f4de749d..4e40b25cd0600e7d76fcdf38789f86fd32275839 100644 (file)
@@ -9,15 +9,13 @@
 #ifndef _ASM_THREAD_INFO_H
 #define _ASM_THREAD_INFO_H
 
-#ifdef __KERNEL__
-
 /*
  * Size of kernel stack for each process
  */
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
 #define THREAD_ORDER 1
 #define ASYNC_ORDER  1
-#else /* __s390x__ */
+#else /* CONFIG_64BIT */
 #ifndef __SMALL_STACK
 #define THREAD_ORDER 2
 #define ASYNC_ORDER  2
@@ -25,7 +23,7 @@
 #define THREAD_ORDER 1
 #define ASYNC_ORDER  1
 #endif
-#endif /* __s390x__ */
+#endif /* CONFIG_64BIT */
 
 #define THREAD_SIZE (PAGE_SIZE << THREAD_ORDER)
 #define ASYNC_SIZE  (PAGE_SIZE << ASYNC_ORDER)
@@ -123,8 +121,6 @@ static inline struct thread_info *current_thread_info(void)
 #define is_32bit_task()                (1)
 #endif
 
-#endif /* __KERNEL__ */
-
 #define PREEMPT_ACTIVE         0x4000000
 
 #endif /* _ASM_THREAD_INFO_H */
index e63069ba39e3b274d46d691f62ae74e5d14d4249..15d647901e5cafc14a355325a7ebe25d53da88da 100644 (file)
@@ -10,8 +10,6 @@
 #ifndef _ASM_S390_TIMER_H
 #define _ASM_S390_TIMER_H
 
-#ifdef __KERNEL__
-
 #include <linux/timer.h>
 
 #define VTIMER_MAX_SLICE (0x7ffffffffffff000LL)
@@ -50,6 +48,4 @@ extern void vtime_init(void);
 extern void vtime_stop_cpu(void);
 extern void vtime_start_leave(void);
 
-#endif /* __KERNEL__ */
-
 #endif /* _ASM_S390_TIMER_H */
index 775a5eea8f9eb9896e9d38e809dc74d51823d99f..06e5acbc84bd50ef4917eabb5f6fc8cd2d6f6b22 100644 (file)
@@ -106,7 +106,7 @@ static inline void pte_free_tlb(struct mmu_gather *tlb, pgtable_t pte,
 static inline void pmd_free_tlb(struct mmu_gather *tlb, pmd_t *pmd,
                                unsigned long address)
 {
-#ifdef __s390x__
+#ifdef CONFIG_64BIT
        if (tlb->mm->context.asce_limit <= (1UL << 31))
                return;
        if (!tlb->fullmm)
@@ -125,7 +125,7 @@ static inline void pmd_free_tlb(struct mmu_gather *tlb, pmd_t *pmd,
 static inline void pud_free_tlb(struct mmu_gather *tlb, pud_t *pud,
                                unsigned long address)
 {
-#ifdef __s390x__
+#ifdef CONFIG_64BIT
        if (tlb->mm->context.asce_limit <= (1UL << 42))
                return;
        if (!tlb->fullmm)
index 1d8648cf2fea81eb7a6fdbe5129fd22478b074c2..9fde315f3a7cd42184a54f2174584258076e6a60 100644 (file)
@@ -27,12 +27,12 @@ static inline void __tlb_flush_global(void)
        register unsigned long reg4 asm("4");
        long dummy;
 
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
        if (!MACHINE_HAS_CSP) {
                smp_ptlb_all();
                return;
        }
-#endif /* __s390x__ */
+#endif /* CONFIG_64BIT */
 
        dummy = 0;
        reg2 = reg3 = 0;
index 05ebbcdbbf6ba7d34791545f3f37355fcb10ef00..6c8c35f8df142b3b8e22dd21d3230a2403dcab38 100644 (file)
@@ -28,7 +28,7 @@ typedef __signed__ long saddr_t;
 
 #ifndef __ASSEMBLY__
 
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
 typedef union {
        unsigned long long pair;
        struct {
@@ -37,7 +37,7 @@ typedef union {
        } subreg;
 } register_pair;
 
-#endif /* ! __s390x__   */
+#endif /* ! CONFIG_64BIT   */
 #endif /* __ASSEMBLY__  */
 #endif /* __KERNEL__    */
 #endif /* _S390_TYPES_H */
index 8f2cada4f7c916d9d88fb87cbb390cf99e4b793e..1f3a79bcd262722e251d575009cec172a9c77509 100644 (file)
 
 #define segment_eq(a,b) ((a).ar4 == (b).ar4)
 
-#define __access_ok(addr, size)        \
-({                             \
-       __chk_user_ptr(addr);   \
-       1;                      \
+static inline int __range_ok(unsigned long addr, unsigned long size)
+{
+       return 1;
+}
+
+#define __access_ok(addr, size)                                \
+({                                                     \
+       __chk_user_ptr(addr);                           \
+       __range_ok((unsigned long)(addr), (size));      \
 })
 
 #define access_ok(type, addr, size) __access_ok(addr, size)
@@ -377,7 +382,7 @@ clear_user(void __user *to, unsigned long n)
 }
 
 extern int memcpy_real(void *, void *, size_t);
-extern void copy_to_absolute_zero(void *dest, void *src, size_t count);
+extern void memcpy_absolute(void *, void *, size_t);
 extern int copy_to_user_real(void __user *dest, void *src, size_t count);
 extern int copy_from_user_real(void *dest, void __user *src, size_t count);
 
index c4a11cfad3c8a55aa1178b5769f82d973d963c66..a73eb2e1e918351356005b99940629235ef6ee98 100644 (file)
@@ -1,8 +1,6 @@
 #ifndef __S390_VDSO_H__
 #define __S390_VDSO_H__
 
-#ifdef __KERNEL__
-
 /* Default link addresses for the vDSOs */
 #define VDSO32_LBASE   0
 #define VDSO64_LBASE   0
@@ -45,7 +43,4 @@ void vdso_free_per_cpu(struct _lowcore *lowcore);
 #endif
 
 #endif /* __ASSEMBLY__ */
-
-#endif /* __KERNEL__ */
-
 #endif /* __S390_VDSO_H__ */
index 3aa4d00aaf50ec0af3d4581facf40c1cffeef171..c880ff72db44a0247f57c48450a230f682843ec1 100644 (file)
@@ -88,6 +88,9 @@ ENTRY(diag308_reset)
        stctg   %c0,%c15,0(%r4)
        larl    %r4,.Lfpctl             # Floating point control register
        stfpc   0(%r4)
+       larl    %r4,.Lcontinue_psw      # Save PSW flags
+       epsw    %r2,%r3
+       stm     %r2,%r3,0(%r4)
        larl    %r4,.Lrestart_psw       # Setup restart PSW at absolute 0
        lghi    %r3,0
        lg      %r4,0(%r4)              # Save PSW
@@ -103,11 +106,20 @@ ENTRY(diag308_reset)
        lctlg   %c0,%c15,0(%r4)
        larl    %r4,.Lfpctl             # Restore floating point ctl register
        lfpc    0(%r4)
+       larl    %r4,.Lcontinue_psw      # Restore PSW flags
+       lpswe   0(%r4)
+.Lcontinue:
        br      %r14
 .align 16
 .Lrestart_psw:
        .long   0x00080000,0x80000000 + .Lrestart_part2
 
+       .section .data..nosave,"aw",@progbits
+.align 8
+.Lcontinue_psw:
+       .quad   0,.Lcontinue
+       .previous
+
        .section .bss
 .align 8
 .Lctlregs:
index 377c096ca4a72c658327b543127692c1e1a91f0c..3c0c19830c37f5a0f7896d0a292762b794b30a74 100644 (file)
@@ -32,8 +32,6 @@
 #include "compat_ptrace.h"
 #include "entry.h"
 
-#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
-
 typedef struct 
 {
        __u8 callee_used_stack[__SIGNAL_FRAMESIZE32];
@@ -364,7 +362,6 @@ asmlinkage long sys32_sigreturn(void)
                goto badframe;
        if (__copy_from_user(&set.sig, &frame->sc.oldmask, _SIGMASK_COPY_SIZE32))
                goto badframe;
-       sigdelsetmask(&set, ~_BLOCKABLE);
        set_current_blocked(&set);
        if (restore_sigregs32(regs, &frame->sregs))
                goto badframe;
@@ -390,7 +387,6 @@ asmlinkage long sys32_rt_sigreturn(void)
                goto badframe;
        if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
                goto badframe;
-       sigdelsetmask(&set, ~_BLOCKABLE);
        set_current_blocked(&set);
        if (restore_sigregs32(regs, &frame->uc.uc_mcontext))
                goto badframe;
@@ -572,7 +568,7 @@ give_sigsegv:
  * OK, we're invoking a handler
  */    
 
-int handle_signal32(unsigned long sig, struct k_sigaction *ka,
+void handle_signal32(unsigned long sig, struct k_sigaction *ka,
                    siginfo_t *info, sigset_t *oldset, struct pt_regs *regs)
 {
        int ret;
@@ -583,8 +579,8 @@ int handle_signal32(unsigned long sig, struct k_sigaction *ka,
        else
                ret = setup_frame32(sig, ka, oldset, regs);
        if (ret)
-               return ret;
-       block_sigmask(ka, sig);
-       return 0;
+               return;
+       signal_delivered(sig, info, ka, regs,
+                                test_thread_flag(TIF_SINGLE_STEP));
 }
 
index d84181f1f5e83f4dc82b6099a495bc763e317375..6684fff1755834f14837868248a31a66644f4575 100644 (file)
@@ -237,7 +237,7 @@ static noinline __init void detect_machine_type(void)
                S390_lowcore.machine_flags |= MACHINE_FLAG_VM;
 }
 
-static __init void early_pgm_check_handler(void)
+static void early_pgm_check_handler(void)
 {
        unsigned long addr;
        const struct exception_table_entry *fixup;
index 6cdddac93a2e48ed47f58d74648c5d858c7d8557..f66a229ab0b3fdf52308cb55c0ab627ef9564a7b 100644 (file)
@@ -31,7 +31,7 @@ void do_per_trap(struct pt_regs *regs);
 void syscall_trace(struct pt_regs *regs, int entryexit);
 void kernel_stack_overflow(struct pt_regs * regs);
 void do_signal(struct pt_regs *regs);
-int handle_signal32(unsigned long sig, struct k_sigaction *ka,
+void handle_signal32(unsigned long sig, struct k_sigaction *ka,
                    siginfo_t *info, sigset_t *oldset, struct pt_regs *regs);
 void do_notify_resume(struct pt_regs *regs);
 
index e1ac3893e972883e2c17b2787fad0a8857efc8d5..796c976b5fdc1b49a6d82e5a9a7cf19906a9443c 100644 (file)
@@ -85,11 +85,6 @@ startup_kdump_relocated:
        basr    %r13,0
 0:
        mvc     0(8,%r0),.Lrestart_psw-0b(%r13) # Setup restart PSW
-       mvc     464(16,%r0),.Lpgm_psw-0b(%r13)  # Setup pgm check PSW
-       lhi     %r1,1                           # Start new kernel
-       diag    %r1,%r1,0x308                   # with diag 308
-
-.Lno_diag308:                                  # No diag 308
        sam31                                   # Switch to 31 bit addr mode
        sr      %r1,%r1                         # Erase register r1
        sr      %r2,%r2                         # Erase register r2
@@ -98,8 +93,6 @@ startup_kdump_relocated:
 .align 8
 .Lrestart_psw:
        .long   0x00080000,0x80000000 + startup
-.Lpgm_psw:
-       .quad   0x0000000180000000,0x0000000000000000 + .Lno_diag308
 #else
 .align 2
 .Lep_startup_kdump:
index 8342e65a140daf7bb3fc9f589fbcd1f8d417f322..2f6cfd460cb6ad5a04fd033ea7f515b49ba3e487 100644 (file)
@@ -1528,12 +1528,15 @@ static struct shutdown_action __refdata dump_action = {
 
 static void dump_reipl_run(struct shutdown_trigger *trigger)
 {
-       u32 csum;
-
-       csum = csum_partial(reipl_block_actual, reipl_block_actual->hdr.len, 0);
-       copy_to_absolute_zero(&S390_lowcore.ipib_checksum, &csum, sizeof(csum));
-       copy_to_absolute_zero(&S390_lowcore.ipib, &reipl_block_actual,
-                             sizeof(reipl_block_actual));
+       struct {
+               void    *addr;
+               __u32   csum;
+       } __packed ipib;
+
+       ipib.csum = csum_partial(reipl_block_actual,
+                                reipl_block_actual->hdr.len, 0);
+       ipib.addr = reipl_block_actual;
+       memcpy_absolute(&S390_lowcore.ipib, &ipib, sizeof(ipib));
        dump_run(trigger);
 }
 
@@ -1750,6 +1753,7 @@ static struct kobj_attribute on_restart_attr =
 
 static void __do_restart(void *ignore)
 {
+       __arch_local_irq_stosm(0x04); /* enable DAT */
        smp_send_stop();
 #ifdef CONFIG_CRASH_DUMP
        crash_kexec(NULL);
index 8a22c27219dd0748f380a0aef0d63128b01ec21a..b4f4a7133fa10e3456b82f7f27fc8fbf70d942ed 100644 (file)
@@ -42,7 +42,8 @@ static const struct irq_class intrclass_names[] = {
        {.name = "VRT", .desc = "[EXT] Virtio" },
        {.name = "SCP", .desc = "[EXT] Service Call" },
        {.name = "IUC", .desc = "[EXT] IUCV" },
-       {.name = "CPM", .desc = "[EXT] CPU Measurement" },
+       {.name = "CMS", .desc = "[EXT] CPU-Measurement: Sampling" },
+       {.name = "CMC", .desc = "[EXT] CPU-Measurement: Counter" },
        {.name = "CIO", .desc = "[I/O] Common I/O Layer Interrupt" },
        {.name = "QAI", .desc = "[I/O] QDIO Adapter Interrupt" },
        {.name = "DAS", .desc = "[I/O] DASD" },
index bdad47d544783d89fa015ee6db34d52a2a555695..cdacf8f91b2d11b7cb3acd683f7a8db830c6fb05 100644 (file)
@@ -24,6 +24,7 @@
 #include <asm/ipl.h>
 #include <asm/diag.h>
 #include <asm/asm-offsets.h>
+#include <asm/os_info.h>
 
 typedef void (*relocate_kernel_t)(kimage_entry_t *, unsigned long);
 
@@ -79,8 +80,8 @@ static void __do_machine_kdump(void *image)
 #ifdef CONFIG_CRASH_DUMP
        int (*start_kdump)(int) = (void *)((struct kimage *) image)->start;
 
-       __load_psw_mask(PSW_MASK_BASE | PSW_DEFAULT_KEY | PSW_MASK_EA | PSW_MASK_BA);
        setup_regs();
+       __load_psw_mask(PSW_MASK_BASE | PSW_DEFAULT_KEY | PSW_MASK_EA | PSW_MASK_BA);
        start_kdump(1);
 #endif
 }
@@ -114,8 +115,13 @@ static void crash_map_pages(int enable)
               size % KEXEC_CRASH_MEM_ALIGN);
        if (enable)
                vmem_add_mapping(crashk_res.start, size);
-       else
+       else {
                vmem_remove_mapping(crashk_res.start, size);
+               if (size)
+                       os_info_crashkernel_add(crashk_res.start, size);
+               else
+                       os_info_crashkernel_add(0, 0);
+       }
 }
 
 /*
@@ -208,6 +214,7 @@ static void __machine_kexec(void *data)
 {
        struct kimage *image = data;
 
+       __arch_local_irq_stosm(0x04); /* enable DAT */
        pfault_fini();
        tracing_off();
        debug_locks_off();
index e8d6c214d498a0aaf5037f7d64c5fd7e6306fbc9..95fa5ac6c4cedbf6d287ca25708b906cb1f35c4c 100644 (file)
@@ -60,7 +60,7 @@ void __init os_info_init(void)
        os_info.version_minor = OS_INFO_VERSION_MINOR;
        os_info.magic = OS_INFO_MAGIC;
        os_info.csum = os_info_csum(&os_info);
-       copy_to_absolute_zero(&S390_lowcore.os_info, &ptr, sizeof(ptr));
+       memcpy_absolute(&S390_lowcore.os_info, &ptr, sizeof(ptr));
 }
 
 #ifdef CONFIG_CRASH_DUMP
@@ -138,7 +138,6 @@ static void os_info_old_init(void)
                goto fail_free;
        os_info_old_alloc(OS_INFO_VMCOREINFO, 1);
        os_info_old_alloc(OS_INFO_REIPL_BLOCK, 1);
-       os_info_old_alloc(OS_INFO_INIT_FN, PAGE_SIZE);
        pr_info("crashkernel: addr=0x%lx size=%lu\n",
                (unsigned long) os_info_old->crashkernel_addr,
                (unsigned long) os_info_old->crashkernel_size);
index cb019f429e88ba22745a14bf38c714743c2383ad..9871b1971ed7602a7efef88fc62d019dde3b98c4 100644 (file)
@@ -225,7 +225,7 @@ static void cpumf_measurement_alert(struct ext_code ext_code,
        if (!(alert & CPU_MF_INT_CF_MASK))
                return;
 
-       kstat_cpu(smp_processor_id()).irqs[EXTINT_CPM]++;
+       kstat_cpu(smp_processor_id()).irqs[EXTINT_CMC]++;
        cpuhw = &__get_cpu_var(cpu_hw_events);
 
        /* Measurement alerts are shared and might happen when the PMU
index 06264ae8ccd9e05fd54f166d4d2bdaa37df18441..489d1d8d96b068f63b61886ee3c55d50f52b3913 100644 (file)
@@ -428,10 +428,12 @@ static void __init setup_lowcore(void)
        lc->restart_fn = (unsigned long) do_restart;
        lc->restart_data = 0;
        lc->restart_source = -1UL;
-       memcpy(&S390_lowcore.restart_stack, &lc->restart_stack,
-              4*sizeof(unsigned long));
-       copy_to_absolute_zero(&S390_lowcore.restart_psw,
-                             &lc->restart_psw, sizeof(psw_t));
+
+       /* Setup absolute zero lowcore */
+       memcpy_absolute(&S390_lowcore.restart_stack, &lc->restart_stack,
+                       4 * sizeof(unsigned long));
+       memcpy_absolute(&S390_lowcore.restart_psw, &lc->restart_psw,
+                       sizeof(lc->restart_psw));
 
        set_prefix((u32)(unsigned long) lc);
        lowcore_ptr[0] = lc;
@@ -598,7 +600,7 @@ static void __init setup_vmcoreinfo(void)
 #ifdef CONFIG_KEXEC
        unsigned long ptr = paddr_vmcoreinfo_note();
 
-       copy_to_absolute_zero(&S390_lowcore.vmcore_info, &ptr, sizeof(ptr));
+       memcpy_absolute(&S390_lowcore.vmcore_info, &ptr, sizeof(ptr));
 #endif
 }
 
index f626232e216c0ca100f14c1cbeed52c8696c578f..ac565b44aabbf76c23da00540f8f2884e06f07a6 100644 (file)
@@ -33,9 +33,6 @@
 #include <asm/switch_to.h>
 #include "entry.h"
 
-#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
-
-
 typedef struct 
 {
        __u8 callee_used_stack[__SIGNAL_FRAMESIZE];
@@ -169,7 +166,6 @@ SYSCALL_DEFINE0(sigreturn)
                goto badframe;
        if (__copy_from_user(&set.sig, &frame->sc.oldmask, _SIGMASK_COPY_SIZE))
                goto badframe;
-       sigdelsetmask(&set, ~_BLOCKABLE);
        set_current_blocked(&set);
        if (restore_sigregs(regs, &frame->sregs))
                goto badframe;
@@ -189,7 +185,6 @@ SYSCALL_DEFINE0(rt_sigreturn)
                goto badframe;
        if (__copy_from_user(&set.sig, &frame->uc.uc_sigmask, sizeof(set)))
                goto badframe;
-       sigdelsetmask(&set, ~_BLOCKABLE);
        set_current_blocked(&set);
        if (restore_sigregs(regs, &frame->uc.uc_mcontext))
                goto badframe;
@@ -367,7 +362,7 @@ give_sigsegv:
        return -EFAULT;
 }
 
-static int handle_signal(unsigned long sig, struct k_sigaction *ka,
+static void handle_signal(unsigned long sig, struct k_sigaction *ka,
                         siginfo_t *info, sigset_t *oldset,
                         struct pt_regs *regs)
 {
@@ -379,9 +374,9 @@ static int handle_signal(unsigned long sig, struct k_sigaction *ka,
        else
                ret = setup_frame(sig, ka, oldset, regs);
        if (ret)
-               return ret;
-       block_sigmask(ka, sig);
-       return 0;
+               return;
+       signal_delivered(sig, info, ka, regs,
+                                test_thread_flag(TIF_SINGLE_STEP));
 }
 
 /*
@@ -398,12 +393,7 @@ void do_signal(struct pt_regs *regs)
        siginfo_t info;
        int signr;
        struct k_sigaction ka;
-       sigset_t *oldset;
-
-       if (test_thread_flag(TIF_RESTORE_SIGMASK))
-               oldset = &current->saved_sigmask;
-       else
-               oldset = &current->blocked;
+       sigset_t *oldset = sigmask_to_save();
 
        /*
         * Get signal to deliver. When running under ptrace, at this point
@@ -441,24 +431,10 @@ void do_signal(struct pt_regs *regs)
                /* No longer in a system call */
                clear_thread_flag(TIF_SYSCALL);
 
-               if ((is_compat_task() ?
-                    handle_signal32(signr, &ka, &info, oldset, regs) :
-                    handle_signal(signr, &ka, &info, oldset, regs)) == 0) {
-                       /*
-                        * A signal was successfully delivered; the saved
-                        * sigmask will have been stored in the signal frame,
-                        * and will be restored by sigreturn, so we can simply
-                        * clear the TIF_RESTORE_SIGMASK flag.
-                        */
-                       if (test_thread_flag(TIF_RESTORE_SIGMASK))
-                               clear_thread_flag(TIF_RESTORE_SIGMASK);
-
-                       /*
-                        * Let tracing know that we've done the handler setup.
-                        */
-                       tracehook_signal_handler(signr, &info, &ka, regs,
-                                        test_thread_flag(TIF_SINGLE_STEP));
-               }
+               if (is_compat_task())
+                       handle_signal32(signr, &ka, &info, oldset, regs);
+               else
+                       handle_signal(signr, &ka, &info, oldset, regs);
                return;
        }
 
@@ -484,16 +460,11 @@ void do_signal(struct pt_regs *regs)
        /*
         * If there's no signal to deliver, we just put the saved sigmask back.
         */
-       if (test_thread_flag(TIF_RESTORE_SIGMASK)) {
-               clear_thread_flag(TIF_RESTORE_SIGMASK);
-               sigprocmask(SIG_SETMASK, &current->saved_sigmask, NULL);
-       }
+       restore_saved_sigmask();
 }
 
 void do_notify_resume(struct pt_regs *regs)
 {
        clear_thread_flag(TIF_NOTIFY_RESUME);
        tracehook_notify_resume(regs);
-       if (current->replacement_session_keyring)
-               key_replace_session_keyring();
 }
index 647ba9425893de446e5237d84234c612ab5fedc9..15cca26ccb6c4ff1cbde51c60259a7ec95471718 100644 (file)
@@ -297,26 +297,27 @@ static void pcpu_start_fn(struct pcpu *pcpu, void (*func)(void *), void *data)
 static void pcpu_delegate(struct pcpu *pcpu, void (*func)(void *),
                          void *data, unsigned long stack)
 {
-       struct _lowcore *lc = pcpu->lowcore;
-       unsigned short this_cpu;
+       struct _lowcore *lc = lowcore_ptr[pcpu - pcpu_devices];
+       struct {
+               unsigned long   stack;
+               void            *func;
+               void            *data;
+               unsigned long   source;
+       } restart = { stack, func, data, stap() };
 
        __load_psw_mask(psw_kernel_bits);
-       this_cpu = stap();
-       if (pcpu->address == this_cpu)
+       if (pcpu->address == restart.source)
                func(data);     /* should not return */
        /* Stop target cpu (if func returns this stops the current cpu). */
        pcpu_sigp_retry(pcpu, sigp_stop, 0);
        /* Restart func on the target cpu and stop the current cpu. */
-       lc->restart_stack = stack;
-       lc->restart_fn = (unsigned long) func;
-       lc->restart_data = (unsigned long) data;
-       lc->restart_source = (unsigned long) this_cpu;
+       memcpy_absolute(&lc->restart_stack, &restart, sizeof(restart));
        asm volatile(
                "0:     sigp    0,%0,6  # sigp restart to target cpu\n"
                "       brc     2,0b    # busy, try again\n"
                "1:     sigp    0,%1,5  # sigp stop to current cpu\n"
                "       brc     2,1b    # busy, try again\n"
-               : : "d" (pcpu->address), "d" (this_cpu) : "0", "1", "cc");
+               : : "d" (pcpu->address), "d" (restart.source) : "0", "1", "cc");
        for (;;) ;
 }
 
@@ -800,17 +801,6 @@ void __noreturn cpu_die(void)
 
 #endif /* CONFIG_HOTPLUG_CPU */
 
-static void smp_call_os_info_init_fn(void)
-{
-       int (*init_fn)(void);
-       unsigned long size;
-
-       init_fn = os_info_old_entry(OS_INFO_INIT_FN, &size);
-       if (!init_fn)
-               return;
-       init_fn();
-}
-
 void __init smp_prepare_cpus(unsigned int max_cpus)
 {
        /* request the 0x1201 emergency signal external interrupt */
@@ -819,7 +809,6 @@ void __init smp_prepare_cpus(unsigned int max_cpus)
        /* request the 0x1202 external call external interrupt */
        if (register_external_interrupt(0x1202, do_ext_call_interrupt) != 0)
                panic("Couldn't request external interrupt 0x1202");
-       smp_call_os_info_init_fn();
        smp_detect_cpus();
 }
 
@@ -943,19 +932,6 @@ static struct attribute_group cpu_common_attr_group = {
        .attrs = cpu_common_attrs,
 };
 
-static ssize_t show_capability(struct device *dev,
-                               struct device_attribute *attr, char *buf)
-{
-       unsigned int capability;
-       int rc;
-
-       rc = get_cpu_capability(&capability);
-       if (rc)
-               return rc;
-       return sprintf(buf, "%u\n", capability);
-}
-static DEVICE_ATTR(capability, 0444, show_capability, NULL);
-
 static ssize_t show_idle_count(struct device *dev,
                                struct device_attribute *attr, char *buf)
 {
@@ -993,7 +969,6 @@ static ssize_t show_idle_time(struct device *dev,
 static DEVICE_ATTR(idle_time_us, 0444, show_idle_time, NULL);
 
 static struct attribute *cpu_online_attrs[] = {
-       &dev_attr_capability.attr,
        &dev_attr_idle_count.attr,
        &dev_attr_idle_time_us.attr,
        NULL,
index 2a94b774695c069241ed413f50a0f9b8b183c656..fa0eb238dac7d8940321c239d970d1245078d219 100644 (file)
@@ -392,27 +392,6 @@ static __init int create_proc_service_level(void)
 }
 subsys_initcall(create_proc_service_level);
 
-/*
- * Bogomips calculation based on cpu capability.
- */
-int get_cpu_capability(unsigned int *capability)
-{
-       struct sysinfo_1_2_2 *info;
-       int rc;
-
-       info = (void *) get_zeroed_page(GFP_KERNEL);
-       if (!info)
-               return -ENOMEM;
-       rc = stsi(info, 1, 2, 2);
-       if (rc == -ENOSYS)
-               goto out;
-       rc = 0;
-       *capability = info->capability;
-out:
-       free_page((unsigned long) info);
-       return rc;
-}
-
 /*
  * CPU capability might have changed. Therefore recalculate loops_per_jiffy.
  */
index 60455f104ea36ee9d3a22fd7100031c21f94866e..58a75a8ae90ce7beae1a4553801c47dd544cd1ee 100644 (file)
@@ -14,7 +14,7 @@
 #include <asm/futex.h>
 #include "uaccess.h"
 
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
 #define AHI    "ahi"
 #define ALR    "alr"
 #define CLR    "clr"
index bb1a7eed42ce4cbef8350dca7a8eb85269fb1966..57e94298539b51326ff1d9a4a10a9ef36c405326 100644 (file)
@@ -15,7 +15,7 @@
 #include <asm/futex.h>
 #include "uaccess.h"
 
-#ifndef __s390x__
+#ifndef CONFIG_64BIT
 #define AHI    "ahi"
 #define ALR    "alr"
 #define CLR    "clr"
index 795a0a9bb2eba72875aa3a300f98929daa1c6c12..921fa541dc0431050dfc6b429b4836b94972d6a0 100644 (file)
@@ -101,19 +101,27 @@ int memcpy_real(void *dest, void *src, size_t count)
 }
 
 /*
- * Copy memory to absolute zero
+ * Copy memory in absolute mode (kernel to kernel)
  */
-void copy_to_absolute_zero(void *dest, void *src, size_t count)
+void memcpy_absolute(void *dest, void *src, size_t count)
 {
-       unsigned long cr0;
+       unsigned long cr0, flags, prefix;
 
-       BUG_ON((unsigned long) dest + count >= sizeof(struct _lowcore));
-       preempt_disable();
+       flags = arch_local_irq_save();
        __ctl_store(cr0, 0, 0);
        __ctl_clear_bit(0, 28); /* disable lowcore protection */
-       memcpy_real(dest + store_prefix(), src, count);
+       prefix = store_prefix();
+       if (prefix) {
+               local_mcck_disable();
+               set_prefix(0);
+               memcpy(dest, src, count);
+               set_prefix(prefix);
+               local_mcck_enable();
+       } else {
+               memcpy(dest, src, count);
+       }
        __ctl_load(cr0, 0, 0);
-       preempt_enable();
+       arch_local_irq_restore(flags);
 }
 
 /*
@@ -187,20 +195,6 @@ static int is_swapped(unsigned long addr)
        return 0;
 }
 
-/*
- * Return swapped prefix or zero page address
- */
-static unsigned long get_swapped(unsigned long addr)
-{
-       unsigned long prefix = store_prefix();
-
-       if (addr < sizeof(struct _lowcore))
-               return addr + prefix;
-       if (addr >= prefix && addr < prefix + sizeof(struct _lowcore))
-               return addr - prefix;
-       return addr;
-}
-
 /*
  * Convert a physical pointer for /dev/mem access
  *
@@ -218,7 +212,7 @@ void *xlate_dev_mem_ptr(unsigned long addr)
                size = PAGE_SIZE - (addr & ~PAGE_MASK);
                bounce = (void *) __get_free_page(GFP_ATOMIC);
                if (bounce)
-                       memcpy_real(bounce, (void *) get_swapped(addr), size);
+                       memcpy_absolute(bounce, (void *) addr, size);
        }
        preempt_enable();
        put_online_cpus();
index 4799383e2df9551c45ad69f08f57455bb9771dc0..71ae20df674e53f051834f20319f3c48fae1358b 100644 (file)
@@ -109,7 +109,7 @@ static int vmem_add_mem(unsigned long start, unsigned long size, int ro)
                pte = mk_pte_phys(address, __pgprot(ro ? _PAGE_RO : 0));
                pm_dir = pmd_offset(pu_dir, address);
 
-#ifdef __s390x__
+#ifdef CONFIG_64BIT
                if (MACHINE_HAS_HPAGE && !(address & ~HPAGE_MASK) &&
                    (address + HPAGE_SIZE <= start + size) &&
                    (address >= HPAGE_SIZE)) {
index c6646de07bf455acb80c3c94674cdc61c535677c..a4a89fa980d6c4e0e8e44a00e8519d9ead2ac2b1 100644 (file)
@@ -235,7 +235,7 @@ static void hws_ext_handler(struct ext_code ext_code,
        if (!(param32 & CPU_MF_INT_SF_MASK))
                return;
 
-       kstat_cpu(smp_processor_id()).irqs[EXTINT_CPM]++;
+       kstat_cpu(smp_processor_id()).irqs[EXTINT_CMS]++;
        atomic_xchg(&cb->ext_params, atomic_read(&cb->ext_params) | param32);
 
        if (hws_wq)
index d4a49011c48a58d4311b182f6015efeb67a55e6d..e382c52ca0d90b455d9bee68f7587f4278d36bc8 100644 (file)
@@ -34,8 +34,6 @@
 #include <asm/syscalls.h>
 #include <asm/ucontext.h>
 
-#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
-
 struct rt_sigframe {
        u32 rs_ass[4];          /* argument save space */
        u32 rs_code[2];         /* signal trampoline */
@@ -162,7 +160,6 @@ score_rt_sigreturn(struct pt_regs *regs)
        if (__copy_from_user(&set, &frame->rs_uc.uc_sigmask, sizeof(set)))
                goto badframe;
 
-       sigdelsetmask(&set, ~_BLOCKABLE);
        set_current_blocked(&set);
 
        sig = restore_sigcontext(regs, &frame->rs_uc.uc_mcontext);
@@ -241,11 +238,9 @@ give_sigsegv:
        return -EFAULT;
 }
 
-static int handle_signal(unsigned long sig, siginfo_t *info,
-       struct k_sigaction *ka, sigset_t *oldset, struct pt_regs *regs)
+static void handle_signal(unsigned long sig, siginfo_t *info,
+       struct k_sigaction *ka, struct pt_regs *regs)
 {
-       int ret;
-
        if (regs->is_syscall) {
                switch (regs->regs[4]) {
                case ERESTART_RESTARTBLOCK:
@@ -269,18 +264,15 @@ static int handle_signal(unsigned long sig, siginfo_t *info,
        /*
         * Set up the stack frame
         */
-       ret = setup_rt_frame(ka, regs, sig, oldset, info);
-
-       if (ret == 0)
-               block_sigmask(ka, sig);
+       if (setup_rt_frame(ka, regs, sig, sigmask_to_save(), info) < 0)
+               return;
 
-       return ret;
+       signal_delivered(sig, info, ka, regs, 0);
 }
 
 static void do_signal(struct pt_regs *regs)
 {
        struct k_sigaction ka;
-       sigset_t *oldset;
        siginfo_t info;
        int signr;
 
@@ -292,25 +284,10 @@ static void do_signal(struct pt_regs *regs)
        if (!user_mode(regs))
                return;
 
-       if (test_thread_flag(TIF_RESTORE_SIGMASK))
-               oldset = &current->saved_sigmask;
-       else
-               oldset = &current->blocked;
-
        signr = get_signal_to_deliver(&info, &ka, regs, NULL);
        if (signr > 0) {
                /* Actually deliver the signal.  */
-               if (handle_signal(signr, &info, &ka, oldset, regs) == 0) {
-                       /*
-                        * A signal was successfully delivered; the saved
-                        * sigmask will have been stored in the signal frame,
-                        * and will be restored by sigreturn, so we can simply
-                        * clear the TIF_RESTORE_SIGMASK flag.
-                        */
-                       if (test_thread_flag(TIF_RESTORE_SIGMASK))
-                               clear_thread_flag(TIF_RESTORE_SIGMASK);
-               }
-
+               handle_signal(signr, &info, &ka, regs);
                return;
        }
 
@@ -337,10 +314,7 @@ static void do_signal(struct pt_regs *regs)
         * If there's no signal to deliver, we just put the saved sigmask
         * back
         */
-       if (test_thread_flag(TIF_RESTORE_SIGMASK)) {
-               clear_thread_flag(TIF_RESTORE_SIGMASK);
-               sigprocmask(SIG_SETMASK, &current->saved_sigmask, NULL);
-       }
+       restore_saved_sigmask();
 }
 
 /*
@@ -356,7 +330,5 @@ asmlinkage void do_notify_resume(struct pt_regs *regs, void *unused,
        if (thread_info_flags & _TIF_NOTIFY_RESUME) {
                clear_thread_flag(TIF_NOTIFY_RESUME);
                tracehook_notify_resume(regs);
-               if (current->replacement_session_keyring)
-                       key_replace_session_keyring();
        }
 }
index abda58467ece9e86ff1249029bcc7143281b2f04..ba0bdc423b072fa62f74fbc64e1bc5b683f2af7d 100644 (file)
@@ -3,8 +3,6 @@
 
 typedef unsigned short __kernel_mode_t;
 #define __kernel_mode_t __kernel_mode_t
-typedef unsigned short __kernel_nlink_t;
-#define __kernel_nlink_t __kernel_nlink_t
 typedef unsigned short __kernel_ipc_pid_t;
 #define __kernel_ipc_pid_t __kernel_ipc_pid_t
 typedef unsigned short __kernel_uid_t;
index fcda07b4a616be8196f105ce5d2faee8682c9af1..244f7e950e176b0cbdc907f70b4fdf88572b08f0 100644 (file)
@@ -3,8 +3,6 @@
 
 typedef unsigned short __kernel_mode_t;
 #define __kernel_mode_t __kernel_mode_t
-typedef unsigned short __kernel_nlink_t;
-#define __kernel_nlink_t __kernel_nlink_t
 typedef unsigned short __kernel_ipc_pid_t;
 #define __kernel_ipc_pid_t __kernel_ipc_pid_t
 typedef unsigned short __kernel_uid_t;
index 0c04ffc4f12c41d344eded872aea06aaf7232e2b..bc13b57cdc834210b95aaa70ea603298ed55f468 100644 (file)
@@ -169,7 +169,7 @@ static inline void set_restore_sigmask(void)
 {
        struct thread_info *ti = current_thread_info();
        ti->status |= TS_RESTORE_SIGMASK;
-       set_bit(TIF_SIGPENDING, (unsigned long *)&ti->flags);
+       WARN_ON(!test_bit(TIF_SIGPENDING, (unsigned long *)&ti->flags));
 }
 
 #define TI_FLAG_FAULT_CODE_SHIFT       24
@@ -189,6 +189,23 @@ static inline unsigned int get_thread_fault_code(void)
        struct thread_info *ti = current_thread_info();
        return ti->flags >> TI_FLAG_FAULT_CODE_SHIFT;
 }
+
+static inline void clear_restore_sigmask(void)
+{
+       current_thread_info()->status &= ~TS_RESTORE_SIGMASK;
+}
+static inline bool test_restore_sigmask(void)
+{
+       return current_thread_info()->status & TS_RESTORE_SIGMASK;
+}
+static inline bool test_and_clear_restore_sigmask(void)
+{
+       struct thread_info *ti = current_thread_info();
+       if (!(ti->status & TS_RESTORE_SIGMASK))
+               return false;
+       ti->status &= ~TS_RESTORE_SIGMASK;
+       return true;
+}
 #endif /* !__ASSEMBLY__ */
 
 #endif /* __KERNEL__ */
index cb4172c8af7d81c90e373a7c9a7ecb61a0bbd3a9..d6b7b6154f8764576abac916033326ba1c1e6bd2 100644 (file)
@@ -32,8 +32,6 @@
 #include <asm/syscalls.h>
 #include <asm/fpu.h>
 
-#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
-
 struct fdpic_func_descriptor {
        unsigned long   text;
        unsigned long   GOT;
@@ -226,7 +224,6 @@ asmlinkage int sys_sigreturn(unsigned long r4, unsigned long r5,
                                    sizeof(frame->extramask))))
                goto badframe;
 
-       sigdelsetmask(&set, ~_BLOCKABLE);
        set_current_blocked(&set);
 
        if (restore_sigcontext(regs, &frame->sc, &r0))
@@ -256,7 +253,6 @@ asmlinkage int sys_rt_sigreturn(unsigned long r4, unsigned long r5,
        if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
                goto badframe;
 
-       sigdelsetmask(&set, ~_BLOCKABLE);
        set_current_blocked(&set);
 
        if (restore_sigcontext(regs, &frame->uc.uc_mcontext, &r0))
@@ -522,10 +518,11 @@ handle_syscall_restart(unsigned long save_r0, struct pt_regs *regs,
 /*
  * OK, we're invoking a handler
  */
-static int
+static void
 handle_signal(unsigned long sig, struct k_sigaction *ka, siginfo_t *info,
-             sigset_t *oldset, struct pt_regs *regs, unsigned int save_r0)
+             struct pt_regs *regs, unsigned int save_r0)
 {
+       sigset_t *oldset = sigmask_to_save();
        int ret;
 
        /* Set up the stack frame */
@@ -534,10 +531,10 @@ handle_signal(unsigned long sig, struct k_sigaction *ka, siginfo_t *info,
        else
                ret = setup_frame(sig, ka, oldset, regs);
 
-       if (ret == 0)
-               block_sigmask(ka, sig);
-
-       return ret;
+       if (ret)
+               return;
+       signal_delivered(sig, info, ka, regs,
+                       test_thread_flag(TIF_SINGLESTEP));
 }
 
 /*
@@ -554,7 +551,6 @@ static void do_signal(struct pt_regs *regs, unsigned int save_r0)
        siginfo_t info;
        int signr;
        struct k_sigaction ka;
-       sigset_t *oldset;
 
        /*
         * We want the common case to go fast, which
@@ -565,30 +561,12 @@ static void do_signal(struct pt_regs *regs, unsigned int save_r0)
        if (!user_mode(regs))
                return;
 
-       if (current_thread_info()->status & TS_RESTORE_SIGMASK)
-               oldset = &current->saved_sigmask;
-       else
-               oldset = &current->blocked;
-
        signr = get_signal_to_deliver(&info, &ka, regs, NULL);
        if (signr > 0) {
                handle_syscall_restart(save_r0, regs, &ka.sa);
 
                /* Whee!  Actually deliver the signal.  */
-               if (handle_signal(signr, &ka, &info, oldset,
-                                 regs, save_r0) == 0) {
-                       /*
-                        * A signal was successfully delivered; the saved
-                        * sigmask will have been stored in the signal frame,
-                        * and will be restored by sigreturn, so we can simply
-                        * clear the TS_RESTORE_SIGMASK flag
-                        */
-                       current_thread_info()->status &= ~TS_RESTORE_SIGMASK;
-
-                       tracehook_signal_handler(signr, &info, &ka, regs,
-                                       test_thread_flag(TIF_SINGLESTEP));
-               }
-
+               handle_signal(signr, &ka, &info, regs, save_r0);
                return;
        }
 
@@ -610,10 +588,7 @@ static void do_signal(struct pt_regs *regs, unsigned int save_r0)
         * If there's no signal to deliver, we just put the saved sigmask
         * back.
         */
-       if (current_thread_info()->status & TS_RESTORE_SIGMASK) {
-               current_thread_info()->status &= ~TS_RESTORE_SIGMASK;
-               sigprocmask(SIG_SETMASK, &current->saved_sigmask, NULL);
-       }
+       restore_saved_sigmask();
 }
 
 asmlinkage void do_notify_resume(struct pt_regs *regs, unsigned int save_r0,
@@ -626,7 +601,5 @@ asmlinkage void do_notify_resume(struct pt_regs *regs, unsigned int save_r0,
        if (thread_info_flags & _TIF_NOTIFY_RESUME) {
                clear_thread_flag(TIF_NOTIFY_RESUME);
                tracehook_notify_resume(regs);
-               if (current->replacement_session_keyring)
-                       key_replace_session_keyring();
        }
 }
index b589a354c069aec0ee66d231b7790c7c0d9b3717..6b5b3dfe886b2d3c15a4b08a4d4f21ab74e90da0 100644 (file)
 
 #define DEBUG_SIG 0
 
-#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
-
-static int
+static void
 handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka,
-               sigset_t *oldset, struct pt_regs * regs);
+               struct pt_regs * regs);
 
 static inline void
 handle_syscall_restart(struct pt_regs *regs, struct sigaction *sa)
@@ -88,7 +86,6 @@ static void do_signal(struct pt_regs *regs)
        siginfo_t info;
        int signr;
        struct k_sigaction ka;
-       sigset_t *oldset;
 
        /*
         * We want the common case to go fast, which
@@ -99,28 +96,13 @@ static void do_signal(struct pt_regs *regs)
        if (!user_mode(regs))
                return;
 
-       if (current_thread_info()->status & TS_RESTORE_SIGMASK)
-               oldset = &current->saved_sigmask;
-       else
-               oldset = &current->blocked;
-
        signr = get_signal_to_deliver(&info, &ka, regs, 0);
        if (signr > 0) {
                handle_syscall_restart(regs, &ka.sa);
 
                /* Whee!  Actually deliver the signal.  */
-               if (handle_signal(signr, &info, &ka, oldset, regs) == 0) {
-                       /*
-                        * If a signal was successfully delivered, the
-                        * saved sigmask is in its frame, and we can
-                        * clear the TS_RESTORE_SIGMASK flag.
-                        */
-                       current_thread_info()->status &= ~TS_RESTORE_SIGMASK;
-
-                       tracehook_signal_handler(signr, &info, &ka, regs,
-                                       test_thread_flag(TIF_SINGLESTEP));
-                       return;
-               }
+               handle_signal(signr, &info, &ka, regs);
+               return;
        }
 
        /* Did we come from a system call? */
@@ -143,12 +125,7 @@ static void do_signal(struct pt_regs *regs)
        }
 
        /* No signal to deliver -- put the saved sigmask back */
-       if (current_thread_info()->status & TS_RESTORE_SIGMASK) {
-               current_thread_info()->status &= ~TS_RESTORE_SIGMASK;
-               sigprocmask(SIG_SETMASK, &current->saved_sigmask, NULL);
-       }
-
-       return;
+       restore_saved_sigmask();
 }
 
 /*
@@ -351,7 +328,6 @@ asmlinkage int sys_sigreturn(unsigned long r2, unsigned long r3,
                                    sizeof(frame->extramask))))
                goto badframe;
 
-       sigdelsetmask(&set, ~_BLOCKABLE);
        set_current_blocked(&set);
 
        if (restore_sigcontext(regs, &frame->sc, &ret))
@@ -384,7 +360,6 @@ asmlinkage int sys_rt_sigreturn(unsigned long r2, unsigned long r3,
        if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
                goto badframe;
 
-       sigdelsetmask(&set, ~_BLOCKABLE);
        set_current_blocked(&set);
 
        if (restore_sigcontext(regs, &frame->uc.uc_mcontext, &ret))
@@ -659,10 +634,11 @@ give_sigsegv:
 /*
  * OK, we're invoking a handler
  */
-static int
+static void
 handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka,
-               sigset_t *oldset, struct pt_regs * regs)
+               struct pt_regs * regs)
 {
+       sigset_t *oldset = sigmask_to_save();
        int ret;
 
        /* Set up the stack frame */
@@ -671,10 +647,11 @@ handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka,
        else
                ret = setup_frame(sig, ka, oldset, regs);
 
-       if (ret == 0)
-               block_sigmask(ka, sig);
+       if (ret)
+               return;
 
-       return ret;
+       signal_delivered(sig, info, ka, regs,
+                       test_thread_flag(TIF_SINGLESTEP));
 }
 
 asmlinkage void do_notify_resume(struct pt_regs *regs, unsigned long thread_info_flags)
@@ -685,7 +662,5 @@ asmlinkage void do_notify_resume(struct pt_regs *regs, unsigned long thread_info
        if (thread_info_flags & _TIF_NOTIFY_RESUME) {
                clear_thread_flag(TIF_NOTIFY_RESUME);
                tracehook_notify_resume(regs);
-               if (current->replacement_session_keyring)
-                       key_replace_session_keyring();
        }
 }
index b86e9ca79455d7cacb08a21b2e2199bc8879ee7c..2062aa88af41cc696d1f5892782245c4d73190fa 100644 (file)
@@ -123,7 +123,6 @@ void native_play_dead(void)
 int __cpu_disable(void)
 {
        unsigned int cpu = smp_processor_id();
-       struct task_struct *p;
        int ret;
 
        ret = mp_ops->cpu_disable(cpu);
@@ -153,11 +152,7 @@ int __cpu_disable(void)
        flush_cache_all();
        local_flush_tlb_all();
 
-       read_lock(&tasklist_lock);
-       for_each_process(p)
-               if (p->mm)
-                       cpumask_clear_cpu(cpu, mm_cpumask(p->mm));
-       read_unlock(&tasklist_lock);
+       clear_tasks_mm_cpumask(cpu);
 
        return 0;
 }
index 83bd051754e1fecddc0be891676bb9f57d62ad8f..e74ff137762661844783fe76a30986fe01308e9e 100644 (file)
@@ -41,7 +41,6 @@ config SPARC32
        def_bool !64BIT
        select GENERIC_ATOMIC64
        select CLZ_TAB
-       select ARCH_USES_GETTIMEOFFSET
 
 config SPARC64
        def_bool 64BIT
index cbb93e5141de0ff27d4d0197363fff3c23397e9b..61ebe7411ceb0af141b4bc9ceef1c1bcf36521fc 100644 (file)
 #define ASI_M_UNA01         0x01   /* Same here... */
 #define ASI_M_MXCC          0x02   /* Access to TI VIKING MXCC registers */
 #define ASI_M_FLUSH_PROBE   0x03   /* Reference MMU Flush/Probe; rw, ss */
-#ifndef CONFIG_SPARC_LEON
 #define ASI_M_MMUREGS       0x04   /* MMU Registers; rw, ss */
-#else
-#define ASI_M_MMUREGS       0x19
-#endif /* CONFIG_SPARC_LEON */
 #define ASI_M_TLBDIAG       0x05   /* MMU TLB only Diagnostics */
 #define ASI_M_DIAGS         0x06   /* Reference MMU Diagnostics */
 #define ASI_M_IODIAG        0x07   /* MMU I/O TLB only Diagnostics */
index 02a172fb193aaded11b3080f2e5337ee951e1ad9..a0e28ef025587a8384f3825f04a5ec66cd99ca75 100644 (file)
 /* All traps low-level code here must end with this macro. */
 #define RESTORE_ALL b ret_trap_entry; clr %l6;
 
+/* Support for run-time patching of single instructions.
+ * This is used to handle the differences in the ASI for
+ * MMUREGS for LEON and SUN.
+ *
+ * Sample:
+ * LEON_PI(lda [%g0] ASI_LEON_MMUREGS, %o0
+ * SUN_PI_(lda [%g0] ASI_M_MMUREGS, %o0
+ * PI == Patch Instruction
+ *
+ * For LEON we will use the first variant,
+ * and for all other we will use the SUN variant.
+ * The order is important.
+ */
+#define LEON_PI(...)                           \
+662:   __VA_ARGS__
+
+#define SUN_PI_(...)                           \
+       .section .leon_1insn_patch, "ax";       \
+       .word 662b;                             \
+       __VA_ARGS__;                            \
+       .previous
+
 #endif /* !(_SPARC_ASMMACRO_H) */
index 48a7c65731d2e0cf08ab2e974adcf922f2dddc58..8493fd3c7ba5a5ea39364948c26ecdd4feef41b8 100644 (file)
@@ -12,13 +12,18 @@ extern int dma_supported(struct device *dev, u64 mask);
 #define dma_alloc_noncoherent(d, s, h, f) dma_alloc_coherent(d, s, h, f)
 #define dma_free_noncoherent(d, s, v, h) dma_free_coherent(d, s, v, h)
 
-extern struct dma_map_ops *dma_ops, pci32_dma_ops;
+extern struct dma_map_ops *dma_ops;
+extern struct dma_map_ops *leon_dma_ops;
+extern struct dma_map_ops pci32_dma_ops;
+
 extern struct bus_type pci_bus_type;
 
 static inline struct dma_map_ops *get_dma_ops(struct device *dev)
 {
 #if defined(CONFIG_SPARC32) && defined(CONFIG_PCI)
-       if (dev->bus == &pci_bus_type)
+       if (sparc_cpu_model == sparc_leon)
+               return leon_dma_ops;
+       else if (dev->bus == &pci_bus_type)
                return &pci32_dma_ops;
 #endif
        return dma_ops;
index 07659124c1404da4ac11200c5269eb7131c0929f..3375c6293893654bfb0ab46a41ee084ccd3d8cc6 100644 (file)
@@ -8,8 +8,6 @@
 #ifndef LEON_H_INCLUDE
 #define LEON_H_INCLUDE
 
-#ifdef CONFIG_SPARC_LEON
-
 /* mmu register access, ASI_LEON_MMUREGS */
 #define LEON_CNR_CTRL          0x000
 #define LEON_CNR_CTXP          0x100
 
 #ifndef __ASSEMBLY__
 
-/* do a virtual address read without cache */
-static inline unsigned long leon_readnobuffer_reg(unsigned long paddr)
-{
-       unsigned long retval;
-       __asm__ __volatile__("lda [%1] %2, %0\n\t" :
-                            "=r"(retval) : "r"(paddr), "i"(ASI_LEON_NOCACHE));
-       return retval;
-}
-
 /* do a physical address bypass write, i.e. for 0x80000000 */
 static inline void leon_store_reg(unsigned long paddr, unsigned long value)
 {
@@ -87,47 +76,16 @@ static inline unsigned long leon_load_reg(unsigned long paddr)
        return retval;
 }
 
-static inline void leon_srmmu_disabletlb(void)
-{
-       unsigned int retval;
-       __asm__ __volatile__("lda [%%g0] %2, %0\n\t" : "=r"(retval) : "r"(0),
-                            "i"(ASI_LEON_MMUREGS));
-       retval |= LEON_CNR_CTRL_TLBDIS;
-       __asm__ __volatile__("sta %0, [%%g0] %2\n\t" : : "r"(retval), "r"(0),
-                            "i"(ASI_LEON_MMUREGS) : "memory");
-}
-
-static inline void leon_srmmu_enabletlb(void)
-{
-       unsigned int retval;
-       __asm__ __volatile__("lda [%%g0] %2, %0\n\t" : "=r"(retval) : "r"(0),
-                            "i"(ASI_LEON_MMUREGS));
-       retval = retval & ~LEON_CNR_CTRL_TLBDIS;
-       __asm__ __volatile__("sta %0, [%%g0] %2\n\t" : : "r"(retval), "r"(0),
-                            "i"(ASI_LEON_MMUREGS) : "memory");
-}
-
 /* macro access for leon_load_reg() and leon_store_reg() */
 #define LEON3_BYPASS_LOAD_PA(x)            (leon_load_reg((unsigned long)(x)))
 #define LEON3_BYPASS_STORE_PA(x, v) (leon_store_reg((unsigned long)(x), (unsigned long)(v)))
-#define LEON3_BYPASS_ANDIN_PA(x, v) LEON3_BYPASS_STORE_PA(x, LEON3_BYPASS_LOAD_PA(x) & v)
-#define LEON3_BYPASS_ORIN_PA(x, v)  LEON3_BYPASS_STORE_PA(x, LEON3_BYPASS_LOAD_PA(x) | v)
 #define LEON_BYPASS_LOAD_PA(x)      leon_load_reg((unsigned long)(x))
 #define LEON_BYPASS_STORE_PA(x, v)  leon_store_reg((unsigned long)(x), (unsigned long)(v))
-#define LEON_REGLOAD_PA(x)          leon_load_reg((unsigned long)(x)+LEON_PREGS)
-#define LEON_REGSTORE_PA(x, v)      leon_store_reg((unsigned long)(x)+LEON_PREGS, (unsigned long)(v))
-#define LEON_REGSTORE_OR_PA(x, v)   LEON_REGSTORE_PA(x, LEON_REGLOAD_PA(x) | (unsigned long)(v))
-#define LEON_REGSTORE_AND_PA(x, v)  LEON_REGSTORE_PA(x, LEON_REGLOAD_PA(x) & (unsigned long)(v))
-
-/* macro access for leon_readnobuffer_reg() */
-#define LEON_BYPASSCACHE_LOAD_VA(x) leon_readnobuffer_reg((unsigned long)(x))
 
 extern void leon_init(void);
 extern void leon_switch_mm(void);
 extern void leon_init_IRQ(void);
 
-extern unsigned long last_valid_pfn;
-
 static inline unsigned long sparc_leon3_get_dcachecfg(void)
 {
        unsigned int retval;
@@ -230,9 +188,6 @@ static inline int sparc_leon3_cpuid(void)
 #error cannot determine LEON_PAGE_SIZE_LEON
 #endif
 
-#define PAGE_MIN_SHIFT   (12)
-#define PAGE_MIN_SIZE    (1UL << PAGE_MIN_SHIFT)
-
 #define LEON3_XCCR_SETS_MASK  0x07000000UL
 #define LEON3_XCCR_SSIZE_MASK 0x00f00000UL
 
@@ -242,7 +197,7 @@ static inline int sparc_leon3_cpuid(void)
 #ifndef __ASSEMBLY__
 struct vm_area_struct;
 
-extern unsigned long srmmu_swprobe(unsigned long vaddr, unsigned long *paddr);
+extern unsigned long leon_swprobe(unsigned long vaddr, unsigned long *paddr);
 extern void leon_flush_icache_all(void);
 extern void leon_flush_dcache_all(void);
 extern void leon_flush_cache_all(void);
@@ -258,15 +213,7 @@ struct leon3_cacheregs {
        unsigned long dccr;     /* 0x0c - Data Cache Configuration Register */
 };
 
-/* struct that hold LEON2 cache configuration register
- * & configuration register
- */
-struct leon2_cacheregs {
-       unsigned long ccr, cfg;
-};
-
-#ifdef __KERNEL__
-
+#include <linux/irq.h>
 #include <linux/interrupt.h>
 
 struct device_node;
@@ -292,24 +239,15 @@ extern void leon_smp_done(void);
 extern void leon_boot_cpus(void);
 extern int leon_boot_one_cpu(int i, struct task_struct *);
 void leon_init_smp(void);
-extern void cpu_idle(void);
-extern void init_IRQ(void);
-extern void cpu_panic(void);
-extern int __leon_processor_id(void);
 void leon_enable_irq_cpu(unsigned int irq_nr, unsigned int cpu);
 extern irqreturn_t leon_percpu_timer_interrupt(int irq, void *unused);
 
-extern unsigned int real_irq_entry[];
 extern unsigned int smpleon_ipi[];
-extern unsigned int patchme_maybe_smp_msg[];
-extern unsigned int t_nmi[], linux_trap_ipi15_leon[];
-extern unsigned int linux_trap_ipi15_sun4m[];
+extern unsigned int linux_trap_ipi15_leon[];
 extern int leon_ipi_irq;
 
 #endif /* CONFIG_SMP */
 
-#endif /* __KERNEL__ */
-
 #endif /* __ASSEMBLY__ */
 
 /* macros used in leon_mm.c */
@@ -317,18 +255,4 @@ extern int leon_ipi_irq;
 #define _pfn_valid(pfn)         ((pfn < last_valid_pfn) && (pfn >= PFN(phys_base)))
 #define _SRMMU_PTE_PMASK_LEON 0xffffffff
 
-#else /* defined(CONFIG_SPARC_LEON) */
-
-/* nop definitions for !LEON case */
-#define leon_init() do {} while (0)
-#define leon_switch_mm() do {} while (0)
-#define leon_init_IRQ() do {} while (0)
-#define init_leon() do {} while (0)
-#define leon_smp_done() do {} while (0)
-#define leon_boot_cpus() do {} while (0)
-#define leon_boot_one_cpu(i, t) 1
-#define leon_init_smp() do {} while (0)
-
-#endif /* !defined(CONFIG_SPARC_LEON) */
-
 #endif
index e50f326e71bd1244c091da75519f2638b09ab8f9..f3034eddf4682569c257a2ce19941b013a98c218 100644 (file)
@@ -87,8 +87,6 @@ struct amba_prom_registers {
 #define LEON3_GPTIMER_CONFIG_NRTIMERS(c) ((c)->config & 0x7)
 #define LEON3_GPTIMER_CTRL_ISPENDING(r)  (((r)&LEON3_GPTIMER_CTRL_PENDING) ? 1 : 0)
 
-#ifdef CONFIG_SPARC_LEON
-
 #ifndef __ASSEMBLY__
 
 struct leon3_irqctrl_regs_map {
@@ -264,6 +262,4 @@ extern unsigned int sparc_leon_eirq;
 
 #define amba_device(x) (((x) >> 12) & 0xfff)
 
-#endif /* !defined(CONFIG_SPARC_LEON) */
-
 #endif
index cb828703a63ae853f702d781eac4ce69395ad4a9..79da17866fa8997ab3032f444b7598076dacaf36 100644 (file)
         restore %g0, %g0, %g0;
 
 #ifndef __ASSEMBLY__
+extern unsigned long last_valid_pfn;
 
 /* This makes sense. Honest it does - Anton */
 /* XXX Yes but it's ugly as sin.  FIXME. -KMW */
@@ -148,67 +149,13 @@ extern void *srmmu_nocache_pool;
 #define __nocache_fix(VADDR) __va(__nocache_pa(VADDR))
 
 /* Accessing the MMU control register. */
-static inline unsigned int srmmu_get_mmureg(void)
-{
-        unsigned int retval;
-       __asm__ __volatile__("lda [%%g0] %1, %0\n\t" :
-                            "=r" (retval) :
-                            "i" (ASI_M_MMUREGS));
-       return retval;
-}
-
-static inline void srmmu_set_mmureg(unsigned long regval)
-{
-       __asm__ __volatile__("sta %0, [%%g0] %1\n\t" : :
-                            "r" (regval), "i" (ASI_M_MMUREGS) : "memory");
-
-}
-
-static inline void srmmu_set_ctable_ptr(unsigned long paddr)
-{
-       paddr = ((paddr >> 4) & SRMMU_CTX_PMASK);
-       __asm__ __volatile__("sta %0, [%1] %2\n\t" : :
-                            "r" (paddr), "r" (SRMMU_CTXTBL_PTR),
-                            "i" (ASI_M_MMUREGS) :
-                            "memory");
-}
-
-static inline void srmmu_set_context(int context)
-{
-       __asm__ __volatile__("sta %0, [%1] %2\n\t" : :
-                            "r" (context), "r" (SRMMU_CTX_REG),
-                            "i" (ASI_M_MMUREGS) : "memory");
-}
-
-static inline int srmmu_get_context(void)
-{
-       register int retval;
-       __asm__ __volatile__("lda [%1] %2, %0\n\t" :
-                            "=r" (retval) :
-                            "r" (SRMMU_CTX_REG),
-                            "i" (ASI_M_MMUREGS));
-       return retval;
-}
-
-static inline unsigned int srmmu_get_fstatus(void)
-{
-       unsigned int retval;
-
-       __asm__ __volatile__("lda [%1] %2, %0\n\t" :
-                            "=r" (retval) :
-                            "r" (SRMMU_FAULT_STATUS), "i" (ASI_M_MMUREGS));
-       return retval;
-}
-
-static inline unsigned int srmmu_get_faddr(void)
-{
-       unsigned int retval;
-
-       __asm__ __volatile__("lda [%1] %2, %0\n\t" :
-                            "=r" (retval) :
-                            "r" (SRMMU_FAULT_ADDR), "i" (ASI_M_MMUREGS));
-       return retval;
-}
+unsigned int srmmu_get_mmureg(void);
+void srmmu_set_mmureg(unsigned long regval);
+void srmmu_set_ctable_ptr(unsigned long paddr);
+void srmmu_set_context(int context);
+int srmmu_get_context(void);
+unsigned int srmmu_get_fstatus(void);
+unsigned int srmmu_get_faddr(void);
 
 /* This is guaranteed on all SRMMU's. */
 static inline void srmmu_flush_whole_tlb(void)
@@ -219,23 +166,6 @@ static inline void srmmu_flush_whole_tlb(void)
 
 }
 
-/* These flush types are not available on all chips... */
-#ifndef CONFIG_SPARC_LEON
-static inline unsigned long srmmu_hwprobe(unsigned long vaddr)
-{
-       unsigned long retval;
-
-       vaddr &= PAGE_MASK;
-       __asm__ __volatile__("lda [%1] %2, %0\n\t" :
-                            "=r" (retval) :
-                            "r" (vaddr | 0x400), "i" (ASI_M_FLUSH_PROBE));
-
-       return retval;
-}
-#else
-#define srmmu_hwprobe(addr) srmmu_swprobe(addr, 0)
-#endif
-
 static inline int
 srmmu_get_pte (unsigned long addr)
 {
index 3070f25ae90a3e235eaaf2373949226ea83acfb6..156220ed99eb7dfbfe8696ea9f04da84fc00ff5f 100644 (file)
@@ -9,8 +9,6 @@
 
 #if defined(__sparc__) && defined(__arch64__)
 /* sparc 64 bit */
-typedef unsigned int           __kernel_nlink_t;
-#define __kernel_nlink_t __kernel_nlink_t
 
 typedef unsigned short                __kernel_old_uid_t;
 typedef unsigned short         __kernel_old_gid_t;
@@ -38,9 +36,6 @@ typedef unsigned short         __kernel_gid_t;
 typedef unsigned short         __kernel_mode_t;
 #define __kernel_mode_t __kernel_mode_t
 
-typedef short                  __kernel_nlink_t;
-#define __kernel_nlink_t __kernel_nlink_t
-
 typedef long                   __kernel_daddr_t;
 #define __kernel_daddr_t __kernel_daddr_t
 
index b8c0e5f0a66bbf1e4014bec94292320835914382..cee7ed9c927d9ac433db875646add31e212dc32b 100644 (file)
 #define PSR_VERS    0x0f000000         /* cpu-version field          */
 #define PSR_IMPL    0xf0000000         /* cpu-implementation field   */
 
+#define PSR_VERS_SHIFT         24
+#define PSR_IMPL_SHIFT         28
+#define PSR_VERS_SHIFTED_MASK  0xf
+#define PSR_IMPL_SHIFTED_MASK  0xf
+
+#define PSR_IMPL_TI            0x4
+#define PSR_IMPL_LEON          0xf
+
 #ifdef __KERNEL__
 
 #ifndef __ASSEMBLY__
index 0b0553bbd8a0dd26feb97be864a29997b754643e..f300d1a9b2b6f8f3650ef3b6d712f493e4a6b8b1 100644 (file)
@@ -7,4 +7,7 @@
 /* sparc entry point */
 extern char _start[];
 
+extern char __leon_1insn_patch[];
+extern char __leon_1insn_patch_end[];
+
 #endif
index 5af664932452190e66543500d21c9dcd6ba48763..e6cd224506a9355168ebea0c5ed0be8c3b625120 100644 (file)
@@ -131,8 +131,7 @@ register struct thread_info *current_thread_info_reg asm("g6");
 #define _TIF_POLLING_NRFLAG    (1<<TIF_POLLING_NRFLAG)
 
 #define _TIF_DO_NOTIFY_RESUME_MASK     (_TIF_NOTIFY_RESUME | \
-                                        _TIF_SIGPENDING | \
-                                        _TIF_RESTORE_SIGMASK)
+                                        _TIF_SIGPENDING)
 
 #endif /* __KERNEL__ */
 
index 7f0981b094517aace2b461e2f6d7db9aa6d0fbfa..cfa8c38fb9c8511d51cfbe17e3d8792584d8b241 100644 (file)
@@ -238,7 +238,23 @@ static inline void set_restore_sigmask(void)
 {
        struct thread_info *ti = current_thread_info();
        ti->status |= TS_RESTORE_SIGMASK;
-       set_bit(TIF_SIGPENDING, &ti->flags);
+       WARN_ON(!test_bit(TIF_SIGPENDING, &ti->flags));
+}
+static inline void clear_restore_sigmask(void)
+{
+       current_thread_info()->status &= ~TS_RESTORE_SIGMASK;
+}
+static inline bool test_restore_sigmask(void)
+{
+       return current_thread_info()->status & TS_RESTORE_SIGMASK;
+}
+static inline bool test_and_clear_restore_sigmask(void)
+{
+       struct thread_info *ti = current_thread_info();
+       if (!(ti->status & TS_RESTORE_SIGMASK))
+               return false;
+       ti->status &= ~TS_RESTORE_SIGMASK;
+       return true;
 }
 #endif /* !__ASSEMBLY__ */
 
index 72308f9b0096e45b6da24fef022a261989d5eacf..6cf591b7e1c67e2b82c4e28bf177197ce0f602a7 100644 (file)
@@ -51,8 +51,8 @@ obj-y                   += of_device_common.o
 obj-y                   += of_device_$(BITS).o
 obj-$(CONFIG_SPARC64)   += prom_irqtrans.o
 
-obj-$(CONFIG_SPARC_LEON)+= leon_kernel.o
-obj-$(CONFIG_SPARC_LEON)+= leon_pmc.o
+obj-$(CONFIG_SPARC32)   += leon_kernel.o
+obj-$(CONFIG_SPARC32)   += leon_pmc.o
 
 obj-$(CONFIG_SPARC64)   += reboot.o
 obj-$(CONFIG_SPARC64)   += sysfs.o
index 2d1819641769fca63bad5c086bccd7006a8444ef..a6c94a2bf9d4b1150e41b63d3e71a81138fc1f63 100644 (file)
@@ -121,7 +121,7 @@ static const struct manufacturer_info __initconst manufacturer_info[] = {
                FPU(-1, NULL)
        }
 },{
-       4,
+       PSR_IMPL_TI,
        .cpu_info = {
                CPU(0, "Texas Instruments, Inc. - SuperSparc-(II)"),
                /* SparcClassic  --  borned STP1010TAB-50*/
@@ -191,7 +191,7 @@ static const struct manufacturer_info __initconst manufacturer_info[] = {
                FPU(-1, NULL)
        }
 },{
-       0xF,            /* Aeroflex Gaisler */
+       PSR_IMPL_LEON,          /* Aeroflex Gaisler */
        .cpu_info = {
                CPU(3, "LEON"),
                CPU(-1, NULL)
@@ -440,16 +440,16 @@ static int __init cpu_type_probe(void)
        int psr_impl, psr_vers, fpu_vers;
        int psr;
 
-       psr_impl = ((get_psr() >> 28) & 0xf);
-       psr_vers = ((get_psr() >> 24) & 0xf);
+       psr_impl = ((get_psr() >> PSR_IMPL_SHIFT) & PSR_IMPL_SHIFTED_MASK);
+       psr_vers = ((get_psr() >> PSR_VERS_SHIFT) & PSR_VERS_SHIFTED_MASK);
 
        psr = get_psr();
        put_psr(psr | PSR_EF);
-#ifdef CONFIG_SPARC_LEON
-       fpu_vers = get_psr() & PSR_EF ? ((get_fsr() >> 17) & 0x7) : 7;
-#else
-       fpu_vers = ((get_fsr() >> 17) & 0x7);
-#endif
+
+       if (psr_impl == PSR_IMPL_LEON)
+               fpu_vers = get_psr() & PSR_EF ? ((get_fsr() >> 17) & 0x7) : 7;
+       else
+               fpu_vers = ((get_fsr() >> 17) & 0x7);
 
        put_psr(psr);
 
index 2dbe1806e5300ab8bf49a1c22dda5850c263f75f..dcaa1cf0de40c790b27b32fbbdfe997cfdee072f 100644 (file)
@@ -393,7 +393,6 @@ linux_trap_ipi15_sun4d:
        /* FIXME */
 1:     b,a     1b
 
-#ifdef CONFIG_SPARC_LEON
        .globl  smpleon_ipi
        .extern leon_ipi_interrupt
        /* SMP per-cpu IPI interrupts are handled specially. */
@@ -424,8 +423,6 @@ linux_trap_ipi15_leon:
        b       ret_trap_lockless_ipi
         clr    %l6
 
-#endif /* CONFIG_SPARC_LEON */
-
 #endif /* CONFIG_SMP */
 
        /* This routine handles illegal instructions and privileged
@@ -770,8 +767,11 @@ srmmu_fault:
        mov     0x400, %l5
        mov     0x300, %l4
 
-       lda     [%l5] ASI_M_MMUREGS, %l6        ! read sfar first
-       lda     [%l4] ASI_M_MMUREGS, %l5        ! read sfsr last
+LEON_PI(lda    [%l5] ASI_LEON_MMUREGS, %l6)    ! read sfar first
+SUN_PI_(lda    [%l5] ASI_M_MMUREGS, %l6)       ! read sfar first
+
+LEON_PI(lda    [%l4] ASI_LEON_MMUREGS, %l5)    ! read sfsr last
+SUN_PI_(lda    [%l4] ASI_M_MMUREGS, %l5)       ! read sfsr last
 
        andn    %l6, 0xfff, %l6
        srl     %l5, 6, %l5                     ! and encode all info into l7
index 84b5f0d2afde51b344485c96ee2fbef57e2ae684..e3e80d65e39af1d9167a7ca3156ee2f797112a6c 100644 (file)
@@ -234,7 +234,8 @@ tsetup_srmmu_stackchk:
 
        cmp     %glob_tmp, %sp
        bleu,a  1f
-        lda    [%g0] ASI_M_MMUREGS, %glob_tmp          ! read MMU control
+LEON_PI( lda   [%g0] ASI_LEON_MMUREGS, %glob_tmp)      ! read MMU control
+SUN_PI_( lda   [%g0] ASI_M_MMUREGS, %glob_tmp)         ! read MMU control
 
 trap_setup_user_stack_is_bolixed:
        /* From user/kernel into invalid window w/bad user
@@ -249,18 +250,25 @@ trap_setup_user_stack_is_bolixed:
 1:
        /* Clear the fault status and turn on the no_fault bit. */
        or      %glob_tmp, 0x2, %glob_tmp               ! or in no_fault bit
-       sta     %glob_tmp, [%g0] ASI_M_MMUREGS          ! set it
+LEON_PI(sta    %glob_tmp, [%g0] ASI_LEON_MMUREGS)              ! set it
+SUN_PI_(sta    %glob_tmp, [%g0] ASI_M_MMUREGS)         ! set it
 
        /* Dump the registers and cross fingers. */
        STORE_WINDOW(sp)
 
        /* Clear the no_fault bit and check the status. */
        andn    %glob_tmp, 0x2, %glob_tmp
-       sta     %glob_tmp, [%g0] ASI_M_MMUREGS
+LEON_PI(sta    %glob_tmp, [%g0] ASI_LEON_MMUREGS)
+SUN_PI_(sta    %glob_tmp, [%g0] ASI_M_MMUREGS)
+
        mov     AC_M_SFAR, %glob_tmp
-       lda     [%glob_tmp] ASI_M_MMUREGS, %g0
+LEON_PI(lda    [%glob_tmp] ASI_LEON_MMUREGS, %g0)
+SUN_PI_(lda    [%glob_tmp] ASI_M_MMUREGS, %g0)
+
        mov     AC_M_SFSR, %glob_tmp
-       lda     [%glob_tmp] ASI_M_MMUREGS, %glob_tmp    ! save away status of winstore
+LEON_PI(lda    [%glob_tmp] ASI_LEON_MMUREGS, %glob_tmp)! save away status of winstore
+SUN_PI_(lda    [%glob_tmp] ASI_M_MMUREGS, %glob_tmp)   ! save away status of winstore
+
        andcc   %glob_tmp, 0x2, %g0                     ! did we fault?
        bne     trap_setup_user_stack_is_bolixed        ! failure
         nop
index a0f5c20e4b9cd5f286432d8bd4dc678cbc62e0bd..afeb1d7703032a76c5b29b38ec98cd068f74cb55 100644 (file)
  * the cpu-type
  */
        .align 4
-cputyp:
-        .word   1
-
-       .align 4
        .globl cputypval
 cputypval:
        .asciz "sun4m"
@@ -46,8 +42,8 @@ cputypvar:
 
        .align 4
 
-sun4c_notsup:
-       .asciz  "Sparc-Linux sun4/sun4c support does no longer exist.\n\n"
+notsup:
+       .asciz  "Sparc-Linux sun4/sun4c or MMU-less not supported\n\n"
        .align 4
 
 sun4e_notsup:
@@ -123,7 +119,7 @@ current_pc:
                tst     %o0
                be      no_sun4u_here
                 mov    %g4, %o7                /* Previous %o7. */
-       
+
                mov     %o0, %l0                ! stash away romvec
                mov     %o0, %g7                ! put it here too
                mov     %o1, %l1                ! stash away debug_vec too
@@ -132,7 +128,7 @@ current_pc:
                set     current_pc, %g5
                cmp     %g3, %g5
                be      already_mapped
-                nop 
+                nop
 
                /* %l6 will hold the offset we have to subtract
                 * from absolute symbols in order to access areas
@@ -192,9 +188,9 @@ copy_prom_done:
                bne     not_a_sun4
                 nop
 
-halt_sun4_or_sun4c:
+halt_notsup:
                ld      [%g7 + 0x68], %o1
-               set     sun4c_notsup, %o0
+               set     notsup, %o0
                sub     %o0, %l6, %o0
                call    %o1
                 nop
@@ -202,18 +198,31 @@ halt_sun4_or_sun4c:
                 nop
 
 not_a_sun4:
+               /* It looks like this is a machine we support.
+                * Now find out what MMU we are dealing with
+                * LEON - identified by the psr.impl field
+                * Viking - identified by the psr.impl field
+                * In all other cases a sun4m srmmu.
+                * We check that the MMU is enabled in all cases.
+                */
+
+               /* Check if this is a LEON CPU */
+               rd      %psr, %g3
+               srl     %g3, PSR_IMPL_SHIFT, %g3
+               and     %g3, PSR_IMPL_SHIFTED_MASK, %g3
+               cmp     %g3, PSR_IMPL_LEON
+               be      leon_remap              /* It is a LEON - jump */
+                nop
+
+               /* Sanity-check, is MMU enabled */
                lda     [%g0] ASI_M_MMUREGS, %g1
                andcc   %g1, 1, %g0
-               be      halt_sun4_or_sun4c
+               be      halt_notsup
                 nop
 
-srmmu_remap:
-               /* First, check for a viking (TI) module. */
-               set     0x40000000, %g2
-               rd      %psr, %g3
-               and     %g2, %g3, %g3
-               subcc   %g3, 0x0, %g0
-               bz      srmmu_nviking
+               /* Check for a viking (TI) module. */
+               cmp     %g3, PSR_IMPL_TI
+               bne     srmmu_not_viking
                 nop
 
                /* Figure out what kind of viking we are on.
@@ -228,14 +237,14 @@ srmmu_remap:
                lda     [%g0] ASI_M_MMUREGS, %g3        ! peek in the control reg
                and     %g2, %g3, %g3
                subcc   %g3, 0x0, %g0
-               bnz     srmmu_nviking                   ! is in mbus mode
+               bnz     srmmu_not_viking                        ! is in mbus mode
                 nop
-               
+
                rd      %psr, %g3                       ! DO NOT TOUCH %g3
                andn    %g3, PSR_ET, %g2
                wr      %g2, 0x0, %psr
                WRITE_PAUSE
-               
+
                /* Get context table pointer, then convert to
                 * a physical address, which is 36 bits.
                 */
@@ -258,12 +267,12 @@ srmmu_remap:
                lda     [%g4] ASI_M_BYPASS, %o1         ! This is a level 1 ptr
                srl     %o1, 0x4, %o1                   ! Clear low 4 bits
                sll     %o1, 0x8, %o1                   ! Make physical
-               
+
                /* Ok, pull in the PTD. */
                lda     [%o1] ASI_M_BYPASS, %o2         ! This is the 0x0 16MB pgd
 
                /* Calculate to KERNBASE entry. */
-               add     %o1, KERNBASE >> (SRMMU_PGDIR_SHIFT - 2), %o3           
+               add     %o1, KERNBASE >> (SRMMU_PGDIR_SHIFT - 2), %o3
 
                /* Poke the entry into the calculated address. */
                sta     %o2, [%o3] ASI_M_BYPASS
@@ -293,12 +302,12 @@ srmmu_remap:
                b       go_to_highmem
                 nop
 
+srmmu_not_viking:
                /* This works on viking's in Mbus mode and all
                 * other MBUS modules.  It is virtually the same as
                 * the above madness sans turning traps off and flipping
                 * the AC bit.
                 */
-srmmu_nviking:
                set     AC_M_CTPR, %g1
                lda     [%g1] ASI_M_MMUREGS, %g1        ! get ctx table ptr
                sll     %g1, 0x4, %g1                   ! make physical addr
@@ -313,6 +322,29 @@ srmmu_nviking:
                 nop                                    ! wheee....
 
 
+leon_remap:
+               /* Sanity-check, is MMU enabled */
+               lda     [%g0] ASI_LEON_MMUREGS, %g1
+               andcc   %g1, 1, %g0
+               be      halt_notsup
+                nop
+
+               /* Same code as in the srmmu_not_viking case,
+                * with the LEON ASI for mmuregs
+                */
+               set     AC_M_CTPR, %g1
+               lda     [%g1] ASI_LEON_MMUREGS, %g1     ! get ctx table ptr
+               sll     %g1, 0x4, %g1                   ! make physical addr
+               lda     [%g1] ASI_M_BYPASS, %g1         ! ptr to level 1 pg_table
+               srl     %g1, 0x4, %g1
+               sll     %g1, 0x8, %g1                   ! make phys addr for l1 tbl
+
+               lda     [%g1] ASI_M_BYPASS, %g2         ! get level1 entry for 0x0
+               add     %g1, KERNBASE >> (SRMMU_PGDIR_SHIFT - 2), %g3
+               sta     %g2, [%g3] ASI_M_BYPASS         ! place at KERNBASE entry
+               b       go_to_highmem
+                nop                                    ! wheee....
+
 /* Now do a non-relative jump so that PC is in high-memory */
 go_to_highmem:
                set     execute_in_high_mem, %g1
@@ -336,8 +368,9 @@ execute_in_high_mem:
                sethi   %hi(linux_dbvec), %g1
                st      %o1, [%g1 + %lo(linux_dbvec)]
 
-/* Get the machine type via the mysterious romvec node operations. */
-
+               /* Get the machine type via the romvec
+                * getprops node operation
+                */
                add     %g7, 0x1c, %l1
                ld      [%l1], %l0
                ld      [%l0], %l0
@@ -356,9 +389,42 @@ execute_in_high_mem:
                                                ! to a buf where above string
                                                ! will get stored by the prom.
 
-#ifdef CONFIG_SPARC_LEON
-               /* no cpu-type check is needed, it is a SPARC-LEON */
 
+               /* Check value of "compatible" property.
+                * "value" => "model"
+                * leon => sparc_leon
+                * sun4m => sun4m
+                * sun4s => sun4m
+                * sun4d => sun4d
+                * sun4e => "no_sun4e_here"
+                * '*'   => "no_sun4u_here"
+                * Check single letters only
+                */
+
+               set     cputypval, %o2
+               /* If cputypval[0] == 'l' (lower case letter L) this is leon */
+               ldub    [%o2], %l1
+               cmp     %l1, 'l'
+               be      leon_init
+                nop
+
+               /* Check cputypval[4] to find the sun model */
+               ldub    [%o2 + 0x4], %l1
+
+               cmp     %l1, 'm'
+               be      sun4m_init
+                cmp    %l1, 's'
+               be      sun4m_init
+                cmp    %l1, 'd'
+               be      sun4d_init
+                cmp    %l1, 'e'
+               be      no_sun4e_here           ! Could be a sun4e.
+                nop
+               b       no_sun4u_here           ! AIEEE, a V9 sun4u... Get our BIG BROTHER kernel :))
+                nop
+
+leon_init:
+               /* LEON CPU - set boot_cpu_id */
                sethi   %hi(boot_cpu_id), %g2   ! boot-cpu index
 
 #ifdef CONFIG_SMP
@@ -376,26 +442,6 @@ execute_in_high_mem:
 
                ba continue_boot
                 nop
-#endif
-
-/* Check to cputype. We may be booted on a sun4u (64 bit box),
- * and sun4d needs special treatment.
- */
-
-               set     cputypval, %o2
-               ldub    [%o2 + 0x4], %l1
-
-               cmp     %l1, 'm'
-               be      sun4m_init
-                cmp    %l1, 's'
-               be      sun4m_init
-                cmp    %l1, 'd'
-               be      sun4d_init
-                cmp    %l1, 'e'
-               be      no_sun4e_here           ! Could be a sun4e.
-                nop
-               b       no_sun4u_here           ! AIEEE, a V9 sun4u... Get our BIG BROTHER kernel :))
-                nop
 
 /* CPUID in bootbus can be found at PA 0xff0140000 */
 #define SUN4D_BOOTBUS_CPUID     0xf0140000
@@ -431,9 +477,9 @@ sun4m_init:
 /* This sucks, apparently this makes Vikings call prom panic, will fix later */
 2:
                rd      %psr, %o1
-               srl     %o1, 28, %o1            ! Get a type of the CPU
+               srl     %o1, PSR_IMPL_SHIFT, %o1        ! Get a type of the CPU
 
-               subcc   %o1, 4, %g0             ! TI: Viking or MicroSPARC
+               subcc   %o1, PSR_IMPL_TI, %g0           ! TI: Viking or MicroSPARC
                be      continue_boot
                 nop
 
@@ -459,10 +505,6 @@ continue_boot:
 /* Aieee, now set PC and nPC, enable traps, give ourselves a stack and it's
  * show-time!
  */
-
-               sethi   %hi(cputyp), %o0
-               st      %g4, [%o0 + %lo(cputyp)]
-
                /* Turn on Supervisor, EnableFloating, and all the PIL bits.
                 * Also puts us in register window zero with traps off.
                 */
@@ -480,7 +522,7 @@ continue_boot:
                set     __bss_start , %o0       ! First address of BSS
                set     _end , %o1              ! Last address of BSS
                add     %o0, 0x1, %o0
-1:     
+1:
                stb     %g0, [%o0]
                subcc   %o0, %o1, %g0
                bl      1b
@@ -546,7 +588,7 @@ continue_boot:
                set     dest, %g2; \
                ld      [%g5], %g4; \
                st      %g4, [%g2];
-       
+
                /* Patch for window spills... */
                PATCH_INSN(spnwin_patch1_7win, spnwin_patch1)
                PATCH_INSN(spnwin_patch2_7win, spnwin_patch2)
@@ -597,7 +639,7 @@ continue_boot:
                st      %g4, [%g5 + 0x18]
                st      %g4, [%g5 + 0x1c]
 
-2:             
+2:
                sethi   %hi(nwindows), %g4
                st      %g3, [%g4 + %lo(nwindows)]      ! store final value
                sub     %g3, 0x1, %g3
@@ -617,18 +659,12 @@ continue_boot:
                wr      %g3, PSR_ET, %psr
                WRITE_PAUSE
 
-               /* First we call prom_init() to set up PROMLIB, then
-                * off to start_kernel().
-                */
-
+               /* Call sparc32_start_kernel(struct linux_romvec *rp) */
                sethi   %hi(prom_vector_p), %g5
                ld      [%g5 + %lo(prom_vector_p)], %o0
-               call    prom_init
+               call    sparc32_start_kernel
                 nop
 
-               call    start_kernel
-                nop
-       
                /* We should not get here. */
                call    halt_me
                 nop
@@ -659,7 +695,7 @@ sun4u_5:
                .asciz "write"
                .align  4
 sun4u_6:
-               .asciz  "\n\rOn sun4u you have to use UltraLinux (64bit) kernel\n\rand not a 32bit sun4[cdem] version\n\r\n\r"
+               .asciz  "\n\rOn sun4u you have to use sparc64 kernel\n\rand not a sparc32 version\n\r\n\r"
 sun4u_6e:
                .align  4
 sun4u_7:
index a2846f5e32d8042246e1764132c4ae9ec32bb857..0f094db918c7f8b711aeaab6ba01ac46847332cd 100644 (file)
@@ -55,17 +55,13 @@ const struct sparc32_dma_ops *sparc32_dma_ops;
 /* This function must make sure that caches and memory are coherent after DMA
  * On LEON systems without cache snooping it flushes the entire D-CACHE.
  */
-#ifndef CONFIG_SPARC_LEON
 static inline void dma_make_coherent(unsigned long pa, unsigned long len)
 {
+       if (sparc_cpu_model == sparc_leon) {
+               if (!sparc_leon3_snooping_enabled())
+                       leon_flush_dcache_all();
+       }
 }
-#else
-static inline void dma_make_coherent(unsigned long pa, unsigned long len)
-{
-       if (!sparc_leon3_snooping_enabled())
-               leon_flush_dcache_all();
-}
-#endif
 
 static void __iomem *_sparc_ioremap(struct resource *res, u32 bus, u32 pa, int sz);
 static void __iomem *_sparc_alloc_io(unsigned int busno, unsigned long phys,
@@ -427,9 +423,6 @@ arch_initcall(sparc_register_ioport);
 #endif /* CONFIG_SBUS */
 
 
-/* LEON reuses PCI DMA ops */
-#if defined(CONFIG_PCI) || defined(CONFIG_SPARC_LEON)
-
 /* Allocate and map kernel buffer using consistent mode DMA for a device.
  * hwdev should be valid struct pci_dev pointer for PCI devices.
  */
@@ -657,14 +650,11 @@ struct dma_map_ops pci32_dma_ops = {
 };
 EXPORT_SYMBOL(pci32_dma_ops);
 
-#endif /* CONFIG_PCI || CONFIG_SPARC_LEON */
+/* leon re-uses pci32_dma_ops */
+struct dma_map_ops *leon_dma_ops = &pci32_dma_ops;
+EXPORT_SYMBOL(leon_dma_ops);
 
-#ifdef CONFIG_SPARC_LEON
-struct dma_map_ops *dma_ops = &pci32_dma_ops;
-#elif defined(CONFIG_SBUS)
 struct dma_map_ops *dma_ops = &sbus_dma_ops;
-#endif
-
 EXPORT_SYMBOL(dma_ops);
 
 
index ae04914f7774be143a95964d139f1f2e1003da7d..c145f6fd123b88814c9d650e781dd391ea0e73f7 100644 (file)
@@ -241,9 +241,6 @@ int sparc_floppy_request_irq(unsigned int irq, irq_handler_t irq_handler)
        unsigned int cpu_irq;
        int err;
 
-#if defined CONFIG_SMP && !defined CONFIG_SPARC_LEON
-       struct tt_entry *trap_table;
-#endif
 
        err = request_irq(irq, irq_handler, 0, "floppy", NULL);
        if (err)
@@ -264,13 +261,18 @@ int sparc_floppy_request_irq(unsigned int irq, irq_handler_t irq_handler)
        table[SP_TRAP_IRQ1+(cpu_irq-1)].inst_four = SPARC_NOP;
 
        INSTANTIATE(sparc_ttable)
-#if defined CONFIG_SMP && !defined CONFIG_SPARC_LEON
-       trap_table = &trapbase_cpu1;
-       INSTANTIATE(trap_table)
-       trap_table = &trapbase_cpu2;
-       INSTANTIATE(trap_table)
-       trap_table = &trapbase_cpu3;
-       INSTANTIATE(trap_table)
+
+#if defined CONFIG_SMP
+       if (sparc_cpu_model != sparc_leon) {
+               struct tt_entry *trap_table;
+
+               trap_table = &trapbase_cpu1;
+               INSTANTIATE(trap_table)
+               trap_table = &trapbase_cpu2;
+               INSTANTIATE(trap_table)
+               trap_table = &trapbase_cpu3;
+               INSTANTIATE(trap_table)
+       }
 #endif
 #undef INSTANTIATE
        /*
index a86372d345870202a8bb27e9d72c18b8f7574cb7..291bb5de9ce0963184518a9a00872370821075d1 100644 (file)
@@ -26,6 +26,9 @@ static inline unsigned long kimage_addr_to_ra(const char *p)
 #endif
 
 #ifdef CONFIG_SPARC32
+/* setup_32.c */
+void sparc32_start_kernel(struct linux_romvec *rp);
+
 /* cpu.c */
 extern void cpu_probe(void);
 
index 77c1b916e4dd35c8b04422a1e7102583b1b8a45c..e34e2c40c0609eac5b41b7220f01304cd2373e20 100644 (file)
@@ -23,6 +23,7 @@
 #include <asm/smp.h>
 #include <asm/setup.h>
 
+#include "kernel.h"
 #include "prom.h"
 #include "irq.h"
 
index 519ca923f59f491468d6252423effac83cc9f9d2..4e174321097d7a4af24eb0f5cd3bb4b1d5b37e53 100644 (file)
@@ -7,6 +7,7 @@
 #include <linux/pm.h>
 
 #include <asm/leon_amba.h>
+#include <asm/cpu_type.h>
 #include <asm/leon.h>
 
 /* List of Systems that need fixup instructions around power-down instruction */
@@ -65,13 +66,15 @@ void pmc_leon_idle(void)
 /* Install LEON Power Down function */
 static int __init leon_pmc_install(void)
 {
-       /* Assign power management IDLE handler */
-       if (pmc_leon_need_fixup())
-               pm_idle = pmc_leon_idle_fixup;
-       else
-               pm_idle = pmc_leon_idle;
+       if (sparc_cpu_model == sparc_leon) {
+               /* Assign power management IDLE handler */
+               if (pmc_leon_need_fixup())
+                       pm_idle = pmc_leon_idle_fixup;
+               else
+                       pm_idle = pmc_leon_idle;
 
-       printk(KERN_INFO "leon: power management initialized\n");
+               printk(KERN_INFO "leon: power management initialized\n");
+       }
 
        return 0;
 }
index a469090faf9f12f83251b2432614b0724d9e67e0..0f3fb6d9c8efc3e8cd0f12e48d3ff26d87a6699f 100644 (file)
 
 #include "kernel.h"
 
-#ifdef CONFIG_SPARC_LEON
-
 #include "irq.h"
 
 extern ctxd_t *srmmu_ctx_table_phys;
 static int smp_processors_ready;
 extern volatile unsigned long cpu_callin_map[NR_CPUS];
 extern cpumask_t smp_commenced_mask;
-void __init leon_configure_cache_smp(void);
+void __cpuinit leon_configure_cache_smp(void);
 static void leon_ipi_init(void);
 
 /* IRQ number of LEON IPIs */
@@ -123,7 +121,7 @@ void __cpuinit leon_callin(void)
 
 extern struct linux_prom_registers smp_penguin_ctable;
 
-void __init leon_configure_cache_smp(void)
+void __cpuinit leon_configure_cache_smp(void)
 {
        unsigned long cfg = sparc_leon3_get_dcachecfg();
        int me = smp_processor_id();
@@ -507,5 +505,3 @@ void __init leon_init_smp(void)
 
        sparc32_ipi_ops = &leon_ipi_ops;
 }
-
-#endif /* CONFIG_SPARC_LEON */
index fe6787cc62fc9188cbf792cd53ba65f998eeccb3..cb36e82dcd5dd789221852c366934c93f26d7ba3 100644 (file)
@@ -65,50 +65,25 @@ extern void fpsave(unsigned long *, unsigned long *, void *, unsigned long *);
 struct task_struct *last_task_used_math = NULL;
 struct thread_info *current_set[NR_CPUS];
 
-#ifndef CONFIG_SMP
-
 /*
  * the idle loop on a Sparc... ;)
  */
 void cpu_idle(void)
 {
-       /* endless idle loop with no priority at all */
-       for (;;) {
-               if (pm_idle) {
-                       while (!need_resched())
-                               (*pm_idle)();
-               } else {
-                       while (!need_resched())
-                               cpu_relax();
-               }
-               schedule_preempt_disabled();
-       }
-}
-
-#else
+       set_thread_flag(TIF_POLLING_NRFLAG);
 
-/* This is being executed in task 0 'user space'. */
-void cpu_idle(void)
-{
-        set_thread_flag(TIF_POLLING_NRFLAG);
        /* endless idle loop with no priority at all */
-       while(1) {
-#ifdef CONFIG_SPARC_LEON
-               if (pm_idle) {
-                       while (!need_resched())
+       for (;;) {
+               while (!need_resched()) {
+                       if (pm_idle)
                                (*pm_idle)();
-               } else
-#endif
-               {
-                       while (!need_resched())
+                       else
                                cpu_relax();
                }
                schedule_preempt_disabled();
        }
 }
 
-#endif
-
 /* XXX cli/sti -> local_irq_xxx here, check this works once SMP is fixed. */
 void machine_halt(void)
 {
index 741df916c124b10b12751da38bf7d3dac335c5dd..1303021748c8cc3c5c2ca109e2310e149dca0471 100644 (file)
@@ -23,7 +23,6 @@
 #include <linux/of_pdt.h>
 #include <asm/prom.h>
 #include <asm/oplib.h>
-#include <asm/leon.h>
 
 #include "prom.h"
 
index 7abc24e2bf1a9f5bed13d56cd0bc5e746a5e0d95..6c34de0c2abd40eefc679e35d548ff4c2a132586 100644 (file)
@@ -231,11 +231,14 @@ srmmu_rett_stackchk:
        cmp     %g1, %fp
        bleu    ret_trap_user_stack_is_bolixed
         mov    AC_M_SFSR, %g1
-       lda     [%g1] ASI_M_MMUREGS, %g0
+LEON_PI(lda    [%g1] ASI_LEON_MMUREGS, %g0)
+SUN_PI_(lda    [%g1] ASI_M_MMUREGS, %g0)
 
-       lda     [%g0] ASI_M_MMUREGS, %g1
+LEON_PI(lda    [%g0] ASI_LEON_MMUREGS, %g1)
+SUN_PI_(lda    [%g0] ASI_M_MMUREGS, %g1)
        or      %g1, 0x2, %g1
-       sta     %g1, [%g0] ASI_M_MMUREGS
+LEON_PI(sta    %g1, [%g0] ASI_LEON_MMUREGS)
+SUN_PI_(sta    %g1, [%g0] ASI_M_MMUREGS)
 
        restore %g0, %g0, %g0
 
@@ -244,13 +247,16 @@ srmmu_rett_stackchk:
        save    %g0, %g0, %g0
 
        andn    %g1, 0x2, %g1
-       sta     %g1, [%g0] ASI_M_MMUREGS
+LEON_PI(sta    %g1, [%g0] ASI_LEON_MMUREGS)
+SUN_PI_(sta    %g1, [%g0] ASI_M_MMUREGS)
 
        mov     AC_M_SFAR, %g2
-       lda     [%g2] ASI_M_MMUREGS, %g2
+LEON_PI(lda    [%g2] ASI_LEON_MMUREGS, %g2)
+SUN_PI_(lda    [%g2] ASI_M_MMUREGS, %g2)
 
        mov     AC_M_SFSR, %g1
-       lda     [%g1] ASI_M_MMUREGS, %g1
+LEON_PI(lda    [%g1] ASI_LEON_MMUREGS, %g1)
+SUN_PI_(lda    [%g1] ASI_M_MMUREGS, %g1)
        andcc   %g1, 0x2, %g0
        be      ret_trap_userwins_ok
         nop
index c052313f4dc578700193abaf5fc6e9b127e66e0a..efe3e64bba38bd80814d51958bdb046b9c8f8a68 100644 (file)
@@ -32,6 +32,7 @@
 #include <linux/cpu.h>
 #include <linux/kdebug.h>
 #include <linux/export.h>
+#include <linux/start_kernel.h>
 
 #include <asm/io.h>
 #include <asm/processor.h>
@@ -45,6 +46,7 @@
 #include <asm/cpudata.h>
 #include <asm/setup.h>
 #include <asm/cacheflush.h>
+#include <asm/sections.h>
 
 #include "kernel.h"
 
@@ -237,28 +239,42 @@ static void __init per_cpu_patch(void)
        }
 }
 
+struct leon_1insn_patch_entry {
+       unsigned int addr;
+       unsigned int insn;
+};
+
 enum sparc_cpu sparc_cpu_model;
 EXPORT_SYMBOL(sparc_cpu_model);
 
-struct tt_entry *sparc_ttable;
+static __init void leon_patch(void)
+{
+       struct leon_1insn_patch_entry *start = (void *)__leon_1insn_patch;
+       struct leon_1insn_patch_entry *end = (void *)__leon_1insn_patch_end;
 
-struct pt_regs fake_swapper_regs;
+       /* Default instruction is leon - no patching */
+       if (sparc_cpu_model == sparc_leon)
+               return;
 
-void __init setup_arch(char **cmdline_p)
-{
-       int i;
-       unsigned long highest_paddr;
+       while (start < end) {
+               unsigned long addr = start->addr;
 
-       sparc_ttable = (struct tt_entry *) &trapbase;
+               *(unsigned int *)(addr) = start->insn;
+               flushi(addr);
 
-       /* Initialize PROM console and command line. */
-       *cmdline_p = prom_getbootargs();
-       strcpy(boot_command_line, *cmdline_p);
-       parse_early_param();
+               start++;
+       }
+}
 
-       boot_flags_init(*cmdline_p);
+struct tt_entry *sparc_ttable;
+struct pt_regs fake_swapper_regs;
 
-       register_console(&prom_early_console);
+/* Called from head_32.S - before we have setup anything
+ * in the kernel. Be very careful with what you do here.
+ */
+void __init sparc32_start_kernel(struct linux_romvec *rp)
+{
+       prom_init(rp);
 
        /* Set sparc_cpu_model */
        sparc_cpu_model = sun_unknown;
@@ -275,6 +291,26 @@ void __init setup_arch(char **cmdline_p)
        if (!strncmp(&cputypval[0], "leon" , 4))
                sparc_cpu_model = sparc_leon;
 
+       leon_patch();
+       start_kernel();
+}
+
+void __init setup_arch(char **cmdline_p)
+{
+       int i;
+       unsigned long highest_paddr;
+
+       sparc_ttable = (struct tt_entry *) &trapbase;
+
+       /* Initialize PROM console and command line. */
+       *cmdline_p = prom_getbootargs();
+       strcpy(boot_command_line, *cmdline_p);
+       parse_early_param();
+
+       boot_flags_init(*cmdline_p);
+
+       register_console(&prom_early_console);
+
        printk("ARCH: ");
        switch(sparc_cpu_model) {
        case sun4m:
index bb1513e45f1a811b05a07979995726b99eca1a94..a53e0a5fd3a3d944a5eb42391e2862c92a23c044 100644 (file)
@@ -32,8 +32,6 @@
 
 #include "sigutil.h"
 
-#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
-
 /* This magic should be in g_upper[0] for all upper parts
  * to be valid.
  */
@@ -274,7 +272,6 @@ void do_sigreturn32(struct pt_regs *regs)
                case 2: set.sig[1] = seta[2] + (((long)seta[3]) << 32);
                case 1: set.sig[0] = seta[0] + (((long)seta[1]) << 32);
        }
-       sigdelsetmask(&set, ~_BLOCKABLE);
        set_current_blocked(&set);
        return;
 
@@ -376,7 +373,6 @@ asmlinkage void do_rt_sigreturn32(struct pt_regs *regs)
                case 2: set.sig[1] = seta.sig[2] + (((long)seta.sig[3]) << 32);
                case 1: set.sig[0] = seta.sig[0] + (((long)seta.sig[1]) << 32);
        }
-       sigdelsetmask(&set, ~_BLOCKABLE);
        set_current_blocked(&set);
        return;
 segv:
@@ -775,7 +771,7 @@ sigsegv:
        return -EFAULT;
 }
 
-static inline int handle_signal32(unsigned long signr, struct k_sigaction *ka,
+static inline void handle_signal32(unsigned long signr, struct k_sigaction *ka,
                                  siginfo_t *info,
                                  sigset_t *oldset, struct pt_regs *regs)
 {
@@ -787,12 +783,9 @@ static inline int handle_signal32(unsigned long signr, struct k_sigaction *ka,
                err = setup_frame32(ka, regs, signr, oldset);
 
        if (err)
-               return err;
-
-       block_sigmask(ka, signr);
-       tracehook_signal_handler(signr, info, ka, regs, 0);
+               return;
 
-       return 0;
+       signal_delivered(signr, info, ka, regs, 0);
 }
 
 static inline void syscall_restart32(unsigned long orig_i0, struct pt_regs *regs,
@@ -841,14 +834,7 @@ void do_signal32(sigset_t *oldset, struct pt_regs * regs)
        if (signr > 0) {
                if (restart_syscall)
                        syscall_restart32(orig_i0, regs, &ka.sa);
-               if (handle_signal32(signr, &ka, &info, oldset, regs) == 0) {
-                       /* A signal was successfully delivered; the saved
-                        * sigmask will have been stored in the signal frame,
-                        * and will be restored by sigreturn, so we can simply
-                        * clear the TS_RESTORE_SIGMASK flag.
-                        */
-                       current_thread_info()->status &= ~TS_RESTORE_SIGMASK;
-               }
+               handle_signal32(signr, &ka, &info, oldset, regs);
                return;
        }
        if (restart_syscall &&
@@ -872,10 +858,7 @@ void do_signal32(sigset_t *oldset, struct pt_regs * regs)
        /* If there's no signal to deliver, we just put the saved sigmask
         * back
         */
-       if (current_thread_info()->status & TS_RESTORE_SIGMASK) {
-               current_thread_info()->status &= ~TS_RESTORE_SIGMASK;
-               set_current_blocked(&current->saved_sigmask);
-       }
+       restore_saved_sigmask();
 }
 
 struct sigstack32 {
index 2b7e849f7c6528b1da91146f3f515ccbf21370c3..68f9c8650af47b2731c622cc8ada8768b63d90cb 100644 (file)
@@ -29,8 +29,6 @@
 
 #include "sigutil.h"
 
-#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
-
 extern void fpsave(unsigned long *fpregs, unsigned long *fsr,
                   void *fpqueue, unsigned long *fpqdepth);
 extern void fpload(unsigned long *fpregs, unsigned long *fsr);
@@ -130,7 +128,6 @@ asmlinkage void do_sigreturn(struct pt_regs *regs)
        if (err)
                goto segv_and_exit;
 
-       sigdelsetmask(&set, ~_BLOCKABLE);
        set_current_blocked(&set);
        return;
 
@@ -197,7 +194,6 @@ asmlinkage void do_rt_sigreturn(struct pt_regs *regs)
                        goto segv;
        }
 
-       sigdelsetmask(&set, ~_BLOCKABLE);
        set_current_blocked(&set);
        return;
 segv:
@@ -449,10 +445,11 @@ sigsegv:
        return -EFAULT;
 }
 
-static inline int
+static inline void
 handle_signal(unsigned long signr, struct k_sigaction *ka,
-             siginfo_t *info, sigset_t *oldset, struct pt_regs *regs)
+             siginfo_t *info, struct pt_regs *regs)
 {
+       sigset_t *oldset = sigmask_to_save();
        int err;
 
        if (ka->sa.sa_flags & SA_SIGINFO)
@@ -461,12 +458,9 @@ handle_signal(unsigned long signr, struct k_sigaction *ka,
                err = setup_frame(ka, regs, signr, oldset);
 
        if (err)
-               return err;
-
-       block_sigmask(ka, signr);
-       tracehook_signal_handler(signr, info, ka, regs, 0);
+               return;
 
-       return 0;
+       signal_delivered(signr, info, ka, regs, 0);
 }
 
 static inline void syscall_restart(unsigned long orig_i0, struct pt_regs *regs,
@@ -498,7 +492,6 @@ static void do_signal(struct pt_regs *regs, unsigned long orig_i0)
 {
        struct k_sigaction ka;
        int restart_syscall;
-       sigset_t *oldset;
        siginfo_t info;
        int signr;
 
@@ -523,11 +516,6 @@ static void do_signal(struct pt_regs *regs, unsigned long orig_i0)
        if (pt_regs_is_syscall(regs) && (regs->psr & PSR_C))
                regs->u_regs[UREG_G6] = orig_i0;
 
-       if (test_thread_flag(TIF_RESTORE_SIGMASK))
-               oldset = &current->saved_sigmask;
-       else
-               oldset = &current->blocked;
-
        signr = get_signal_to_deliver(&info, &ka, regs, NULL);
 
        /* If the debugger messes with the program counter, it clears
@@ -544,15 +532,7 @@ static void do_signal(struct pt_regs *regs, unsigned long orig_i0)
        if (signr > 0) {
                if (restart_syscall)
                        syscall_restart(orig_i0, regs, &ka.sa);
-               if (handle_signal(signr, &ka, &info, oldset, regs) == 0) {
-                       /* a signal was successfully delivered; the saved
-                        * sigmask will have been stored in the signal frame,
-                        * and will be restored by sigreturn, so we can simply
-                        * clear the TIF_RESTORE_SIGMASK flag.
-                        */
-                       if (test_thread_flag(TIF_RESTORE_SIGMASK))
-                               clear_thread_flag(TIF_RESTORE_SIGMASK);
-               }
+               handle_signal(signr, &ka, &info, regs);
                return;
        }
        if (restart_syscall &&
@@ -576,22 +556,17 @@ static void do_signal(struct pt_regs *regs, unsigned long orig_i0)
        /* if there's no signal to deliver, we just put the saved sigmask
         * back
         */
-       if (test_thread_flag(TIF_RESTORE_SIGMASK)) {
-               clear_thread_flag(TIF_RESTORE_SIGMASK);
-               set_current_blocked(&current->saved_sigmask);
-       }
+       restore_saved_sigmask();
 }
 
 void do_notify_resume(struct pt_regs *regs, unsigned long orig_i0,
                      unsigned long thread_info_flags)
 {
-       if (thread_info_flags & (_TIF_SIGPENDING | _TIF_RESTORE_SIGMASK))
+       if (thread_info_flags & _TIF_SIGPENDING)
                do_signal(regs, orig_i0);
        if (thread_info_flags & _TIF_NOTIFY_RESUME) {
                clear_thread_flag(TIF_NOTIFY_RESUME);
                tracehook_notify_resume(regs);
-               if (current->replacement_session_keyring)
-                       key_replace_session_keyring();
        }
 }
 
index eafaab486b2d24465a0911e46a08c35ce17d3d29..867de2f8189c32acf359fe5575cd139ede6b7048 100644 (file)
@@ -38,8 +38,6 @@
 #include "systbls.h"
 #include "sigutil.h"
 
-#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
-
 /* {set, get}context() needed for 64-bit SparcLinux userland. */
 asmlinkage void sparc64_set_context(struct pt_regs *regs)
 {
@@ -71,7 +69,6 @@ asmlinkage void sparc64_set_context(struct pt_regs *regs)
                        if (__copy_from_user(&set, &ucp->uc_sigmask, sizeof(sigset_t)))
                                goto do_sigsegv;
                }
-               sigdelsetmask(&set, ~_BLOCKABLE);
                set_current_blocked(&set);
        }
        if (test_thread_flag(TIF_32BIT)) {
@@ -315,7 +312,6 @@ void do_rt_sigreturn(struct pt_regs *regs)
        /* Prevent syscall restart.  */
        pt_regs_clear_syscall(regs);
 
-       sigdelsetmask(&set, ~_BLOCKABLE);
        set_current_blocked(&set);
        return;
 segv:
@@ -466,7 +462,7 @@ sigsegv:
        return -EFAULT;
 }
 
-static inline int handle_signal(unsigned long signr, struct k_sigaction *ka,
+static inline void handle_signal(unsigned long signr, struct k_sigaction *ka,
                                siginfo_t *info,
                                sigset_t *oldset, struct pt_regs *regs)
 {
@@ -475,12 +471,9 @@ static inline int handle_signal(unsigned long signr, struct k_sigaction *ka,
        err = setup_rt_frame(ka, regs, signr, oldset,
                             (ka->sa.sa_flags & SA_SIGINFO) ? info : NULL);
        if (err)
-               return err;
-
-       block_sigmask(ka, signr);
-       tracehook_signal_handler(signr, info, ka, regs, 0);
+               return;
 
-       return 0;
+       signal_delivered(signr, info, ka, regs, 0);
 }
 
 static inline void syscall_restart(unsigned long orig_i0, struct pt_regs *regs,
@@ -512,7 +505,7 @@ static void do_signal(struct pt_regs *regs, unsigned long orig_i0)
 {
        struct k_sigaction ka;
        int restart_syscall;
-       sigset_t *oldset;
+       sigset_t *oldset = sigmask_to_save();
        siginfo_t info;
        int signr;
        
@@ -538,11 +531,6 @@ static void do_signal(struct pt_regs *regs, unsigned long orig_i0)
            (regs->tstate & (TSTATE_XCARRY | TSTATE_ICARRY)))
                regs->u_regs[UREG_G6] = orig_i0;
 
-       if (current_thread_info()->status & TS_RESTORE_SIGMASK)
-               oldset = &current->saved_sigmask;
-       else
-               oldset = &current->blocked;
-
 #ifdef CONFIG_COMPAT
        if (test_thread_flag(TIF_32BIT)) {
                extern void do_signal32(sigset_t *, struct pt_regs *);
@@ -563,14 +551,7 @@ static void do_signal(struct pt_regs *regs, unsigned long orig_i0)
        if (signr > 0) {
                if (restart_syscall)
                        syscall_restart(orig_i0, regs, &ka.sa);
-               if (handle_signal(signr, &ka, &info, oldset, regs) == 0) {
-                       /* A signal was successfully delivered; the saved
-                        * sigmask will have been stored in the signal frame,
-                        * and will be restored by sigreturn, so we can simply
-                        * clear the TS_RESTORE_SIGMASK flag.
-                        */
-                       current_thread_info()->status &= ~TS_RESTORE_SIGMASK;
-               }
+               handle_signal(signr, &ka, &info, oldset, regs);
                return;
        }
        if (restart_syscall &&
@@ -594,10 +575,7 @@ static void do_signal(struct pt_regs *regs, unsigned long orig_i0)
        /* If there's no signal to deliver, we just put the saved sigmask
         * back
         */
-       if (current_thread_info()->status & TS_RESTORE_SIGMASK) {
-               current_thread_info()->status &= ~TS_RESTORE_SIGMASK;
-               set_current_blocked(&current->saved_sigmask);
-       }
+       restore_saved_sigmask();
 }
 
 void do_notify_resume(struct pt_regs *regs, unsigned long orig_i0, unsigned long thread_info_flags)
@@ -607,8 +585,6 @@ void do_notify_resume(struct pt_regs *regs, unsigned long orig_i0, unsigned long
        if (thread_info_flags & _TIF_NOTIFY_RESUME) {
                clear_thread_flag(TIF_NOTIFY_RESUME);
                tracehook_notify_resume(regs);
-               if (current->replacement_session_keyring)
-                       key_replace_session_keyring();
        }
 }
 
index 3ee51f189a55297b0babeb1f54d0b40af97de6f8..275f74fd6f6a3f16fdd4e5fae291af2a364075c0 100644 (file)
@@ -580,16 +580,9 @@ SYSCALL_DEFINE5(64_mremap, unsigned long, addr,    unsigned long, old_len,
                unsigned long, new_len, unsigned long, flags,
                unsigned long, new_addr)
 {
-       unsigned long ret = -EINVAL;
-
        if (test_thread_flag(TIF_32BIT))
-               goto out;
-
-       down_write(&current->mm->mmap_sem);
-       ret = do_mremap(addr, old_len, new_len, flags, new_addr);
-       up_write(&current->mm->mmap_sem);
-out:
-       return ret;       
+               return -EINVAL;
+       return sys_mremap(addr, old_len, new_len, flags, new_addr);
 }
 
 /* we come to here via sys_nis_syscall so it can setup the regs argument */
index 7364ddc9e5aadd64317bc48c48aada5948b16727..af27acab44868a085ad2182049d81a6da61831e2 100644 (file)
@@ -149,8 +149,6 @@ sun4d_cpu_startup:
 
        b,a     smp_do_cpu_idle
 
-#ifdef CONFIG_SPARC_LEON
-
        __CPUINIT
        .align  4
         .global leon_smp_cpu_startup, smp_penguin_ctable
@@ -161,7 +159,7 @@ leon_smp_cpu_startup:
         ld [%g1+4],%g1
         srl %g1,4,%g1
         set 0x00000100,%g5 /* SRMMU_CTXTBL_PTR */
-       sta %g1, [%g5] ASI_M_MMUREGS
+       sta %g1, [%g5] ASI_LEON_MMUREGS
 
        /* Set up a sane %psr -- PIL<0xf> S<0x1> PS<0x1> CWP<0x0> */
        set     (PSR_PIL | PSR_S | PSR_PS), %g1
@@ -207,5 +205,3 @@ leon_smp_cpu_startup:
         nop
 
        b,a     smp_do_cpu_idle
-
-#endif
index c72fdf55e1c108435b6f4193b1c8377b441eef3b..3b05e6697710da1718be093e1f56dccaddc04e94 100644 (file)
@@ -2054,7 +2054,7 @@ void do_fpieee(struct pt_regs *regs)
        do_fpe_common(regs);
 }
 
-extern int do_mathemu(struct pt_regs *, struct fpustate *);
+extern int do_mathemu(struct pt_regs *, struct fpustate *, bool);
 
 void do_fpother(struct pt_regs *regs)
 {
@@ -2068,7 +2068,7 @@ void do_fpother(struct pt_regs *regs)
        switch ((current_thread_info()->xfsr[0] & 0x1c000)) {
        case (2 << 14): /* unfinished_FPop */
        case (3 << 14): /* unimplemented_FPop */
-               ret = do_mathemu(regs, f);
+               ret = do_mathemu(regs, f, false);
                break;
        }
        if (ret)
@@ -2308,10 +2308,12 @@ void do_illegal_instruction(struct pt_regs *regs)
                        } else {
                                struct fpustate *f = FPUSTATE;
 
-                               /* XXX maybe verify XFSR bits like
-                                * XXX do_fpother() does?
+                               /* On UltraSPARC T2 and later, FPU insns which
+                                * are not implemented in HW signal an illegal
+                                * instruction trap and do not set the FP Trap
+                                * Trap in the %fsr to unimplemented_FPop.
                                 */
-                               if (do_mathemu(regs, f))
+                               if (do_mathemu(regs, f, true))
                                        return;
                        }
                }
index 0e1605697b4905b030923be4d18ef2efa47fe423..89c2c29f154b4c45114df0dce93feee4ce8330af 100644 (file)
@@ -107,6 +107,11 @@ SECTIONS
                *(.sun4v_2insn_patch)
                __sun4v_2insn_patch_end = .;
        }
+       .leon_1insn_patch : {
+               __leon_1insn_patch = .;
+               *(.leon_1insn_patch)
+               __leon_1insn_patch_end = .;
+       }
        .swapper_tsb_phys_patch : {
                __swapper_tsb_phys_patch = .;
                *(.swapper_tsb_phys_patch)
index 4c2de3cf309b65ad77b9f92e0773b5bd2e1f9e8d..28a7bc69f82b1dcf2113547cd19697244843d7dd 100644 (file)
@@ -332,24 +332,30 @@ spwin_srmmu_stackchk:
         mov    AC_M_SFSR, %glob_tmp
 
        /* Clear the fault status and turn on the no_fault bit. */
-       lda     [%glob_tmp] ASI_M_MMUREGS, %g0          ! eat SFSR
+LEON_PI(lda    [%glob_tmp] ASI_LEON_MMUREGS, %g0)      ! eat SFSR
+SUN_PI_(lda    [%glob_tmp] ASI_M_MMUREGS, %g0)         ! eat SFSR
 
-       lda     [%g0] ASI_M_MMUREGS, %glob_tmp          ! read MMU control
+LEON_PI(lda    [%g0] ASI_LEON_MMUREGS, %glob_tmp)      ! read MMU control
+SUN_PI_(lda    [%g0] ASI_M_MMUREGS, %glob_tmp)         ! read MMU control
        or      %glob_tmp, 0x2, %glob_tmp               ! or in no_fault bit
-       sta     %glob_tmp, [%g0] ASI_M_MMUREGS          ! set it
+LEON_PI(sta    %glob_tmp, [%g0] ASI_LEON_MMUREGS)      ! set it
+SUN_PI_(sta    %glob_tmp, [%g0] ASI_M_MMUREGS)         ! set it
 
        /* Dump the registers and cross fingers. */
        STORE_WINDOW(sp)
 
        /* Clear the no_fault bit and check the status. */
        andn    %glob_tmp, 0x2, %glob_tmp
-       sta     %glob_tmp, [%g0] ASI_M_MMUREGS
+LEON_PI(sta    %glob_tmp, [%g0] ASI_LEON_MMUREGS)
+SUN_PI_(sta    %glob_tmp, [%g0] ASI_M_MMUREGS)
 
        mov     AC_M_SFAR, %glob_tmp
-       lda     [%glob_tmp] ASI_M_MMUREGS, %g0
+LEON_PI(lda    [%glob_tmp] ASI_LEON_MMUREGS, %g0)
+SUN_PI_(lda    [%glob_tmp] ASI_M_MMUREGS, %g0)
 
        mov     AC_M_SFSR, %glob_tmp
-       lda     [%glob_tmp] ASI_M_MMUREGS, %glob_tmp
+LEON_PI(lda    [%glob_tmp] ASI_LEON_MMUREGS, %glob_tmp)
+SUN_PI_(lda    [%glob_tmp] ASI_M_MMUREGS, %glob_tmp)
        andcc   %glob_tmp, 0x2, %g0                     ! did we fault?
        be,a    spwin_finish_up + 0x4                   ! cool beans, success
         restore %g0, %g0, %g0
index 9fde91a249e06b470008c155b8bc56f8b7de08e9..2c21cc59683e2ca28d9c316e47c8b0cda6055795 100644 (file)
@@ -254,16 +254,19 @@ srmmu_fwin_stackchk:
        mov     AC_M_SFSR, %l4
        cmp     %l5, %sp
        bleu    fwin_user_stack_is_bolixed
-        lda    [%l4] ASI_M_MMUREGS, %g0        ! clear fault status
+LEON_PI( lda   [%l4] ASI_LEON_MMUREGS, %g0)    ! clear fault status
+SUN_PI_( lda   [%l4] ASI_M_MMUREGS, %g0)       ! clear fault status
 
        /* The technique is, turn off faults on this processor,
         * just let the load rip, then check the sfsr to see if
         * a fault did occur.  Then we turn on fault traps again
         * and branch conditionally based upon what happened.
         */
-       lda     [%g0] ASI_M_MMUREGS, %l5        ! read mmu-ctrl reg
+LEON_PI(lda    [%g0] ASI_LEON_MMUREGS, %l5)    ! read mmu-ctrl reg
+SUN_PI_(lda    [%g0] ASI_M_MMUREGS, %l5)       ! read mmu-ctrl reg
        or      %l5, 0x2, %l5                   ! turn on no-fault bit
-       sta     %l5, [%g0] ASI_M_MMUREGS        ! store it
+LEON_PI(sta    %l5, [%g0] ASI_LEON_MMUREGS)    ! store it
+SUN_PI_(sta    %l5, [%g0] ASI_M_MMUREGS)       ! store it
 
        /* Cross fingers and go for it. */
        LOAD_WINDOW(sp)
@@ -275,18 +278,22 @@ srmmu_fwin_stackchk:
 
        /* LOCATION: Window 'T' */
 
-       lda     [%g0] ASI_M_MMUREGS, %twin_tmp1 ! load mmu-ctrl again
-       andn    %twin_tmp1, 0x2, %twin_tmp1     ! clear no-fault bit
-       sta     %twin_tmp1, [%g0] ASI_M_MMUREGS ! store it
+LEON_PI(lda    [%g0] ASI_LEON_MMUREGS, %twin_tmp1)     ! load mmu-ctrl again
+SUN_PI_(lda    [%g0] ASI_M_MMUREGS, %twin_tmp1)        ! load mmu-ctrl again
+       andn    %twin_tmp1, 0x2, %twin_tmp1             ! clear no-fault bit
+LEON_PI(sta    %twin_tmp1, [%g0] ASI_LEON_MMUREGS)     ! store it
+SUN_PI_(sta    %twin_tmp1, [%g0] ASI_M_MMUREGS)        ! store it
 
        mov     AC_M_SFAR, %twin_tmp2
-       lda     [%twin_tmp2] ASI_M_MMUREGS, %g0 ! read fault address
+LEON_PI(lda    [%twin_tmp2] ASI_LEON_MMUREGS, %g0)     ! read fault address
+SUN_PI_(lda    [%twin_tmp2] ASI_M_MMUREGS, %g0)        ! read fault address
 
        mov     AC_M_SFSR, %twin_tmp2
-       lda     [%twin_tmp2] ASI_M_MMUREGS, %twin_tmp2  ! read fault status
-       andcc   %twin_tmp2, 0x2, %g0                    ! did fault occur?
+LEON_PI(lda    [%twin_tmp2] ASI_LEON_MMUREGS, %twin_tmp2) ! read fault status
+SUN_PI_(lda    [%twin_tmp2] ASI_M_MMUREGS, %twin_tmp2)    ! read fault status
+       andcc   %twin_tmp2, 0x2, %g0                       ! did fault occur?
 
-       bne     1f                                      ! yep, cleanup
+       bne     1f                                         ! yep, cleanup
         nop
 
        wr      %t_psr, 0x0, %psr
index 2bbe2f28ad23355edb2716828769061327c2f829..1704068da92806d5868d0374bea669f31f21b113 100644 (file)
@@ -163,7 +163,7 @@ typedef union {
        u64 q[2];
 } *argp;
 
-int do_mathemu(struct pt_regs *regs, struct fpustate *f)
+int do_mathemu(struct pt_regs *regs, struct fpustate *f, bool illegal_insn_trap)
 {
        unsigned long pc = regs->tpc;
        unsigned long tstate = regs->tstate;
@@ -218,7 +218,7 @@ int do_mathemu(struct pt_regs *regs, struct fpustate *f)
                        case FSQRTS: {
                                unsigned long x = current_thread_info()->xfsr[0];
 
-                               x = (x >> 14) & 0xf;
+                               x = (x >> 14) & 0x7;
                                TYPE(x,1,1,1,1,0,0);
                                break;
                        }
@@ -226,7 +226,7 @@ int do_mathemu(struct pt_regs *regs, struct fpustate *f)
                        case FSQRTD: {
                                unsigned long x = current_thread_info()->xfsr[0];
 
-                               x = (x >> 14) & 0xf;
+                               x = (x >> 14) & 0x7;
                                TYPE(x,2,1,2,1,0,0);
                                break;
                        }
@@ -357,9 +357,17 @@ int do_mathemu(struct pt_regs *regs, struct fpustate *f)
        if (type) {
                argp rs1 = NULL, rs2 = NULL, rd = NULL;
                
-               freg = (current_thread_info()->xfsr[0] >> 14) & 0xf;
-               if (freg != (type >> 9))
-                       goto err;
+               /* Starting with UltraSPARC-T2, the cpu does not set the FP Trap
+                * Type field in the %fsr to unimplemented_FPop.  Nor does it
+                * use the fp_exception_other trap.  Instead it signals an
+                * illegal instruction and leaves the FP trap type field of
+                * the %fsr unchanged.
+                */
+               if (!illegal_insn_trap) {
+                       int ftt = (current_thread_info()->xfsr[0] >> 14) & 0x7;
+                       if (ftt != (type >> 9))
+                               goto err;
+               }
                current_thread_info()->xfsr[0] &= ~0x1c000;
                freg = ((insn >> 14) & 0x1f);
                switch (type & 0x3) {
index 69ffd3112fed2053ae72f42c7a602a43d7378d13..30c3eccfdf5a209f8b09458824aa9d706f3079dd 100644 (file)
@@ -8,8 +8,9 @@ obj-$(CONFIG_SPARC64)   += ultra.o tlb.o tsb.o gup.o
 obj-y                   += fault_$(BITS).o
 obj-y                   += init_$(BITS).o
 obj-$(CONFIG_SPARC32)   += extable.o srmmu.o iommu.o io-unit.o
+obj-$(CONFIG_SPARC32)   += srmmu_access.o
 obj-$(CONFIG_SPARC32)   += hypersparc.o viking.o tsunami.o swift.o
-obj-$(CONFIG_SPARC_LEON)+= leon_mm.o
+obj-$(CONFIG_SPARC32)   += leon_mm.o
 
 # Only used by sparc64
 obj-$(CONFIG_HUGETLB_PAGE) += hugetlbpage.o
index 4c67ae6e50231990bcd40ce3c2170a2f7c070497..5bed085a2c17984a5c33fc5ea509005e3db679cb 100644 (file)
@@ -32,7 +32,7 @@ static inline unsigned long leon_get_ctable_ptr(void)
 }
 
 
-unsigned long srmmu_swprobe(unsigned long vaddr, unsigned long *paddr)
+unsigned long leon_swprobe(unsigned long vaddr, unsigned long *paddr)
 {
 
        unsigned int ctxtbl;
index 256db6b22c54ed7cf2cc26412d5013930896703b..62e3f57733037d89d828d389bdefff93c0bc3f0b 100644 (file)
@@ -646,6 +646,23 @@ static void __init srmmu_allocate_ptable_skeleton(unsigned long start,
        }
 }
 
+/* These flush types are not available on all chips... */
+static inline unsigned long srmmu_probe(unsigned long vaddr)
+{
+       unsigned long retval;
+
+       if (sparc_cpu_model != sparc_leon) {
+
+               vaddr &= PAGE_MASK;
+               __asm__ __volatile__("lda [%1] %2, %0\n\t" :
+                                    "=r" (retval) :
+                                    "r" (vaddr | 0x400), "i" (ASI_M_FLUSH_PROBE));
+       } else {
+               retval = leon_swprobe(vaddr, 0);
+       }
+       return retval;
+}
+
 /*
  * This is much cleaner than poking around physical address space
  * looking at the prom's page table directly which is what most
@@ -665,7 +682,7 @@ static void __init srmmu_inherit_prom_mappings(unsigned long start,
                        break; /* probably wrap around */
                if(start == 0xfef00000)
                        start = KADB_DEBUGGER_BEGVM;
-               if(!(prompte = srmmu_hwprobe(start))) {
+               if(!(prompte = srmmu_probe(start))) {
                        start += PAGE_SIZE;
                        continue;
                }
@@ -674,12 +691,12 @@ static void __init srmmu_inherit_prom_mappings(unsigned long start,
                what = 0;
     
                if(!(start & ~(SRMMU_REAL_PMD_MASK))) {
-                       if(srmmu_hwprobe((start-PAGE_SIZE) + SRMMU_REAL_PMD_SIZE) == prompte)
+                       if(srmmu_probe((start-PAGE_SIZE) + SRMMU_REAL_PMD_SIZE) == prompte)
                                what = 1;
                }
     
                if(!(start & ~(SRMMU_PGDIR_MASK))) {
-                       if(srmmu_hwprobe((start-PAGE_SIZE) + SRMMU_PGDIR_SIZE) ==
+                       if(srmmu_probe((start-PAGE_SIZE) + SRMMU_PGDIR_SIZE) ==
                           prompte)
                                what = 2;
                }
@@ -1156,7 +1173,7 @@ static void turbosparc_flush_page_to_ram(unsigned long page)
 #ifdef TURBOSPARC_WRITEBACK
        volatile unsigned long clear;
 
-       if (srmmu_hwprobe(page))
+       if (srmmu_probe(page))
                turbosparc_flush_page_cache(page);
        clear = srmmu_get_fstatus();
 #endif
diff --git a/arch/sparc/mm/srmmu_access.S b/arch/sparc/mm/srmmu_access.S
new file mode 100644 (file)
index 0000000..d0a67b2
--- /dev/null
@@ -0,0 +1,82 @@
+/* Assembler variants of srmmu access functions.
+ * Implemented in assembler to allow run-time patching.
+ * LEON uses a different ASI for MMUREGS than SUN.
+ *
+ * The leon_1insn_patch infrastructure is used
+ * for the run-time patching.
+ */
+
+#include <linux/linkage.h>
+
+#include <asm/asmmacro.h>
+#include <asm/pgtsrmmu.h>
+#include <asm/asi.h>
+
+/* unsigned int srmmu_get_mmureg(void) */
+ENTRY(srmmu_get_mmureg)
+LEON_PI(lda    [%g0] ASI_LEON_MMUREGS, %o0)
+SUN_PI_(lda    [%g0] ASI_M_MMUREGS, %o0)
+       retl
+        nop
+ENDPROC(srmmu_get_mmureg)
+
+/* void srmmu_set_mmureg(unsigned long regval) */
+ENTRY(srmmu_set_mmureg)
+LEON_PI(sta    %o0, [%g0] ASI_LEON_MMUREGS)
+SUN_PI_(sta    %o0, [%g0] ASI_M_MMUREGS)
+       retl
+        nop
+ENDPROC(srmmu_set_mmureg)
+
+/* void srmmu_set_ctable_ptr(unsigned long paddr) */
+ENTRY(srmmu_set_ctable_ptr)
+       /* paddr = ((paddr >> 4) & SRMMU_CTX_PMASK); */
+       srl     %o0, 4, %g1
+       and     %g1, SRMMU_CTX_PMASK, %g1
+
+       mov     SRMMU_CTXTBL_PTR, %g2
+LEON_PI(sta    %g1, [%g2] ASI_LEON_MMUREGS)
+SUN_PI_(sta    %g1, [%g2] ASI_M_MMUREGS)
+       retl
+        nop
+ENDPROC(srmmu_set_ctable_ptr)
+
+
+/* void srmmu_set_context(int context) */
+ENTRY(srmmu_set_context)
+       mov     SRMMU_CTX_REG, %g1
+LEON_PI(sta    %o0, [%g1] ASI_LEON_MMUREGS)
+SUN_PI_(sta    %o0, [%g1] ASI_M_MMUREGS)
+       retl
+        nop
+ENDPROC(srmmu_set_context)
+
+
+/* int srmmu_get_context(void) */
+ENTRY(srmmu_get_context)
+       mov     SRMMU_CTX_REG, %o0
+LEON_PI(lda     [%o0] ASI_LEON_MMUREGS, %o0)
+SUN_PI_(lda    [%o0] ASI_M_MMUREGS, %o0)
+       retl
+        nop
+ENDPROC(srmmu_get_context)
+
+
+/* unsigned int srmmu_get_fstatus(void) */
+ENTRY(srmmu_get_fstatus)
+       mov     SRMMU_FAULT_STATUS, %o0
+LEON_PI(lda     [%o0] ASI_LEON_MMUREGS, %o0)
+SUN_PI_(lda    [%o0] ASI_M_MMUREGS, %o0)
+       retl
+        nop
+ENDPROC(srmmu_get_fstatus)
+
+
+/* unsigned int srmmu_get_faddr(void) */
+ENTRY(srmmu_get_faddr)
+       mov     SRMMU_FAULT_ADDR, %o0
+LEON_PI(lda     [%o0] ASI_LEON_MMUREGS, %o0)
+SUN_PI_(lda    [%o0] ASI_M_MMUREGS, %o0)
+       retl
+        nop
+ENDPROC(srmmu_get_faddr)
index 69adc08d36a52541b1be754dd7c809824b2d707a..6e74450ff0a110afc32901e273d74a77c80f8ca4 100644 (file)
@@ -44,7 +44,6 @@ typedef __kernel_uid32_t __compat_gid32_t;
 typedef __kernel_mode_t compat_mode_t;
 typedef __kernel_dev_t compat_dev_t;
 typedef __kernel_loff_t compat_loff_t;
-typedef __kernel_nlink_t compat_nlink_t;
 typedef __kernel_ipc_pid_t compat_ipc_pid_t;
 typedef __kernel_daddr_t compat_daddr_t;
 typedef __kernel_fsid_t        compat_fsid_t;
index 656c486e64fafd424863fabe0f66d52a36e5c243..7e1fef36bde6651f2f199115919c887e494448f4 100644 (file)
@@ -166,7 +166,23 @@ static inline void set_restore_sigmask(void)
 {
        struct thread_info *ti = current_thread_info();
        ti->status |= TS_RESTORE_SIGMASK;
-       set_bit(TIF_SIGPENDING, &ti->flags);
+       WARN_ON(!test_bit(TIF_SIGPENDING, &ti->flags));
+}
+static inline void clear_restore_sigmask(void)
+{
+       current_thread_info()->status &= ~TS_RESTORE_SIGMASK;
+}
+static inline bool test_restore_sigmask(void)
+{
+       return current_thread_info()->status & TS_RESTORE_SIGMASK;
+}
+static inline bool test_and_clear_restore_sigmask(void)
+{
+       struct thread_info *ti = current_thread_info();
+       if (!(ti->status & TS_RESTORE_SIGMASK))
+               return false;
+       ti->status &= ~TS_RESTORE_SIGMASK;
+       return true;
 }
 #endif /* !__ASSEMBLY__ */
 
index cdef6e5ec022cdbe6ab4c1c139f56dffc7fbcd8e..474571b8408584d37743678ce02b2ca77c3c9938 100644 (file)
@@ -118,8 +118,6 @@ struct compat_rt_sigframe {
        struct compat_ucontext uc;
 };
 
-#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
-
 long compat_sys_rt_sigaction(int sig, struct compat_sigaction __user *act,
                             struct compat_sigaction __user *oact,
                             size_t sigsetsize)
@@ -302,7 +300,6 @@ long compat_sys_rt_sigreturn(struct pt_regs *regs)
        if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
                goto badframe;
 
-       sigdelsetmask(&set, ~_BLOCKABLE);
        set_current_blocked(&set);
 
        if (restore_sigcontext(regs, &frame->uc.uc_mcontext))
index ba1023d8a0218ca3a33918cf3a256a592a77fb5f..6be7991505019a30ce3ff6268c275a931a088644 100644 (file)
@@ -565,8 +565,6 @@ int do_work_pending(struct pt_regs *regs, u32 thread_info_flags)
        if (thread_info_flags & _TIF_NOTIFY_RESUME) {
                clear_thread_flag(TIF_NOTIFY_RESUME);
                tracehook_notify_resume(regs);
-               if (current->replacement_session_keyring)
-                       key_replace_session_keyring();
                return 1;
        }
        if (thread_info_flags & _TIF_SINGLESTEP) {
index f79d4b88c747ae97db505a7f54143e610e37e24d..e29b0553211d611af9802dfe190f9d0d9d868f69 100644 (file)
@@ -37,8 +37,6 @@
 
 #define DEBUG_SIG 0
 
-#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
-
 SYSCALL_DEFINE3(sigaltstack, const stack_t __user *, uss,
                stack_t __user *, uoss, struct pt_regs *, regs)
 {
@@ -96,7 +94,6 @@ SYSCALL_DEFINE1(rt_sigreturn, struct pt_regs *, regs)
        if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
                goto badframe;
 
-       sigdelsetmask(&set, ~_BLOCKABLE);
        set_current_blocked(&set);
 
        if (restore_sigcontext(regs, &frame->uc.uc_mcontext))
@@ -242,10 +239,11 @@ give_sigsegv:
  * OK, we're invoking a handler
  */
 
-static int handle_signal(unsigned long sig, siginfo_t *info,
-                        struct k_sigaction *ka, sigset_t *oldset,
+static void handle_signal(unsigned long sig, siginfo_t *info,
+                        struct k_sigaction *ka,
                         struct pt_regs *regs)
 {
+       sigset_t *oldset = sigmask_to_save();
        int ret;
 
        /* Are we from a system call? */
@@ -278,15 +276,9 @@ static int handle_signal(unsigned long sig, siginfo_t *info,
        else
 #endif
                ret = setup_rt_frame(sig, ka, info, oldset, regs);
-       if (ret == 0) {
-               /* This code is only called from system calls or from
-                * the work_pending path in the return-to-user code, and
-                * either way we can re-enable interrupts unconditionally.
-                */
-               block_sigmask(ka, sig);
-       }
-
-       return ret;
+       if (ret)
+               return;
+       signal_delivered(sig, info, ka, regs, 0);
 }
 
 /*
@@ -299,7 +291,6 @@ void do_signal(struct pt_regs *regs)
        siginfo_t info;
        int signr;
        struct k_sigaction ka;
-       sigset_t *oldset;
 
        /*
         * i386 will check if we're coming from kernel mode and bail out
@@ -308,24 +299,10 @@ void do_signal(struct pt_regs *regs)
         * helpful, we can reinstate the check on "!user_mode(regs)".
         */
 
-       if (current_thread_info()->status & TS_RESTORE_SIGMASK)
-               oldset = &current->saved_sigmask;
-       else
-               oldset = &current->blocked;
-
        signr = get_signal_to_deliver(&info, &ka, regs, NULL);
        if (signr > 0) {
                /* Whee! Actually deliver the signal.  */
-               if (handle_signal(signr, &info, &ka, oldset, regs) == 0) {
-                       /*
-                        * A signal was successfully delivered; the saved
-                        * sigmask will have been stored in the signal frame,
-                        * and will be restored by sigreturn, so we can simply
-                        * clear the TS_RESTORE_SIGMASK flag.
-                        */
-                       current_thread_info()->status &= ~TS_RESTORE_SIGMASK;
-               }
-
+               handle_signal(signr, &info, &ka, regs);
                goto done;
        }
 
@@ -350,10 +327,7 @@ void do_signal(struct pt_regs *regs)
        }
 
        /* If there's no signal to deliver, just put the saved sigmask back. */
-       if (current_thread_info()->status & TS_RESTORE_SIGMASK) {
-               current_thread_info()->status &= ~TS_RESTORE_SIGMASK;
-               sigprocmask(SIG_SETMASK, &current->saved_sigmask, NULL);
-       }
+       restore_saved_sigmask();
 
 done:
        /* Avoid double syscall restart if there are nested signals. */
index 76078490c2581c92a2c40cc039419e918f8b6f9d..e584e40ee8320f62e82e67abe6230a40ac75b19d 100644 (file)
@@ -6,9 +6,6 @@
 #ifndef __FRAME_KERN_H_
 #define __FRAME_KERN_H_
 
-#define _S(nr) (1<<((nr)-1))
-#define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP)))
-
 extern int setup_signal_stack_sc(unsigned long stack_top, int sig, 
                                 struct k_sigaction *ka,
                                 struct pt_regs *regs, 
index 3a2235e0abc3e18dec7862208364bb8de2d6916c..ccb9a9d283f165760b20fec6bb3b821f814b0a37 100644 (file)
@@ -117,11 +117,8 @@ void interrupt_end(void)
                schedule();
        if (test_thread_flag(TIF_SIGPENDING))
                do_signal();
-       if (test_and_clear_thread_flag(TIF_NOTIFY_RESUME)) {
+       if (test_and_clear_thread_flag(TIF_NOTIFY_RESUME))
                tracehook_notify_resume(&current->thread.regs);
-               if (current->replacement_session_keyring)
-                       key_replace_session_keyring();
-       }
 }
 
 void exit_thread(void)
index 4d93dff6b3713e321a66f49ef160d3da4380e7a2..3d15243ce69234c3ddbac961ef58b0282247a4e4 100644 (file)
@@ -4,7 +4,9 @@
  */
 
 #include "linux/sched.h"
+#include "linux/spinlock.h"
 #include "linux/slab.h"
+#include "linux/oom.h"
 #include "kern_util.h"
 #include "os.h"
 #include "skas.h"
@@ -22,13 +24,18 @@ static void kill_off_processes(void)
                struct task_struct *p;
                int pid;
 
+               read_lock(&tasklist_lock);
                for_each_process(p) {
-                       if (p->mm == NULL)
-                               continue;
+                       struct task_struct *t;
 
-                       pid = p->mm->context.id.u.pid;
+                       t = find_lock_task_mm(p);
+                       if (!t)
+                               continue;
+                       pid = t->mm->context.id.u.pid;
+                       task_unlock(t);
                        os_kill_ptraced_process(pid, 1);
                }
+               read_unlock(&tasklist_lock);
        }
 }
 
index 292e706016c521842b3654ea0e1de2619211dc60..7362d58efc29612c1ffaad64558b3a986b820515 100644 (file)
 EXPORT_SYMBOL(block_signals);
 EXPORT_SYMBOL(unblock_signals);
 
-#define _S(nr) (1<<((nr)-1))
-
-#define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP)))
-
 /*
  * OK, we're invoking a handler
  */
-static int handle_signal(struct pt_regs *regs, unsigned long signr,
-                        struct k_sigaction *ka, siginfo_t *info,
-                        sigset_t *oldset)
+static void handle_signal(struct pt_regs *regs, unsigned long signr,
+                        struct k_sigaction *ka, siginfo_t *info)
 {
+       sigset_t *oldset = sigmask_to_save();
        unsigned long sp;
        int err;
 
@@ -65,9 +61,7 @@ static int handle_signal(struct pt_regs *regs, unsigned long signr,
        if (err)
                force_sigsegv(signr, current);
        else
-               block_sigmask(ka, signr);
-
-       return err;
+               signal_delivered(signr, info, ka, regs, 0);
 }
 
 static int kern_do_signal(struct pt_regs *regs)
@@ -77,24 +71,9 @@ static int kern_do_signal(struct pt_regs *regs)
        int sig, handled_sig = 0;
 
        while ((sig = get_signal_to_deliver(&info, &ka_copy, regs, NULL)) > 0) {
-               sigset_t *oldset;
-               if (test_thread_flag(TIF_RESTORE_SIGMASK))
-                       oldset = &current->saved_sigmask;
-               else
-                       oldset = &current->blocked;
                handled_sig = 1;
                /* Whee!  Actually deliver the signal.  */
-               if (!handle_signal(regs, sig, &ka_copy, &info, oldset)) {
-                       /*
-                        * a signal was successfully delivered; the saved
-                        * sigmask will have been stored in the signal frame,
-                        * and will be restored by sigreturn, so we can simply
-                        * clear the TIF_RESTORE_SIGMASK flag
-                        */
-                       if (test_thread_flag(TIF_RESTORE_SIGMASK))
-                               clear_thread_flag(TIF_RESTORE_SIGMASK);
-                       break;
-               }
+               handle_signal(regs, sig, &ka_copy, &info);
        }
 
        /* Did we come from a system call? */
@@ -130,10 +109,8 @@ static int kern_do_signal(struct pt_regs *regs)
         * if there's no signal to deliver, we just put the saved sigmask
         * back
         */
-       if (!handled_sig && test_thread_flag(TIF_RESTORE_SIGMASK)) {
-               clear_thread_flag(TIF_RESTORE_SIGMASK);
-               sigprocmask(SIG_SETMASK, &current->saved_sigmask, NULL);
-       }
+       if (!handled_sig)
+               restore_saved_sigmask();
        return handled_sig;
 }
 
index dafc9471595021748eda5e750a80d3483a662379..3be60765c0e25d634282ea66e0902a9dc8d41bd4 100644 (file)
@@ -30,6 +30,8 @@ int handle_page_fault(unsigned long address, unsigned long ip,
        pmd_t *pmd;
        pte_t *pte;
        int err = -EFAULT;
+       unsigned int flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE |
+                                (is_write ? FAULT_FLAG_WRITE : 0);
 
        *code_out = SEGV_MAPERR;
 
@@ -40,6 +42,7 @@ int handle_page_fault(unsigned long address, unsigned long ip,
        if (in_atomic())
                goto out_nosemaphore;
 
+retry:
        down_read(&mm->mmap_sem);
        vma = find_vma(mm, address);
        if (!vma)
@@ -65,7 +68,11 @@ good_area:
        do {
                int fault;
 
-               fault = handle_mm_fault(mm, vma, address, is_write ? FAULT_FLAG_WRITE : 0);
+               fault = handle_mm_fault(mm, vma, address, flags);
+
+               if ((fault & VM_FAULT_RETRY) && fatal_signal_pending(current))
+                       goto out_nosemaphore;
+
                if (unlikely(fault & VM_FAULT_ERROR)) {
                        if (fault & VM_FAULT_OOM) {
                                goto out_of_memory;
@@ -75,10 +82,17 @@ good_area:
                        }
                        BUG();
                }
-               if (fault & VM_FAULT_MAJOR)
-                       current->maj_flt++;
-               else
-                       current->min_flt++;
+               if (flags & FAULT_FLAG_ALLOW_RETRY) {
+                       if (fault & VM_FAULT_MAJOR)
+                               current->maj_flt++;
+                       else
+                               current->min_flt++;
+                       if (fault & VM_FAULT_RETRY) {
+                               flags &= ~FAULT_FLAG_ALLOW_RETRY;
+
+                               goto retry;
+                       }
+               }
 
                pgd = pgd_offset(mm, address);
                pud = pud_offset(pgd, address);
index 7754df6ef7d45444dea14b6ebd93a035e54309d6..8adedb37720a0e997dea9ea88abff76113904830 100644 (file)
@@ -21,8 +21,6 @@
 #include <asm/cacheflush.h>
 #include <asm/ucontext.h>
 
-#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
-
 /*
  * For UniCore syscalls, we encode the syscall number into the instruction.
  */
@@ -61,10 +59,8 @@ static int restore_sigframe(struct pt_regs *regs, struct sigframe __user *sf)
        int err;
 
        err = __copy_from_user(&set, &sf->uc.uc_sigmask, sizeof(set));
-       if (err == 0) {
-               sigdelsetmask(&set, ~_BLOCKABLE);
+       if (err == 0)
                set_current_blocked(&set);
-       }
 
        err |= __get_user(regs->UCreg_00, &sf->uc.uc_mcontext.regs.UCreg_00);
        err |= __get_user(regs->UCreg_01, &sf->uc.uc_mcontext.regs.UCreg_01);
@@ -312,13 +308,12 @@ static inline void setup_syscall_restart(struct pt_regs *regs)
 /*
  * OK, we're invoking a handler
  */
-static int handle_signal(unsigned long sig, struct k_sigaction *ka,
-             siginfo_t *info, sigset_t *oldset,
-             struct pt_regs *regs, int syscall)
+static void handle_signal(unsigned long sig, struct k_sigaction *ka,
+             siginfo_t *info, struct pt_regs *regs, int syscall)
 {
        struct thread_info *thread = current_thread_info();
        struct task_struct *tsk = current;
-       sigset_t blocked;
+       sigset_t *oldset = sigmask_to_save();
        int usig = sig;
        int ret;
 
@@ -364,15 +359,10 @@ static int handle_signal(unsigned long sig, struct k_sigaction *ka,
 
        if (ret != 0) {
                force_sigsegv(sig, tsk);
-               return ret;
+               return;
        }
 
-       /*
-        * Block the signal if we were successful.
-        */
-       block_sigmask(ka, sig);
-
-       return 0;
+       signal_delivered(sig, info, ka, regs, 0);
 }
 
 /*
@@ -399,32 +389,12 @@ static void do_signal(struct pt_regs *regs, int syscall)
        if (!user_mode(regs))
                return;
 
-       if (try_to_freeze())
-               goto no_signal;
-
        signr = get_signal_to_deliver(&info, &ka, regs, NULL);
        if (signr > 0) {
-               sigset_t *oldset;
-
-               if (test_thread_flag(TIF_RESTORE_SIGMASK))
-                       oldset = &current->saved_sigmask;
-               else
-                       oldset = &current->blocked;
-               if (handle_signal(signr, &ka, &info, oldset, regs, syscall)
-                               == 0) {
-                       /*
-                        * A signal was successfully delivered; the saved
-                        * sigmask will have been stored in the signal frame,
-                        * and will be restored by sigreturn, so we can simply
-                        * clear the TIF_RESTORE_SIGMASK flag.
-                        */
-                       if (test_thread_flag(TIF_RESTORE_SIGMASK))
-                               clear_thread_flag(TIF_RESTORE_SIGMASK);
-               }
+               handle_signal(signr, &ka, &info, regs, syscall);
                return;
        }
 
- no_signal:
        /*
         * No signal to deliver to the process - restart the syscall.
         */
@@ -451,8 +421,7 @@ static void do_signal(struct pt_regs *regs, int syscall)
        /* If there's no signal to deliver, we just put the saved
         * sigmask back.
         */
-       if (test_and_clear_thread_flag(TIF_RESTORE_SIGMASK))
-               set_current_blocked(&current->saved_sigmask);
+       restore_saved_sigmask();
 }
 
 asmlinkage void do_notify_resume(struct pt_regs *regs,
@@ -464,8 +433,6 @@ asmlinkage void do_notify_resume(struct pt_regs *regs,
        if (thread_flags & _TIF_NOTIFY_RESUME) {
                clear_thread_flag(TIF_NOTIFY_RESUME);
                tracehook_notify_resume(regs);
-               if (current->replacement_session_keyring)
-                       key_replace_session_keyring();
        }
 }
 
index 0e9dec6cadd10a32272fb67823b5ff27e587cc88..e5287d8517aa18172c266d3cb98fa8af2b950bb1 100644 (file)
@@ -1,4 +1,3 @@
-
 obj-$(CONFIG_KVM) += kvm/
 
 # Xen paravirtualization support
@@ -7,6 +6,7 @@ obj-$(CONFIG_XEN) += xen/
 # lguest paravirtualization support
 obj-$(CONFIG_LGUEST_GUEST) += lguest/
 
+obj-y += realmode/
 obj-y += kernel/
 obj-y += mm/
 
index 98bd70faccc50cb0bb07ad96066a74a990870239..daeca56211e39b13babd4504cb19e9797f6e7810 100644 (file)
@@ -273,7 +273,6 @@ asmlinkage long sys32_sigreturn(struct pt_regs *regs)
                                    sizeof(frame->extramask))))
                goto badframe;
 
-       sigdelsetmask(&set, ~_BLOCKABLE);
        set_current_blocked(&set);
 
        if (ia32_restore_sigcontext(regs, &frame->sc, &ax))
@@ -299,7 +298,6 @@ asmlinkage long sys32_rt_sigreturn(struct pt_regs *regs)
        if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
                goto badframe;
 
-       sigdelsetmask(&set, ~_BLOCKABLE);
        set_current_blocked(&set);
 
        if (ia32_restore_sigcontext(regs, &frame->uc.uc_mcontext, &ax))
index 610001d385dd466c48394873c972e3430b7789ba..0c44630d17893e74a2672cf9e896c0a41b762c42 100644 (file)
@@ -29,7 +29,7 @@
 #include <asm/processor.h>
 #include <asm/mmu.h>
 #include <asm/mpspec.h>
-#include <asm/trampoline.h>
+#include <asm/realmode.h>
 
 #define COMPILER_DEPENDENT_INT64   long long
 #define COMPILER_DEPENDENT_UINT64  unsigned long long
@@ -117,11 +117,8 @@ static inline void acpi_disable_pci(void)
 /* Low-level suspend routine. */
 extern int acpi_suspend_lowlevel(void);
 
-extern const unsigned char acpi_wakeup_code[];
-#define acpi_wakeup_address (__pa(TRAMPOLINE_SYM(acpi_wakeup_code)))
-
-/* early initialization routine */
-extern void acpi_reserve_wakeup_memory(void);
+/* Physical address to resume after wakeup */
+#define acpi_wakeup_address ((unsigned long)(real_mode_header->wakeup_start))
 
 /*
  * Check if the CPU can handle C2 and deeper
index b97596e2b68c7ea3f62eebb38cd1f155719c150e..a6983b2772201c6109060ea25d983499f0281531 100644 (file)
@@ -15,6 +15,8 @@
 #include <linux/compiler.h>
 #include <asm/alternative.h>
 
+#define BIT_64(n)                      (U64_C(1) << (n))
+
 /*
  * These have to be done with inline assembly: that way the bit-setting
  * is guaranteed to be atomic. All bit operations return 0 if the bit
index effff47a3c8280fe4d0b5979c433a8400d570129..43876f16caf1ca8981288089d57b362e017bafbf 100644 (file)
@@ -31,6 +31,56 @@ static inline void native_set_pte(pte_t *ptep, pte_t pte)
        ptep->pte_low = pte.pte_low;
 }
 
+#define pmd_read_atomic pmd_read_atomic
+/*
+ * pte_offset_map_lock on 32bit PAE kernels was reading the pmd_t with
+ * a "*pmdp" dereference done by gcc. Problem is, in certain places
+ * where pte_offset_map_lock is called, concurrent page faults are
+ * allowed, if the mmap_sem is hold for reading. An example is mincore
+ * vs page faults vs MADV_DONTNEED. On the page fault side
+ * pmd_populate rightfully does a set_64bit, but if we're reading the
+ * pmd_t with a "*pmdp" on the mincore side, a SMP race can happen
+ * because gcc will not read the 64bit of the pmd atomically. To fix
+ * this all places running pmd_offset_map_lock() while holding the
+ * mmap_sem in read mode, shall read the pmdp pointer using this
+ * function to know if the pmd is null nor not, and in turn to know if
+ * they can run pmd_offset_map_lock or pmd_trans_huge or other pmd
+ * operations.
+ *
+ * Without THP if the mmap_sem is hold for reading, the
+ * pmd can only transition from null to not null while pmd_read_atomic runs.
+ * So there's no need of literally reading it atomically.
+ *
+ * With THP if the mmap_sem is hold for reading, the pmd can become
+ * THP or null or point to a pte (and in turn become "stable") at any
+ * time under pmd_read_atomic, so it's mandatory to read it atomically
+ * with cmpxchg8b.
+ */
+#ifndef CONFIG_TRANSPARENT_HUGEPAGE
+static inline pmd_t pmd_read_atomic(pmd_t *pmdp)
+{
+       pmdval_t ret;
+       u32 *tmp = (u32 *)pmdp;
+
+       ret = (pmdval_t) (*tmp);
+       if (ret) {
+               /*
+                * If the low part is null, we must not read the high part
+                * or we can end up with a partial pmd.
+                */
+               smp_rmb();
+               ret |= ((pmdval_t)*(tmp + 1)) << 32;
+       }
+
+       return (pmd_t) { ret };
+}
+#else /* CONFIG_TRANSPARENT_HUGEPAGE */
+static inline pmd_t pmd_read_atomic(pmd_t *pmdp)
+{
+       return (pmd_t) { atomic64_read((atomic64_t *)pmdp) };
+}
+#endif /* CONFIG_TRANSPARENT_HUGEPAGE */
+
 static inline void native_set_pte_atomic(pte_t *ptep, pte_t pte)
 {
        set_64bit((unsigned long long *)(ptep), native_pte_val(pte));
index 99f262e04b91b6d553fd65bd61957bd9cb5cbd15..8e525059e7d81c0a4cd46dfa2f62695daba80fee 100644 (file)
@@ -10,9 +10,6 @@
 typedef unsigned short __kernel_mode_t;
 #define __kernel_mode_t __kernel_mode_t
 
-typedef unsigned short __kernel_nlink_t;
-#define __kernel_nlink_t __kernel_nlink_t
-
 typedef unsigned short __kernel_ipc_pid_t;
 #define __kernel_ipc_pid_t __kernel_ipc_pid_t
 
index 7745b257f035a8b1438078c670ce49dc1613051f..39bc5777211a63b9fbdd56a43fd7a0183e51697a 100644 (file)
@@ -544,13 +544,16 @@ static inline void load_sp0(struct tss_struct *tss,
  * enable), so that any CPU's that boot up
  * after us can get the correct flags.
  */
-extern unsigned long           mmu_cr4_features;
+extern unsigned long mmu_cr4_features;
+extern u32 *trampoline_cr4_features;
 
 static inline void set_in_cr4(unsigned long mask)
 {
        unsigned long cr4;
 
        mmu_cr4_features |= mask;
+       if (trampoline_cr4_features)
+               *trampoline_cr4_features = mmu_cr4_features;
        cr4 = read_cr4();
        cr4 |= mask;
        write_cr4(cr4);
@@ -561,6 +564,8 @@ static inline void clear_in_cr4(unsigned long mask)
        unsigned long cr4;
 
        mmu_cr4_features &= ~mask;
+       if (trampoline_cr4_features)
+               *trampoline_cr4_features = mmu_cr4_features;
        cr4 = read_cr4();
        cr4 &= ~mask;
        write_cr4(cr4);
diff --git a/arch/x86/include/asm/realmode.h b/arch/x86/include/asm/realmode.h
new file mode 100644 (file)
index 0000000..fce3f4a
--- /dev/null
@@ -0,0 +1,62 @@
+#ifndef _ARCH_X86_REALMODE_H
+#define _ARCH_X86_REALMODE_H
+
+#include <linux/types.h>
+#include <asm/io.h>
+
+/* This must match data at realmode.S */
+struct real_mode_header {
+       u32     text_start;
+       u32     ro_end;
+       /* SMP trampoline */
+       u32     trampoline_start;
+       u32     trampoline_status;
+       u32     trampoline_header;
+#ifdef CONFIG_X86_64
+       u32     trampoline_pgd;
+#endif
+       /* ACPI S3 wakeup */
+#ifdef CONFIG_ACPI_SLEEP
+       u32     wakeup_start;
+       u32     wakeup_header;
+#endif
+       /* APM/BIOS reboot */
+#ifdef CONFIG_X86_32
+       u32     machine_real_restart_asm;
+#endif
+};
+
+/* This must match data at trampoline_32/64.S */
+struct trampoline_header {
+#ifdef CONFIG_X86_32
+       u32 start;
+       u16 gdt_pad;
+       u16 gdt_limit;
+       u32 gdt_base;
+#else
+       u64 start;
+       u64 efer;
+       u32 cr4;
+#endif
+};
+
+extern struct real_mode_header *real_mode_header;
+extern unsigned char real_mode_blob_end[];
+
+extern unsigned long init_rsp;
+extern unsigned long initial_code;
+extern unsigned long initial_gs;
+
+extern unsigned char real_mode_blob[];
+extern unsigned char real_mode_relocs[];
+
+#ifdef CONFIG_X86_32
+extern unsigned char startup_32_smp[];
+extern unsigned char boot_gdt[];
+#else
+extern unsigned char secondary_startup_64[];
+#endif
+
+extern void __init setup_real_mode(void);
+
+#endif /* _ARCH_X86_REALMODE_H */
index ada93b3b8c66fb79f5e3a9fe91d9f15d67234edf..beff97f7df3790d04dfba1906fe657b1d024e579 100644 (file)
@@ -7,8 +7,6 @@
 
 #include <asm/processor-flags.h>
 
-#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
-
 #define __FIX_EFLAGS   (X86_EFLAGS_AC | X86_EFLAGS_OF | \
                         X86_EFLAGS_DF | X86_EFLAGS_TF | X86_EFLAGS_SF | \
                         X86_EFLAGS_ZF | X86_EFLAGS_AF | X86_EFLAGS_PF | \
diff --git a/arch/x86/include/asm/sta2x11.h b/arch/x86/include/asm/sta2x11.h
new file mode 100644 (file)
index 0000000..e9d32df
--- /dev/null
@@ -0,0 +1,12 @@
+/*
+ * Header file for STMicroelectronics ConneXt (STA2X11) IOHub
+ */
+#ifndef __ASM_STA2X11_H
+#define __ASM_STA2X11_H
+
+#include <linux/pci.h>
+
+/* This needs to be called from the MFD to configure its sub-devices */
+struct sta2x11_instance *sta2x11_get_instance(struct pci_dev *pdev);
+
+#endif /* __ASM_STA2X11_H */
index 5c25de07cba82fca1fbd027a3038f5f6cedf66ec..89f794f007ec1e4aa5bbd029bcb32182fffe1f48 100644 (file)
@@ -248,7 +248,23 @@ static inline void set_restore_sigmask(void)
 {
        struct thread_info *ti = current_thread_info();
        ti->status |= TS_RESTORE_SIGMASK;
-       set_bit(TIF_SIGPENDING, (unsigned long *)&ti->flags);
+       WARN_ON(!test_bit(TIF_SIGPENDING, (unsigned long *)&ti->flags));
+}
+static inline void clear_restore_sigmask(void)
+{
+       current_thread_info()->status &= ~TS_RESTORE_SIGMASK;
+}
+static inline bool test_restore_sigmask(void)
+{
+       return current_thread_info()->status & TS_RESTORE_SIGMASK;
+}
+static inline bool test_and_clear_restore_sigmask(void)
+{
+       struct thread_info *ti = current_thread_info();
+       if (!(ti->status & TS_RESTORE_SIGMASK))
+               return false;
+       ti->status &= ~TS_RESTORE_SIGMASK;
+       return true;
 }
 
 static inline bool is_ia32_task(void)
diff --git a/arch/x86/include/asm/trampoline.h b/arch/x86/include/asm/trampoline.h
deleted file mode 100644 (file)
index feca311..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-#ifndef _ASM_X86_TRAMPOLINE_H
-#define _ASM_X86_TRAMPOLINE_H
-
-#ifndef __ASSEMBLY__
-
-#include <linux/types.h>
-#include <asm/io.h>
-
-/*
- * Trampoline 80x86 program as an array.  These are in the init rodata
- * segment, but that's okay, because we only care about the relative
- * addresses of the symbols.
- */
-extern const unsigned char x86_trampoline_start [];
-extern const unsigned char x86_trampoline_end   [];
-extern unsigned char *x86_trampoline_base;
-
-extern unsigned long init_rsp;
-extern unsigned long initial_code;
-extern unsigned long initial_gs;
-
-extern void __init setup_trampolines(void);
-
-extern const unsigned char trampoline_data[];
-extern const unsigned char trampoline_status[];
-
-#define TRAMPOLINE_SYM(x)                                              \
-       ((void *)(x86_trampoline_base +                                 \
-                 ((const unsigned char *)(x) - x86_trampoline_start)))
-
-/* Address of the SMP trampoline */
-static inline unsigned long trampoline_address(void)
-{
-       return virt_to_phys(TRAMPOLINE_SYM(trampoline_data));
-}
-
-#endif /* __ASSEMBLY__ */
-
-#endif /* _ASM_X86_TRAMPOLINE_H */
index 9bba5b79902b92c7d3d8ab45f70b64d55bc5ded3..8215e5652d9747b6a7eb5302abca77446933acf3 100644 (file)
@@ -35,7 +35,6 @@ obj-y                 += tsc.o io_delay.o rtc.o
 obj-y                  += pci-iommu_table.o
 obj-y                  += resource.o
 
-obj-y                          += trampoline.o trampoline_$(BITS).o
 obj-y                          += process.o
 obj-y                          += i387.o xsave.o
 obj-y                          += ptrace.o
@@ -48,7 +47,6 @@ obj-$(CONFIG_STACKTRACE)      += stacktrace.o
 obj-y                          += cpu/
 obj-y                          += acpi/
 obj-y                          += reboot.o
-obj-$(CONFIG_X86_32)           += reboot_32.o
 obj-$(CONFIG_X86_MSR)          += msr.o
 obj-$(CONFIG_X86_CPUID)                += cpuid.o
 obj-$(CONFIG_PCI)              += early-quirks.o
index 6f35260bb3ef30ac4c05e1f7beda99450a753739..163b225814728721ff0d424d8407493a6d92430d 100644 (file)
@@ -1,14 +1,7 @@
-subdir-                                := realmode
-
 obj-$(CONFIG_ACPI)             += boot.o
-obj-$(CONFIG_ACPI_SLEEP)       += sleep.o wakeup_rm.o wakeup_$(BITS).o
+obj-$(CONFIG_ACPI_SLEEP)       += sleep.o wakeup_$(BITS).o
 
 ifneq ($(CONFIG_ACPI_PROCESSOR),)
 obj-y                          += cstate.o
 endif
 
-$(obj)/wakeup_rm.o:    $(obj)/realmode/wakeup.bin
-
-$(obj)/realmode/wakeup.bin: FORCE
-       $(Q)$(MAKE) $(build)=$(obj)/realmode
-
diff --git a/arch/x86/kernel/acpi/realmode/.gitignore b/arch/x86/kernel/acpi/realmode/.gitignore
deleted file mode 100644 (file)
index 58f1f48..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-wakeup.bin
-wakeup.elf
-wakeup.lds
diff --git a/arch/x86/kernel/acpi/realmode/Makefile b/arch/x86/kernel/acpi/realmode/Makefile
deleted file mode 100644 (file)
index 6a564ac..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-#
-# arch/x86/kernel/acpi/realmode/Makefile
-#
-# This file is subject to the terms and conditions of the GNU General Public
-# License.  See the file "COPYING" in the main directory of this archive
-# for more details.
-#
-
-always         := wakeup.bin
-targets                := wakeup.elf wakeup.lds
-
-wakeup-y       += wakeup.o wakemain.o video-mode.o copy.o bioscall.o regs.o
-
-# The link order of the video-*.o modules can matter.  In particular,
-# video-vga.o *must* be listed first, followed by video-vesa.o.
-# Hardware-specific drivers should follow in the order they should be
-# probed, and video-bios.o should typically be last.
-wakeup-y       += video-vga.o
-wakeup-y       += video-vesa.o
-wakeup-y       += video-bios.o
-
-targets                += $(wakeup-y)
-
-bootsrc                := $(src)/../../../boot
-
-# ---------------------------------------------------------------------------
-
-# How to compile the 16-bit code.  Note we always compile for -march=i386,
-# that way we can complain to the user if the CPU is insufficient.
-# Compile with _SETUP since this is similar to the boot-time setup code.
-KBUILD_CFLAGS  := $(LINUXINCLUDE) -g -Os -D_SETUP -D_WAKEUP -D__KERNEL__ \
-                  -I$(srctree)/$(bootsrc) \
-                  $(cflags-y) \
-                  -Wall -Wstrict-prototypes \
-                  -march=i386 -mregparm=3 \
-                  -include $(srctree)/$(bootsrc)/code16gcc.h \
-                  -fno-strict-aliasing -fomit-frame-pointer \
-                  $(call cc-option, -ffreestanding) \
-                  $(call cc-option, -fno-toplevel-reorder,\
-                       $(call cc-option, -fno-unit-at-a-time)) \
-                  $(call cc-option, -fno-stack-protector) \
-                  $(call cc-option, -mpreferred-stack-boundary=2)
-KBUILD_CFLAGS  += $(call cc-option, -m32)
-KBUILD_AFLAGS  := $(KBUILD_CFLAGS) -D__ASSEMBLY__
-GCOV_PROFILE := n
-
-WAKEUP_OBJS = $(addprefix $(obj)/,$(wakeup-y))
-
-LDFLAGS_wakeup.elf     := -T
-
-CPPFLAGS_wakeup.lds += -P -C
-
-$(obj)/wakeup.elf: $(obj)/wakeup.lds $(WAKEUP_OBJS) FORCE
-       $(call if_changed,ld)
-
-OBJCOPYFLAGS_wakeup.bin        := -O binary
-
-$(obj)/wakeup.bin: $(obj)/wakeup.elf FORCE
-       $(call if_changed,objcopy)
diff --git a/arch/x86/kernel/acpi/realmode/bioscall.S b/arch/x86/kernel/acpi/realmode/bioscall.S
deleted file mode 100644 (file)
index f51eb0b..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../../boot/bioscall.S"
diff --git a/arch/x86/kernel/acpi/realmode/copy.S b/arch/x86/kernel/acpi/realmode/copy.S
deleted file mode 100644 (file)
index dc59ebe..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../../boot/copy.S"
diff --git a/arch/x86/kernel/acpi/realmode/regs.c b/arch/x86/kernel/acpi/realmode/regs.c
deleted file mode 100644 (file)
index 6206033..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../../boot/regs.c"
diff --git a/arch/x86/kernel/acpi/realmode/video-bios.c b/arch/x86/kernel/acpi/realmode/video-bios.c
deleted file mode 100644 (file)
index 7deabc1..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../../boot/video-bios.c"
diff --git a/arch/x86/kernel/acpi/realmode/video-mode.c b/arch/x86/kernel/acpi/realmode/video-mode.c
deleted file mode 100644 (file)
index 328ad20..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../../boot/video-mode.c"
diff --git a/arch/x86/kernel/acpi/realmode/video-vesa.c b/arch/x86/kernel/acpi/realmode/video-vesa.c
deleted file mode 100644 (file)
index 9dbb967..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../../boot/video-vesa.c"
diff --git a/arch/x86/kernel/acpi/realmode/video-vga.c b/arch/x86/kernel/acpi/realmode/video-vga.c
deleted file mode 100644 (file)
index bcc8125..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../../boot/video-vga.c"
diff --git a/arch/x86/kernel/acpi/realmode/wakemain.c b/arch/x86/kernel/acpi/realmode/wakemain.c
deleted file mode 100644 (file)
index 883962d..0000000
+++ /dev/null
@@ -1,81 +0,0 @@
-#include "wakeup.h"
-#include "boot.h"
-
-static void udelay(int loops)
-{
-       while (loops--)
-               io_delay();     /* Approximately 1 us */
-}
-
-static void beep(unsigned int hz)
-{
-       u8 enable;
-
-       if (!hz) {
-               enable = 0x00;          /* Turn off speaker */
-       } else {
-               u16 div = 1193181/hz;
-
-               outb(0xb6, 0x43);       /* Ctr 2, squarewave, load, binary */
-               io_delay();
-               outb(div, 0x42);        /* LSB of counter */
-               io_delay();
-               outb(div >> 8, 0x42);   /* MSB of counter */
-               io_delay();
-
-               enable = 0x03;          /* Turn on speaker */
-       }
-       inb(0x61);              /* Dummy read of System Control Port B */
-       io_delay();
-       outb(enable, 0x61);     /* Enable timer 2 output to speaker */
-       io_delay();
-}
-
-#define DOT_HZ         880
-#define DASH_HZ                587
-#define US_PER_DOT     125000
-
-/* Okay, this is totally silly, but it's kind of fun. */
-static void send_morse(const char *pattern)
-{
-       char s;
-
-       while ((s = *pattern++)) {
-               switch (s) {
-               case '.':
-                       beep(DOT_HZ);
-                       udelay(US_PER_DOT);
-                       beep(0);
-                       udelay(US_PER_DOT);
-                       break;
-               case '-':
-                       beep(DASH_HZ);
-                       udelay(US_PER_DOT * 3);
-                       beep(0);
-                       udelay(US_PER_DOT);
-                       break;
-               default:        /* Assume it's a space */
-                       udelay(US_PER_DOT * 3);
-                       break;
-               }
-       }
-}
-
-void main(void)
-{
-       /* Kill machine if structures are wrong */
-       if (wakeup_header.real_magic != 0x12345678)
-               while (1);
-
-       if (wakeup_header.realmode_flags & 4)
-               send_morse("...-");
-
-       if (wakeup_header.realmode_flags & 1)
-               asm volatile("lcallw   $0xc000,$3");
-
-       if (wakeup_header.realmode_flags & 2) {
-               /* Need to call BIOS */
-               probe_cards(0);
-               set_mode(wakeup_header.video_mode);
-       }
-}
diff --git a/arch/x86/kernel/acpi/realmode/wakeup.S b/arch/x86/kernel/acpi/realmode/wakeup.S
deleted file mode 100644 (file)
index b4fd836..0000000
+++ /dev/null
@@ -1,170 +0,0 @@
-/*
- * ACPI wakeup real mode startup stub
- */
-#include <asm/segment.h>
-#include <asm/msr-index.h>
-#include <asm/page_types.h>
-#include <asm/pgtable_types.h>
-#include <asm/processor-flags.h>
-#include "wakeup.h"
-
-       .code16
-       .section ".jump", "ax"
-       .globl  _start
-_start:
-       cli
-       jmp     wakeup_code
-
-/* This should match the structure in wakeup.h */
-               .section ".header", "a"
-               .globl  wakeup_header
-wakeup_header:
-video_mode:    .short  0       /* Video mode number */
-pmode_return:  .byte   0x66, 0xea      /* ljmpl */
-               .long   0       /* offset goes here */
-               .short  __KERNEL_CS
-pmode_cr0:     .long   0       /* Saved %cr0 */
-pmode_cr3:     .long   0       /* Saved %cr3 */
-pmode_cr4:     .long   0       /* Saved %cr4 */
-pmode_efer:    .quad   0       /* Saved EFER */
-pmode_gdt:     .quad   0
-pmode_misc_en: .quad   0       /* Saved MISC_ENABLE MSR */
-pmode_behavior:        .long   0       /* Wakeup behavior flags */
-realmode_flags:        .long   0
-real_magic:    .long   0
-trampoline_segment:    .word 0
-_pad1:         .byte   0
-wakeup_jmp:    .byte   0xea    /* ljmpw */
-wakeup_jmp_off:        .word   3f
-wakeup_jmp_seg:        .word   0
-wakeup_gdt:    .quad   0, 0, 0
-signature:     .long   WAKEUP_HEADER_SIGNATURE
-
-       .text
-       .code16
-wakeup_code:
-       cld
-
-       /* Apparently some dimwit BIOS programmers don't know how to
-          program a PM to RM transition, and we might end up here with
-          junk in the data segment descriptor registers.  The only way
-          to repair that is to go into PM and fix it ourselves... */
-       movw    $16, %cx
-       lgdtl   %cs:wakeup_gdt
-       movl    %cr0, %eax
-       orb     $X86_CR0_PE, %al
-       movl    %eax, %cr0
-       jmp     1f
-1:     ljmpw   $8, $2f
-2:
-       movw    %cx, %ds
-       movw    %cx, %es
-       movw    %cx, %ss
-       movw    %cx, %fs
-       movw    %cx, %gs
-
-       andb    $~X86_CR0_PE, %al
-       movl    %eax, %cr0
-       jmp     wakeup_jmp
-3:
-       /* Set up segments */
-       movw    %cs, %ax
-       movw    %ax, %ds
-       movw    %ax, %es
-       movw    %ax, %ss
-       lidtl   wakeup_idt
-
-       movl    $wakeup_stack_end, %esp
-
-       /* Clear the EFLAGS */
-       pushl   $0
-       popfl
-
-       /* Check header signature... */
-       movl    signature, %eax
-       cmpl    $WAKEUP_HEADER_SIGNATURE, %eax
-       jne     bogus_real_magic
-
-       /* Check we really have everything... */
-       movl    end_signature, %eax
-       cmpl    $WAKEUP_END_SIGNATURE, %eax
-       jne     bogus_real_magic
-
-       /* Call the C code */
-       calll   main
-
-       /* Restore MISC_ENABLE before entering protected mode, in case
-          BIOS decided to clear XD_DISABLE during S3. */
-       movl    pmode_behavior, %eax
-       btl     $WAKEUP_BEHAVIOR_RESTORE_MISC_ENABLE, %eax
-       jnc     1f
-
-       movl    pmode_misc_en, %eax
-       movl    pmode_misc_en + 4, %edx
-       movl    $MSR_IA32_MISC_ENABLE, %ecx
-       wrmsr
-1:
-
-       /* Do any other stuff... */
-
-#ifndef CONFIG_64BIT
-       /* This could also be done in C code... */
-       movl    pmode_cr3, %eax
-       movl    %eax, %cr3
-
-       movl    pmode_cr4, %ecx
-       jecxz   1f
-       movl    %ecx, %cr4
-1:
-       movl    pmode_efer, %eax
-       movl    pmode_efer + 4, %edx
-       movl    %eax, %ecx
-       orl     %edx, %ecx
-       jz      1f
-       movl    $MSR_EFER, %ecx
-       wrmsr
-1:
-
-       lgdtl   pmode_gdt
-
-       /* This really couldn't... */
-       movl    pmode_cr0, %eax
-       movl    %eax, %cr0
-       jmp     pmode_return
-#else
-       pushw   $0
-       pushw   trampoline_segment
-       pushw   $0
-       lret
-#endif
-
-bogus_real_magic:
-1:
-       hlt
-       jmp     1b
-
-       .data
-       .balign 8
-
-       /* This is the standard real-mode IDT */
-wakeup_idt:
-       .word   0xffff          /* limit */
-       .long   0               /* address */
-       .word   0
-
-       .globl  HEAP, heap_end
-HEAP:
-       .long   wakeup_heap
-heap_end:
-       .long   wakeup_stack
-
-       .bss
-wakeup_heap:
-       .space  2048
-wakeup_stack:
-       .space  2048
-wakeup_stack_end:
-
-       .section ".signature","a"
-end_signature:
-       .long   WAKEUP_END_SIGNATURE
diff --git a/arch/x86/kernel/acpi/realmode/wakeup.h b/arch/x86/kernel/acpi/realmode/wakeup.h
deleted file mode 100644 (file)
index 97a29e1..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Definitions for the wakeup data structure at the head of the
- * wakeup code.
- */
-
-#ifndef ARCH_X86_KERNEL_ACPI_RM_WAKEUP_H
-#define ARCH_X86_KERNEL_ACPI_RM_WAKEUP_H
-
-#ifndef __ASSEMBLY__
-#include <linux/types.h>
-
-/* This must match data at wakeup.S */
-struct wakeup_header {
-       u16 video_mode;         /* Video mode number */
-       u16 _jmp1;              /* ljmpl opcode, 32-bit only */
-       u32 pmode_entry;        /* Protected mode resume point, 32-bit only */
-       u16 _jmp2;              /* CS value, 32-bit only */
-       u32 pmode_cr0;          /* Protected mode cr0 */
-       u32 pmode_cr3;          /* Protected mode cr3 */
-       u32 pmode_cr4;          /* Protected mode cr4 */
-       u32 pmode_efer_low;     /* Protected mode EFER */
-       u32 pmode_efer_high;
-       u64 pmode_gdt;
-       u32 pmode_misc_en_low;  /* Protected mode MISC_ENABLE */
-       u32 pmode_misc_en_high;
-       u32 pmode_behavior;     /* Wakeup routine behavior flags */
-       u32 realmode_flags;
-       u32 real_magic;
-       u16 trampoline_segment; /* segment with trampoline code, 64-bit only */
-       u8  _pad1;
-       u8  wakeup_jmp;
-       u16 wakeup_jmp_off;
-       u16 wakeup_jmp_seg;
-       u64 wakeup_gdt[3];
-       u32 signature;          /* To check we have correct structure */
-} __attribute__((__packed__));
-
-extern struct wakeup_header wakeup_header;
-#endif
-
-#define WAKEUP_HEADER_OFFSET   8
-#define WAKEUP_HEADER_SIGNATURE 0x51ee1111
-#define WAKEUP_END_SIGNATURE   0x65a22c82
-
-/* Wakeup behavior bits */
-#define WAKEUP_BEHAVIOR_RESTORE_MISC_ENABLE     0
-
-#endif /* ARCH_X86_KERNEL_ACPI_RM_WAKEUP_H */
diff --git a/arch/x86/kernel/acpi/realmode/wakeup.lds.S b/arch/x86/kernel/acpi/realmode/wakeup.lds.S
deleted file mode 100644 (file)
index d4f8010..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * wakeup.ld
- *
- * Linker script for the real-mode wakeup code
- */
-#undef i386
-#include "wakeup.h"
-
-OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
-OUTPUT_ARCH(i386)
-ENTRY(_start)
-
-SECTIONS
-{
-       . = 0;
-       .jump   : {
-               *(.jump)
-       } = 0x90909090
-
-       . = WAKEUP_HEADER_OFFSET;
-       .header : {
-               *(.header)
-       }
-
-       . = ALIGN(16);
-       .text : {
-                *(.text*)
-       } = 0x90909090
-
-       . = ALIGN(16);
-       .rodata : {
-               *(.rodata*)
-       }
-
-       .videocards : {
-               video_cards = .;
-               *(.videocards)
-               video_cards_end = .;
-       }
-
-       . = ALIGN(16);
-       .data : {
-                *(.data*)
-       }
-
-       . = ALIGN(16);
-       .bss :  {
-               __bss_start = .;
-               *(.bss)
-               __bss_end = .;
-       }
-
-       .signature : {
-               *(.signature)
-       }
-
-       _end = .;
-
-       /DISCARD/ : {
-               *(.note*)
-       }
-}
index 146a49c763a49085b50d5b3b6babcda74650463b..95bf99de9058128f320356caa6cad1ce9b2727da 100644 (file)
@@ -14,8 +14,9 @@
 #include <asm/desc.h>
 #include <asm/pgtable.h>
 #include <asm/cacheflush.h>
+#include <asm/realmode.h>
 
-#include "realmode/wakeup.h"
+#include "../../realmode/rm/wakeup.h"
 #include "sleep.h"
 
 unsigned long acpi_realmode_flags;
@@ -36,13 +37,9 @@ asmlinkage void acpi_enter_s3(void)
  */
 int acpi_suspend_lowlevel(void)
 {
-       struct wakeup_header *header;
-       /* address in low memory of the wakeup routine. */
-       char *acpi_realmode;
+       struct wakeup_header *header =
+               (struct wakeup_header *) __va(real_mode_header->wakeup_header);
 
-       acpi_realmode = TRAMPOLINE_SYM(acpi_wakeup_code);
-
-       header = (struct wakeup_header *)(acpi_realmode + WAKEUP_HEADER_OFFSET);
        if (header->signature != WAKEUP_HEADER_SIGNATURE) {
                printk(KERN_ERR "wakeup header does not match\n");
                return -EINVAL;
@@ -50,27 +47,6 @@ int acpi_suspend_lowlevel(void)
 
        header->video_mode = saved_video_mode;
 
-       header->wakeup_jmp_seg = acpi_wakeup_address >> 4;
-
-       /*
-        * Set up the wakeup GDT.  We set these up as Big Real Mode,
-        * that is, with limits set to 4 GB.  At least the Lenovo
-        * Thinkpad X61 is known to need this for the video BIOS
-        * initialization quirk to work; this is likely to also
-        * be the case for other laptops or integrated video devices.
-        */
-
-       /* GDT[0]: GDT self-pointer */
-       header->wakeup_gdt[0] =
-               (u64)(sizeof(header->wakeup_gdt) - 1) +
-               ((u64)__pa(&header->wakeup_gdt) << 16);
-       /* GDT[1]: big real mode-like code segment */
-       header->wakeup_gdt[1] =
-               GDT_ENTRY(0x809b, acpi_wakeup_address, 0xfffff);
-       /* GDT[2]: big real mode-like data segment */
-       header->wakeup_gdt[2] =
-               GDT_ENTRY(0x8093, acpi_wakeup_address, 0xfffff);
-
 #ifndef CONFIG_64BIT
        store_gdt((struct desc_ptr *)&header->pmode_gdt);
 
@@ -95,7 +71,6 @@ int acpi_suspend_lowlevel(void)
        header->pmode_cr3 = (u32)__pa(&initial_page_table);
        saved_magic = 0x12345678;
 #else /* CONFIG_64BIT */
-       header->trampoline_segment = trampoline_address() >> 4;
 #ifdef CONFIG_SMP
        stack_start = (unsigned long)temp_stack + sizeof(temp_stack);
        early_gdt_descr.address =
index d68677a2a01037a758496ee31aa575ccbb2badff..5653a5791ec92291844a55665cc50a0cb402095b 100644 (file)
@@ -2,8 +2,8 @@
  *     Variables and functions used by the code in sleep.c
  */
 
-#include <asm/trampoline.h>
 #include <linux/linkage.h>
+#include <asm/realmode.h>
 
 extern unsigned long saved_video_mode;
 extern long saved_magic;
diff --git a/arch/x86/kernel/acpi/wakeup_rm.S b/arch/x86/kernel/acpi/wakeup_rm.S
deleted file mode 100644 (file)
index 63b8ab5..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-/*
- * Wrapper script for the realmode binary as a transport object
- * before copying to low memory.
- */
-#include <asm/page_types.h>
-
-       .section ".x86_trampoline","a"
-       .balign PAGE_SIZE
-       .globl  acpi_wakeup_code
-acpi_wakeup_code:
-       .incbin "arch/x86/kernel/acpi/realmode/wakeup.bin"
-       .size   acpi_wakeup_code, .-acpi_wakeup_code
index b772dd6ad45016e5e943ec37cafdef5fac03a152..0a687fd185e6c97b496578d1dc6acb4001c2833d 100644 (file)
@@ -1251,15 +1251,15 @@ void mce_log_therm_throt_event(__u64 status)
  * poller finds an MCE, poll 2x faster.  When the poller finds no more
  * errors, poll 2x slower (up to check_interval seconds).
  */
-static int check_interval = 5 * 60; /* 5 minutes */
+static unsigned long check_interval = 5 * 60; /* 5 minutes */
 
-static DEFINE_PER_CPU(int, mce_next_interval); /* in jiffies */
+static DEFINE_PER_CPU(unsigned long, mce_next_interval); /* in jiffies */
 static DEFINE_PER_CPU(struct timer_list, mce_timer);
 
-static void mce_start_timer(unsigned long data)
+static void mce_timer_fn(unsigned long data)
 {
-       struct timer_list *t = &per_cpu(mce_timer, data);
-       int *n;
+       struct timer_list *t = &__get_cpu_var(mce_timer);
+       unsigned long iv;
 
        WARN_ON(smp_processor_id() != data);
 
@@ -1272,13 +1272,14 @@ static void mce_start_timer(unsigned long data)
         * Alert userspace if needed.  If we logged an MCE, reduce the
         * polling interval, otherwise increase the polling interval.
         */
-       n = &__get_cpu_var(mce_next_interval);
+       iv = __this_cpu_read(mce_next_interval);
        if (mce_notify_irq())
-               *n = max(*n/2, HZ/100);
+               iv = max(iv, (unsigned long) HZ/100);
        else
-               *n = min(*n*2, (int)round_jiffies_relative(check_interval*HZ));
+               iv = min(iv * 2, round_jiffies_relative(check_interval * HZ));
+       __this_cpu_write(mce_next_interval, iv);
 
-       t->expires = jiffies + *n;
+       t->expires = jiffies + iv;
        add_timer_on(t, smp_processor_id());
 }
 
@@ -1472,9 +1473,9 @@ static int __cpuinit __mcheck_cpu_apply_quirks(struct cpuinfo_x86 *c)
                                 rdmsrl(msrs[i], val);
 
                                 /* CntP bit set? */
-                                if (val & BIT(62)) {
-                                        val &= ~BIT(62);
-                                        wrmsrl(msrs[i], val);
+                                if (val & BIT_64(62)) {
+                                       val &= ~BIT_64(62);
+                                       wrmsrl(msrs[i], val);
                                 }
                         }
 
@@ -1556,17 +1557,17 @@ static void __mcheck_cpu_init_vendor(struct cpuinfo_x86 *c)
 static void __mcheck_cpu_init_timer(void)
 {
        struct timer_list *t = &__get_cpu_var(mce_timer);
-       int *n = &__get_cpu_var(mce_next_interval);
+       unsigned long iv = __this_cpu_read(mce_next_interval);
 
-       setup_timer(t, mce_start_timer, smp_processor_id());
+       setup_timer(t, mce_timer_fn, smp_processor_id());
 
        if (mce_ignore_ce)
                return;
 
-       *n = check_interval * HZ;
-       if (!*n)
+       __this_cpu_write(mce_next_interval, iv);
+       if (!iv)
                return;
-       t->expires = round_jiffies(jiffies + *n);
+       t->expires = round_jiffies(jiffies + iv);
        add_timer_on(t, smp_processor_id());
 }
 
@@ -2276,7 +2277,7 @@ mce_cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu)
        case CPU_DOWN_FAILED_FROZEN:
                if (!mce_ignore_ce && check_interval) {
                        t->expires = round_jiffies(jiffies +
-                                          __get_cpu_var(mce_next_interval));
+                                       per_cpu(mce_next_interval, cpu));
                        add_timer_on(t, cpu);
                }
                smp_call_function_single(cpu, mce_reenable_cpu, &action, 1);
index ac140c7be396b6f55c97521ddcd572df664e95b6..bdda2e6c673bf71afb30ed670071a9d02dbdcbfa 100644 (file)
@@ -266,7 +266,7 @@ range_to_mtrr(unsigned int reg, unsigned long range_startk,
                if (align > max_align)
                        align = max_align;
 
-               sizek = 1 << align;
+               sizek = 1UL << align;
                if (debug_print) {
                        char start_factor = 'K', size_factor = 'K';
                        unsigned long start_base, size_base;
index 62d61e9976eb0a83bca3aae5c979bb63505c0bbb..41857970517f795739018c01e1fc070f0a831fff 100644 (file)
@@ -113,7 +113,9 @@ static void __init __e820_add_region(struct e820map *e820x, u64 start, u64 size,
        int x = e820x->nr_map;
 
        if (x >= ARRAY_SIZE(e820x->map)) {
-               printk(KERN_ERR "Ooops! Too many entries in the memory map!\n");
+               printk(KERN_ERR "e820: too many entries; ignoring [mem %#010llx-%#010llx]\n",
+                      (unsigned long long) start,
+                      (unsigned long long) (start + size - 1));
                return;
        }
 
@@ -133,19 +135,19 @@ static void __init e820_print_type(u32 type)
        switch (type) {
        case E820_RAM:
        case E820_RESERVED_KERN:
-               printk(KERN_CONT "(usable)");
+               printk(KERN_CONT "usable");
                break;
        case E820_RESERVED:
-               printk(KERN_CONT "(reserved)");
+               printk(KERN_CONT "reserved");
                break;
        case E820_ACPI:
-               printk(KERN_CONT "(ACPI data)");
+               printk(KERN_CONT "ACPI data");
                break;
        case E820_NVS:
-               printk(KERN_CONT "(ACPI NVS)");
+               printk(KERN_CONT "ACPI NVS");
                break;
        case E820_UNUSABLE:
-               printk(KERN_CONT "(unusable)");
+               printk(KERN_CONT "unusable");
                break;
        default:
                printk(KERN_CONT "type %u", type);
@@ -158,10 +160,10 @@ void __init e820_print_map(char *who)
        int i;
 
        for (i = 0; i < e820.nr_map; i++) {
-               printk(KERN_INFO " %s: %016Lx - %016Lx ", who,
+               printk(KERN_INFO "%s: [mem %#018Lx-%#018Lx] ", who,
                       (unsigned long long) e820.map[i].addr,
                       (unsigned long long)
-                      (e820.map[i].addr + e820.map[i].size));
+                      (e820.map[i].addr + e820.map[i].size - 1));
                e820_print_type(e820.map[i].type);
                printk(KERN_CONT "\n");
        }
@@ -428,9 +430,8 @@ static u64 __init __e820_update_range(struct e820map *e820x, u64 start,
                size = ULLONG_MAX - start;
 
        end = start + size;
-       printk(KERN_DEBUG "e820 update range: %016Lx - %016Lx ",
-                      (unsigned long long) start,
-                      (unsigned long long) end);
+       printk(KERN_DEBUG "e820: update [mem %#010Lx-%#010Lx] ",
+              (unsigned long long) start, (unsigned long long) (end - 1));
        e820_print_type(old_type);
        printk(KERN_CONT " ==> ");
        e820_print_type(new_type);
@@ -509,9 +510,8 @@ u64 __init e820_remove_range(u64 start, u64 size, unsigned old_type,
                size = ULLONG_MAX - start;
 
        end = start + size;
-       printk(KERN_DEBUG "e820 remove range: %016Lx - %016Lx ",
-                      (unsigned long long) start,
-                      (unsigned long long) end);
+       printk(KERN_DEBUG "e820: remove [mem %#010Lx-%#010Lx] ",
+              (unsigned long long) start, (unsigned long long) (end - 1));
        if (checktype)
                e820_print_type(old_type);
        printk(KERN_CONT "\n");
@@ -567,7 +567,7 @@ void __init update_e820(void)
        if (sanitize_e820_map(e820.map, ARRAY_SIZE(e820.map), &nr_map))
                return;
        e820.nr_map = nr_map;
-       printk(KERN_INFO "modified physical RAM map:\n");
+       printk(KERN_INFO "e820: modified physical RAM map:\n");
        e820_print_map("modified");
 }
 static void __init update_e820_saved(void)
@@ -637,8 +637,8 @@ __init void e820_setup_gap(void)
        if (!found) {
                gapstart = (max_pfn << PAGE_SHIFT) + 1024*1024;
                printk(KERN_ERR
-       "PCI: Warning: Cannot find a gap in the 32bit address range\n"
-       "PCI: Unassigned devices with 32bit resource registers may break!\n");
+       "e820: cannot find a gap in the 32bit address range\n"
+       "e820: PCI devices with unassigned 32bit BARs may break!\n");
        }
 #endif
 
@@ -648,8 +648,8 @@ __init void e820_setup_gap(void)
        pci_mem_start = gapstart;
 
        printk(KERN_INFO
-              "Allocating PCI resources starting at %lx (gap: %lx:%lx)\n",
-              pci_mem_start, gapstart, gapsize);
+              "e820: [mem %#010lx-%#010lx] available for PCI devices\n",
+              gapstart, gapstart + gapsize - 1);
 }
 
 /**
@@ -667,7 +667,7 @@ void __init parse_e820_ext(struct setup_data *sdata)
        extmap = (struct e820entry *)(sdata->data);
        __append_e820_map(extmap, entries);
        sanitize_e820_map(e820.map, ARRAY_SIZE(e820.map), &e820.nr_map);
-       printk(KERN_INFO "extended physical RAM map:\n");
+       printk(KERN_INFO "e820: extended physical RAM map:\n");
        e820_print_map("extended");
 }
 
@@ -734,7 +734,7 @@ u64 __init early_reserve_e820(u64 size, u64 align)
        addr = __memblock_alloc_base(size, align, MEMBLOCK_ALLOC_ACCESSIBLE);
        if (addr) {
                e820_update_range_saved(addr, size, E820_RAM, E820_RESERVED);
-               printk(KERN_INFO "update e820_saved for early_reserve_e820\n");
+               printk(KERN_INFO "e820: update e820_saved for early_reserve_e820\n");
                update_e820_saved();
        }
 
@@ -784,7 +784,7 @@ static unsigned long __init e820_end_pfn(unsigned long limit_pfn, unsigned type)
        if (last_pfn > max_arch_pfn)
                last_pfn = max_arch_pfn;
 
-       printk(KERN_INFO "last_pfn = %#lx max_arch_pfn = %#lx\n",
+       printk(KERN_INFO "e820: last_pfn = %#lx max_arch_pfn = %#lx\n",
                         last_pfn, max_arch_pfn);
        return last_pfn;
 }
@@ -888,7 +888,7 @@ void __init finish_e820_parsing(void)
                        early_panic("Invalid user supplied memory map");
                e820.nr_map = nr;
 
-               printk(KERN_INFO "user-defined physical RAM map:\n");
+               printk(KERN_INFO "e820: user-defined physical RAM map:\n");
                e820_print_map("user");
        }
 }
@@ -996,8 +996,9 @@ void __init e820_reserve_resources_late(void)
                        end = MAX_RESOURCE_SIZE;
                if (start >= end)
                        continue;
-               printk(KERN_DEBUG "reserve RAM buffer: %016llx - %016llx ",
-                              start, end);
+               printk(KERN_DEBUG
+                      "e820: reserve RAM buffer [mem %#010llx-%#010llx]\n",
+                      start, end);
                reserve_region_with_split(&iomem_resource, start, end,
                                          "RAM buffer");
        }
@@ -1047,7 +1048,7 @@ void __init setup_memory_map(void)
 
        who = x86_init.resources.memory_setup();
        memcpy(&e820_saved, &e820, sizeof(struct e820map));
-       printk(KERN_INFO "BIOS-provided physical RAM map:\n");
+       printk(KERN_INFO "e820: BIOS-provided physical RAM map:\n");
        e820_print_map(who);
 }
 
index 01ccf9b71473ce18ffe0cccf5fa09fd379dcf4cf..623f288374763286ec9e58aa66ecabb4e3fc2f6b 100644 (file)
@@ -316,7 +316,6 @@ ret_from_exception:
        preempt_stop(CLBR_ANY)
 ret_from_intr:
        GET_THREAD_INFO(%ebp)
-resume_userspace_sig:
 #ifdef CONFIG_VM86
        movl PT_EFLAGS(%esp), %eax      # mix EFLAGS and CS
        movb PT_CS(%esp), %al
@@ -615,9 +614,13 @@ work_notifysig:                            # deal with pending signals and
                                        # vm86-space
        TRACE_IRQS_ON
        ENABLE_INTERRUPTS(CLBR_NONE)
+       movb PT_CS(%esp), %bl
+       andb $SEGMENT_RPL_MASK, %bl
+       cmpb $USER_RPL, %bl
+       jb resume_kernel
        xorl %edx, %edx
        call do_notify_resume
-       jmp resume_userspace_sig
+       jmp resume_userspace
 
        ALIGN
 work_notifysig_v86:
@@ -630,9 +633,13 @@ work_notifysig_v86:
 #endif
        TRACE_IRQS_ON
        ENABLE_INTERRUPTS(CLBR_NONE)
+       movb PT_CS(%esp), %bl
+       andb $SEGMENT_RPL_MASK, %bl
+       cmpb $USER_RPL, %bl
+       jb resume_kernel
        xorl %edx, %edx
        call do_notify_resume
-       jmp resume_userspace_sig
+       jmp resume_userspace
 END(work_pending)
 
        # perform syscall exit tracing
index 51ff18616d50be24bc61fceef68ef43ccbb4becb..c18f59d10101cefc82f6638bcca758a480b70cc3 100644 (file)
@@ -14,7 +14,6 @@
 #include <asm/sections.h>
 #include <asm/e820.h>
 #include <asm/page.h>
-#include <asm/trampoline.h>
 #include <asm/apic.h>
 #include <asm/io_apic.h>
 #include <asm/bios_ebda.h>
index 3a3b779f41d320b81443615eb62a2ddadd0e5061..037df57a99ac34d5ba1a5cab5abaa4554e0689d0 100644 (file)
@@ -24,7 +24,6 @@
 #include <asm/sections.h>
 #include <asm/kdebug.h>
 #include <asm/e820.h>
-#include <asm/trampoline.h>
 #include <asm/bios_ebda.h>
 
 static void __init zap_identity_mappings(void)
index 463c9797ca6ab5f392bc65c6fbe1c1ddfc699c18..d42ab17b739729c4b27d3f3fe02fdea2633f2f5a 100644 (file)
@@ -274,10 +274,7 @@ num_subarch_entries = (. - subarch_entries) / 4
  * If cpu hotplug is not supported then this code can go in init section
  * which will be freed later
  */
-
 __CPUINIT
-
-#ifdef CONFIG_SMP
 ENTRY(startup_32_smp)
        cld
        movl $(__BOOT_DS),%eax
@@ -288,7 +285,7 @@ ENTRY(startup_32_smp)
        movl pa(stack_start),%ecx
        movl %eax,%ss
        leal -__PAGE_OFFSET(%ecx),%esp
-#endif /* CONFIG_SMP */
+
 default_entry:
 
 /*
index 7a40f2447321d5c6af500be479844ac6379a98b3..94bf9cc2c7ee5f380e28246182e00e06f64ff573 100644 (file)
@@ -139,10 +139,6 @@ ident_complete:
        /* Fixup phys_base */
        addq    %rbp, phys_base(%rip)
 
-       /* Fixup trampoline */
-       addq    %rbp, trampoline_level4_pgt + 0(%rip)
-       addq    %rbp, trampoline_level4_pgt + (511*8)(%rip)
-
        /* Due to ENTRY(), sometimes the empty space gets filled with
         * zeros. Better take a jmp than relying on empty space being
         * filled with 0x90 (nop)
index 9cc7b4392f7c8b0462ad4d031667e5197eff9373..1460a5df92f7a7f314ed0be95a81765cf2df676e 100644 (file)
@@ -870,7 +870,7 @@ int __init hpet_enable(void)
        else
                pr_warn("HPET initial state will not be saved\n");
        cfg &= ~(HPET_CFG_ENABLE | HPET_CFG_LEGACY);
-       hpet_writel(cfg, HPET_Tn_CFG(i));
+       hpet_writel(cfg, HPET_CFG);
        if (cfg)
                pr_warn("HPET: Unrecognized bits %#x set in global cfg\n",
                        cfg);
index b02d4dd6b8a304e0bbd0d3d2d6a62e470d01bc2a..d2b56489d70fb12781e6787c98b1ea13f7d08705 100644 (file)
@@ -27,7 +27,6 @@
 #include <asm/proto.h>
 #include <asm/bios_ebda.h>
 #include <asm/e820.h>
-#include <asm/trampoline.h>
 #include <asm/setup.h>
 #include <asm/smp.h>
 
@@ -568,8 +567,8 @@ static int __init smp_scan_config(unsigned long base, unsigned long length)
        struct mpf_intel *mpf;
        unsigned long mem;
 
-       apic_printk(APIC_VERBOSE, "Scan SMP from %p for %ld bytes.\n",
-                       bp, length);
+       apic_printk(APIC_VERBOSE, "Scan for SMP in [mem %#010lx-%#010lx]\n",
+                   base, base + length - 1);
        BUILD_BUG_ON(sizeof(*mpf) != 16);
 
        while (length > 0) {
@@ -584,8 +583,10 @@ static int __init smp_scan_config(unsigned long base, unsigned long length)
 #endif
                        mpf_found = mpf;
 
-                       printk(KERN_INFO "found SMP MP-table at [%p] %llx\n",
-                              mpf, (u64)virt_to_phys(mpf));
+                       printk(KERN_INFO "found SMP MP-table at [mem %#010llx-%#010llx] mapped at [%p]\n",
+                              (unsigned long long) virt_to_phys(mpf),
+                              (unsigned long long) virt_to_phys(mpf) +
+                              sizeof(*mpf) - 1, mpf);
 
                        mem = virt_to_phys(mpf);
                        memblock_reserve(mem, sizeof(*mpf));
index 77215c23fba1250dc9b45085e36d99d7356358b7..79c45af81604c7e191116c23b3625c80bb81476d 100644 (file)
@@ -24,6 +24,7 @@
 #ifdef CONFIG_X86_32
 # include <linux/ctype.h>
 # include <linux/mc146818rtc.h>
+# include <asm/realmode.h>
 #else
 # include <asm/x86_init.h>
 #endif
@@ -156,15 +157,10 @@ static int __init set_bios_reboot(const struct dmi_system_id *d)
        return 0;
 }
 
-extern const unsigned char machine_real_restart_asm[];
-extern const u64 machine_real_restart_gdt[3];
-
 void machine_real_restart(unsigned int type)
 {
-       void *restart_va;
-       unsigned long restart_pa;
-       void (*restart_lowmem)(unsigned int);
-       u64 *lowmem_gdt;
+       void (*restart_lowmem)(unsigned int) = (void (*)(unsigned int))
+               real_mode_header->machine_real_restart_asm;
 
        local_irq_disable();
 
@@ -195,21 +191,6 @@ void machine_real_restart(unsigned int type)
         * too. */
        *((unsigned short *)0x472) = reboot_mode;
 
-       /* Patch the GDT in the low memory trampoline */
-       lowmem_gdt = TRAMPOLINE_SYM(machine_real_restart_gdt);
-
-       restart_va = TRAMPOLINE_SYM(machine_real_restart_asm);
-       restart_pa = virt_to_phys(restart_va);
-       restart_lowmem = (void (*)(unsigned int))restart_pa;
-
-       /* GDT[0]: GDT self-pointer */
-       lowmem_gdt[0] =
-               (u64)(sizeof(machine_real_restart_gdt) - 1) +
-               ((u64)virt_to_phys(lowmem_gdt) << 16);
-       /* GDT[1]: 64K real mode code segment */
-       lowmem_gdt[1] =
-               GDT_ENTRY(0x009b, restart_pa, 0xffff);
-
        /* Jump to the identity-mapped low memory code */
        restart_lowmem(type);
 }
diff --git a/arch/x86/kernel/reboot_32.S b/arch/x86/kernel/reboot_32.S
deleted file mode 100644 (file)
index 1d5c46d..0000000
+++ /dev/null
@@ -1,135 +0,0 @@
-#include <linux/linkage.h>
-#include <linux/init.h>
-#include <asm/segment.h>
-#include <asm/page_types.h>
-
-/*
- * The following code and data reboots the machine by switching to real
- * mode and jumping to the BIOS reset entry point, as if the CPU has
- * really been reset.  The previous version asked the keyboard
- * controller to pulse the CPU reset line, which is more thorough, but
- * doesn't work with at least one type of 486 motherboard.  It is easy
- * to stop this code working; hence the copious comments.
- *
- * This code is called with the restart type (0 = BIOS, 1 = APM) in %eax.
- */
-       .section ".x86_trampoline","a"
-       .balign 16
-       .code32
-ENTRY(machine_real_restart_asm)
-r_base = .
-       /* Get our own relocated address */
-       call    1f
-1:     popl    %ebx
-       subl    $(1b - r_base), %ebx
-
-       /* Compute the equivalent real-mode segment */
-       movl    %ebx, %ecx
-       shrl    $4, %ecx
-       
-       /* Patch post-real-mode segment jump */
-       movw    (dispatch_table - r_base)(%ebx,%eax,2),%ax
-       movw    %ax, (101f - r_base)(%ebx)
-       movw    %cx, (102f - r_base)(%ebx)
-
-       /* Set up the IDT for real mode. */
-       lidtl   (machine_real_restart_idt - r_base)(%ebx)
-
-       /*
-        * Set up a GDT from which we can load segment descriptors for real
-        * mode.  The GDT is not used in real mode; it is just needed here to
-        * prepare the descriptors.
-        */
-       lgdtl   (machine_real_restart_gdt - r_base)(%ebx)
-
-       /*
-        * Load the data segment registers with 16-bit compatible values
-        */
-       movl    $16, %ecx
-       movl    %ecx, %ds
-       movl    %ecx, %es
-       movl    %ecx, %fs
-       movl    %ecx, %gs
-       movl    %ecx, %ss
-       ljmpl   $8, $1f - r_base
-
-/*
- * This is 16-bit protected mode code to disable paging and the cache,
- * switch to real mode and jump to the BIOS reset code.
- *
- * The instruction that switches to real mode by writing to CR0 must be
- * followed immediately by a far jump instruction, which set CS to a
- * valid value for real mode, and flushes the prefetch queue to avoid
- * running instructions that have already been decoded in protected
- * mode.
- *
- * Clears all the flags except ET, especially PG (paging), PE
- * (protected-mode enable) and TS (task switch for coprocessor state
- * save).  Flushes the TLB after paging has been disabled.  Sets CD and
- * NW, to disable the cache on a 486, and invalidates the cache.  This
- * is more like the state of a 486 after reset.  I don't know if
- * something else should be done for other chips.
- *
- * More could be done here to set up the registers as if a CPU reset had
- * occurred; hopefully real BIOSs don't assume much.  This is not the
- * actual BIOS entry point, anyway (that is at 0xfffffff0).
- *
- * Most of this work is probably excessive, but it is what is tested.
- */
-       .code16
-1:
-       xorl    %ecx, %ecx
-       movl    %cr0, %eax
-       andl    $0x00000011, %eax
-       orl     $0x60000000, %eax
-       movl    %eax, %cr0
-       movl    %ecx, %cr3
-       movl    %cr0, %edx
-       andl    $0x60000000, %edx       /* If no cache bits -> no wbinvd */
-       jz      2f
-       wbinvd
-2:
-       andb    $0x10, %al
-       movl    %eax, %cr0
-       .byte   0xea                    /* ljmpw */
-101:   .word   0                       /* Offset */
-102:   .word   0                       /* Segment */
-
-bios:
-       ljmpw   $0xf000, $0xfff0
-
-apm:
-       movw    $0x1000, %ax
-       movw    %ax, %ss
-       movw    $0xf000, %sp
-       movw    $0x5307, %ax
-       movw    $0x0001, %bx
-       movw    $0x0003, %cx
-       int     $0x15
-
-END(machine_real_restart_asm)
-
-       .balign 16
-       /* These must match <asm/reboot.h */
-dispatch_table:
-       .word   bios - r_base
-       .word   apm - r_base
-END(dispatch_table)
-
-       .balign 16
-machine_real_restart_idt:
-       .word   0xffff          /* Length - real mode default value */
-       .long   0               /* Base - real mode default value */
-END(machine_real_restart_idt)
-
-       .balign 16
-ENTRY(machine_real_restart_gdt)
-       .quad   0               /* Self-pointer, filled in by PM code */
-       .quad   0               /* 16-bit code segment, filled in by PM code */
-       /*
-        * 16-bit data segment with the selector value 16 = 0x10 and
-        * base value 0x100; since this is consistent with real mode
-        * semantics we don't have to reload the segments once CR0.PE = 0.
-        */
-       .quad   GDT_ENTRY(0x0093, 0x100, 0xffff)
-END(machine_real_restart_gdt)
index f2afee6a19c1e82f9e8ec587dcaa4aa7885f9528..16be6dc14db1b67c108475466ee494312e82d665 100644 (file)
@@ -73,7 +73,7 @@
 
 #include <asm/mtrr.h>
 #include <asm/apic.h>
-#include <asm/trampoline.h>
+#include <asm/realmode.h>
 #include <asm/e820.h>
 #include <asm/mpspec.h>
 #include <asm/setup.h>
@@ -334,8 +334,8 @@ static void __init relocate_initrd(void)
        memblock_reserve(ramdisk_here, area_size);
        initrd_start = ramdisk_here + PAGE_OFFSET;
        initrd_end   = initrd_start + ramdisk_size;
-       printk(KERN_INFO "Allocated new RAMDISK: %08llx - %08llx\n",
-                        ramdisk_here, ramdisk_here + ramdisk_size);
+       printk(KERN_INFO "Allocated new RAMDISK: [mem %#010llx-%#010llx]\n",
+                        ramdisk_here, ramdisk_here + ramdisk_size - 1);
 
        q = (char *)initrd_start;
 
@@ -366,8 +366,8 @@ static void __init relocate_initrd(void)
        /* high pages is not converted by early_res_to_bootmem */
        ramdisk_image = boot_params.hdr.ramdisk_image;
        ramdisk_size  = boot_params.hdr.ramdisk_size;
-       printk(KERN_INFO "Move RAMDISK from %016llx - %016llx to"
-               " %08llx - %08llx\n",
+       printk(KERN_INFO "Move RAMDISK from [mem %#010llx-%#010llx] to"
+               " [mem %#010llx-%#010llx]\n",
                ramdisk_image, ramdisk_image + ramdisk_size - 1,
                ramdisk_here, ramdisk_here + ramdisk_size - 1);
 }
@@ -392,8 +392,8 @@ static void __init reserve_initrd(void)
                       ramdisk_size, end_of_lowmem>>1);
        }
 
-       printk(KERN_INFO "RAMDISK: %08llx - %08llx\n", ramdisk_image,
-                       ramdisk_end);
+       printk(KERN_INFO "RAMDISK: [mem %#010llx-%#010llx]\n", ramdisk_image,
+                       ramdisk_end - 1);
 
 
        if (ramdisk_end <= end_of_lowmem) {
@@ -906,10 +906,10 @@ void __init setup_arch(char **cmdline_p)
        setup_bios_corruption_check();
 #endif
 
-       printk(KERN_DEBUG "initial memory mapped : 0 - %08lx\n",
-                       max_pfn_mapped<<PAGE_SHIFT);
+       printk(KERN_DEBUG "initial memory mapped: [mem 0x00000000-%#010lx]\n",
+                       (max_pfn_mapped<<PAGE_SHIFT) - 1);
 
-       setup_trampolines();
+       setup_real_mode();
 
        init_gbpages();
 
@@ -968,6 +968,8 @@ void __init setup_arch(char **cmdline_p)
        if (boot_cpu_data.cpuid_level >= 0) {
                /* A CPU has %cr4 if and only if it has CPUID */
                mmu_cr4_features = read_cr4();
+               if (trampoline_cr4_features)
+                       *trampoline_cr4_features = mmu_cr4_features;
        }
 
 #ifdef CONFIG_X86_32
index 965dfda0fd5e442fa88a13e5aeff0a9b98e73029..21af737053aad05fb726a0e1023dfad428064518 100644 (file)
@@ -555,7 +555,6 @@ unsigned long sys_sigreturn(struct pt_regs *regs)
                                    sizeof(frame->extramask))))
                goto badframe;
 
-       sigdelsetmask(&set, ~_BLOCKABLE);
        set_current_blocked(&set);
 
        if (restore_sigcontext(regs, &frame->sc, &ax))
@@ -581,7 +580,6 @@ long sys_rt_sigreturn(struct pt_regs *regs)
        if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
                goto badframe;
 
-       sigdelsetmask(&set, ~_BLOCKABLE);
        set_current_blocked(&set);
 
        if (restore_sigcontext(regs, &frame->uc.uc_mcontext, &ax))
@@ -647,42 +645,28 @@ setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
                struct pt_regs *regs)
 {
        int usig = signr_convert(sig);
-       sigset_t *set = &current->blocked;
-       int ret;
-
-       if (current_thread_info()->status & TS_RESTORE_SIGMASK)
-               set = &current->saved_sigmask;
+       sigset_t *set = sigmask_to_save();
 
        /* Set up the stack frame */
        if (is_ia32) {
                if (ka->sa.sa_flags & SA_SIGINFO)
-                       ret = ia32_setup_rt_frame(usig, ka, info, set, regs);
+                       return ia32_setup_rt_frame(usig, ka, info, set, regs);
                else
-                       ret = ia32_setup_frame(usig, ka, set, regs);
+                       return ia32_setup_frame(usig, ka, set, regs);
 #ifdef CONFIG_X86_X32_ABI
        } else if (is_x32) {
-               ret = x32_setup_rt_frame(usig, ka, info,
+               return x32_setup_rt_frame(usig, ka, info,
                                         (compat_sigset_t *)set, regs);
 #endif
        } else {
-               ret = __setup_rt_frame(sig, ka, info, set, regs);
-       }
-
-       if (ret) {
-               force_sigsegv(sig, current);
-               return -EFAULT;
+               return __setup_rt_frame(sig, ka, info, set, regs);
        }
-
-       current_thread_info()->status &= ~TS_RESTORE_SIGMASK;
-       return ret;
 }
 
-static int
+static void
 handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka,
                struct pt_regs *regs)
 {
-       int ret;
-
        /* Are we from a system call? */
        if (syscall_get_nr(current, regs) >= 0) {
                /* If so, check system call restarting.. */
@@ -713,10 +697,10 @@ handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka,
            likely(test_and_clear_thread_flag(TIF_FORCED_TF)))
                regs->flags &= ~X86_EFLAGS_TF;
 
-       ret = setup_rt_frame(sig, ka, info, regs);
-
-       if (ret)
-               return ret;
+       if (setup_rt_frame(sig, ka, info, regs) < 0) {
+               force_sigsegv(sig, current);
+               return;
+       }
 
        /*
         * Clear the direction flag as per the ABI for function entry.
@@ -731,12 +715,8 @@ handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka,
         */
        regs->flags &= ~X86_EFLAGS_TF;
 
-       block_sigmask(ka, sig);
-
-       tracehook_signal_handler(sig, info, ka, regs,
-                                test_thread_flag(TIF_SINGLESTEP));
-
-       return 0;
+       signal_delivered(sig, info, ka, regs,
+                        test_thread_flag(TIF_SINGLESTEP));
 }
 
 #ifdef CONFIG_X86_32
@@ -757,16 +737,6 @@ static void do_signal(struct pt_regs *regs)
        siginfo_t info;
        int signr;
 
-       /*
-        * We want the common case to go fast, which is why we may in certain
-        * cases get here from kernel mode. Just return without doing anything
-        * if so.
-        * X86_32: vm86 regs switched out by assembly code before reaching
-        * here, so testing against kernel CS suffices.
-        */
-       if (!user_mode(regs))
-               return;
-
        signr = get_signal_to_deliver(&info, &ka, regs, NULL);
        if (signr > 0) {
                /* Whee! Actually deliver the signal.  */
@@ -796,10 +766,7 @@ static void do_signal(struct pt_regs *regs)
         * If there's no signal to deliver, we just put the saved sigmask
         * back.
         */
-       if (current_thread_info()->status & TS_RESTORE_SIGMASK) {
-               current_thread_info()->status &= ~TS_RESTORE_SIGMASK;
-               set_current_blocked(&current->saved_sigmask);
-       }
+       restore_saved_sigmask();
 }
 
 /*
@@ -827,8 +794,6 @@ do_notify_resume(struct pt_regs *regs, void *unused, __u32 thread_info_flags)
        if (thread_info_flags & _TIF_NOTIFY_RESUME) {
                clear_thread_flag(TIF_NOTIFY_RESUME);
                tracehook_notify_resume(regs);
-               if (current->replacement_session_keyring)
-                       key_replace_session_keyring();
        }
        if (thread_info_flags & _TIF_USER_RETURN_NOTIFY)
                fire_user_return_notifiers();
@@ -936,7 +901,6 @@ asmlinkage long sys32_x32_rt_sigreturn(struct pt_regs *regs)
        if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
                goto badframe;
 
-       sigdelsetmask(&set, ~_BLOCKABLE);
        set_current_blocked(&set);
 
        if (restore_sigcontext(regs, &frame->uc.uc_mcontext, &ax))
index 433529e29be479b98a41cee06d601f1a389daf2e..f56f96da77f57e011b64e3e69cbabdc76ed3d442 100644 (file)
@@ -57,7 +57,7 @@
 #include <asm/nmi.h>
 #include <asm/irq.h>
 #include <asm/idle.h>
-#include <asm/trampoline.h>
+#include <asm/realmode.h>
 #include <asm/cpu.h>
 #include <asm/numa.h>
 #include <asm/pgtable.h>
@@ -73,6 +73,8 @@
 #include <asm/smpboot_hooks.h>
 #include <asm/i8259.h>
 
+#include <asm/realmode.h>
+
 /* State of each CPU */
 DEFINE_PER_CPU(int, cpu_state) = { 0 };
 
@@ -660,8 +662,12 @@ static void __cpuinit announce_cpu(int cpu, int apicid)
  */
 static int __cpuinit do_boot_cpu(int apicid, int cpu, struct task_struct *idle)
 {
+       volatile u32 *trampoline_status =
+               (volatile u32 *) __va(real_mode_header->trampoline_status);
+       /* start_ip had better be page-aligned! */
+       unsigned long start_ip = real_mode_header->trampoline_start;
+
        unsigned long boot_error = 0;
-       unsigned long start_ip;
        int timeout;
 
        alternatives_smp_switch(1);
@@ -684,9 +690,6 @@ static int __cpuinit do_boot_cpu(int apicid, int cpu, struct task_struct *idle)
        initial_code = (unsigned long)start_secondary;
        stack_start  = idle->thread.sp;
 
-       /* start_ip had better be page-aligned! */
-       start_ip = trampoline_address();
-
        /* So we see what's up */
        announce_cpu(cpu, apicid);
 
@@ -749,8 +752,7 @@ static int __cpuinit do_boot_cpu(int apicid, int cpu, struct task_struct *idle)
                        pr_debug("CPU%d: has booted.\n", cpu);
                } else {
                        boot_error = 1;
-                       if (*(volatile u32 *)TRAMPOLINE_SYM(trampoline_status)
-                           == 0xA5A5A5A5)
+                       if (*trampoline_status == 0xA5A5A5A5)
                                /* trampoline started but...? */
                                pr_err("CPU%d: Stuck ??\n", cpu);
                        else
@@ -776,7 +778,7 @@ static int __cpuinit do_boot_cpu(int apicid, int cpu, struct task_struct *idle)
        }
 
        /* mark "stuck" area as not stuck */
-       *(volatile u32 *)TRAMPOLINE_SYM(trampoline_status) = 0;
+       *trampoline_status = 0;
 
        if (get_uv_system_type() != UV_NON_UNIQUE_APIC) {
                /*
index 6410744ac5cb7249544fb9424853b0e88f28679f..f84fe00fad48a4e1c2273a9f46a18b47af96204c 100644 (file)
@@ -32,7 +32,7 @@
 #include <linux/mm.h>
 #include <linux/tboot.h>
 
-#include <asm/trampoline.h>
+#include <asm/realmode.h>
 #include <asm/processor.h>
 #include <asm/bootparam.h>
 #include <asm/pgtable.h>
@@ -44,7 +44,7 @@
 #include <asm/e820.h>
 #include <asm/io.h>
 
-#include "acpi/realmode/wakeup.h"
+#include "../realmode/rm/wakeup.h"
 
 /* Global pointer to shared data; NULL means no measured launch. */
 struct tboot *tboot __read_mostly;
@@ -201,7 +201,8 @@ static int tboot_setup_sleep(void)
                add_mac_region(e820.map[i].addr, e820.map[i].size);
        }
 
-       tboot->acpi_sinfo.kernel_s3_resume_vector = acpi_wakeup_address;
+       tboot->acpi_sinfo.kernel_s3_resume_vector =
+               real_mode_header->wakeup_start;
 
        return 0;
 }
diff --git a/arch/x86/kernel/trampoline.c b/arch/x86/kernel/trampoline.c
deleted file mode 100644 (file)
index a73b610..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-#include <linux/io.h>
-#include <linux/memblock.h>
-
-#include <asm/trampoline.h>
-#include <asm/cacheflush.h>
-#include <asm/pgtable.h>
-
-unsigned char *x86_trampoline_base;
-
-void __init setup_trampolines(void)
-{
-       phys_addr_t mem;
-       size_t size = PAGE_ALIGN(x86_trampoline_end - x86_trampoline_start);
-
-       /* Has to be in very low memory so we can execute real-mode AP code. */
-       mem = memblock_find_in_range(0, 1<<20, size, PAGE_SIZE);
-       if (!mem)
-               panic("Cannot allocate trampoline\n");
-
-       x86_trampoline_base = __va(mem);
-       memblock_reserve(mem, size);
-
-       printk(KERN_DEBUG "Base memory trampoline at [%p] %llx size %zu\n",
-              x86_trampoline_base, (unsigned long long)mem, size);
-
-       memcpy(x86_trampoline_base, x86_trampoline_start, size);
-}
-
-/*
- * setup_trampolines() gets called very early, to guarantee the
- * availability of low memory.  This is before the proper kernel page
- * tables are set up, so we cannot set page permissions in that
- * function.  Thus, we use an arch_initcall instead.
- */
-static int __init configure_trampolines(void)
-{
-       size_t size = PAGE_ALIGN(x86_trampoline_end - x86_trampoline_start);
-
-       set_memory_x((unsigned long)x86_trampoline_base, size >> PAGE_SHIFT);
-       return 0;
-}
-arch_initcall(configure_trampolines);
diff --git a/arch/x86/kernel/trampoline_32.S b/arch/x86/kernel/trampoline_32.S
deleted file mode 100644 (file)
index 451c0a7..0000000
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- *
- *     Trampoline.S    Derived from Setup.S by Linus Torvalds
- *
- *     4 Jan 1997 Michael Chastain: changed to gnu as.
- *
- *     This is only used for booting secondary CPUs in SMP machine
- *
- *     Entry: CS:IP point to the start of our code, we are 
- *     in real mode with no stack, but the rest of the 
- *     trampoline page to make our stack and everything else
- *     is a mystery.
- *
- *     We jump into arch/x86/kernel/head_32.S.
- *
- *     On entry to trampoline_data, the processor is in real mode
- *     with 16-bit addressing and 16-bit data.  CS has some value
- *     and IP is zero.  Thus, data addresses need to be absolute
- *     (no relocation) and are taken with regard to r_base.
- *
- *     If you work on this file, check the object module with
- *     objdump --reloc to make sure there are no relocation
- *     entries except for:
- *
- *     TYPE              VALUE
- *     R_386_32          startup_32_smp
- *     R_386_32          boot_gdt
- */
-
-#include <linux/linkage.h>
-#include <linux/init.h>
-#include <asm/segment.h>
-#include <asm/page_types.h>
-
-#ifdef CONFIG_SMP
-
-       .section ".x86_trampoline","a"
-       .balign PAGE_SIZE
-       .code16
-
-ENTRY(trampoline_data)
-r_base = .
-       wbinvd                  # Needed for NUMA-Q should be harmless for others
-       mov     %cs, %ax        # Code and data in the same place
-       mov     %ax, %ds
-
-       cli                     # We should be safe anyway
-
-       movl    $0xA5A5A5A5, trampoline_status - r_base
-                               # write marker for master knows we're running
-
-       /* GDT tables in non default location kernel can be beyond 16MB and
-        * lgdt will not be able to load the address as in real mode default
-        * operand size is 16bit. Use lgdtl instead to force operand size
-        * to 32 bit.
-        */
-
-       lidtl   boot_idt_descr - r_base # load idt with 0, 0
-       lgdtl   boot_gdt_descr - r_base # load gdt with whatever is appropriate
-
-       xor     %ax, %ax
-       inc     %ax             # protected mode (PE) bit
-       lmsw    %ax             # into protected mode
-       # flush prefetch and jump to startup_32_smp in arch/i386/kernel/head.S
-       ljmpl   $__BOOT_CS, $(startup_32_smp-__PAGE_OFFSET)
-
-       # These need to be in the same 64K segment as the above;
-       # hence we don't use the boot_gdt_descr defined in head.S
-boot_gdt_descr:
-       .word   __BOOT_DS + 7                   # gdt limit
-       .long   boot_gdt - __PAGE_OFFSET        # gdt base
-
-boot_idt_descr:
-       .word   0                               # idt limit = 0
-       .long   0                               # idt base = 0L
-
-ENTRY(trampoline_status)
-       .long   0
-
-.globl trampoline_end
-trampoline_end:
-
-#endif /* CONFIG_SMP */
diff --git a/arch/x86/kernel/trampoline_64.S b/arch/x86/kernel/trampoline_64.S
deleted file mode 100644 (file)
index 09ff517..0000000
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- *
- *     Trampoline.S    Derived from Setup.S by Linus Torvalds
- *
- *     4 Jan 1997 Michael Chastain: changed to gnu as.
- *     15 Sept 2005 Eric Biederman: 64bit PIC support
- *
- *     Entry: CS:IP point to the start of our code, we are 
- *     in real mode with no stack, but the rest of the 
- *     trampoline page to make our stack and everything else
- *     is a mystery.
- *
- *     On entry to trampoline_data, the processor is in real mode
- *     with 16-bit addressing and 16-bit data.  CS has some value
- *     and IP is zero.  Thus, data addresses need to be absolute
- *     (no relocation) and are taken with regard to r_base.
- *
- *     With the addition of trampoline_level4_pgt this code can
- *     now enter a 64bit kernel that lives at arbitrary 64bit
- *     physical addresses.
- *
- *     If you work on this file, check the object module with objdump
- *     --full-contents --reloc to make sure there are no relocation
- *     entries.
- */
-
-#include <linux/linkage.h>
-#include <linux/init.h>
-#include <asm/pgtable_types.h>
-#include <asm/page_types.h>
-#include <asm/msr.h>
-#include <asm/segment.h>
-#include <asm/processor-flags.h>
-
-       .section ".x86_trampoline","a"
-       .balign PAGE_SIZE
-       .code16
-
-ENTRY(trampoline_data)
-r_base = .
-       cli                     # We should be safe anyway
-       wbinvd
-       mov     %cs, %ax        # Code and data in the same place
-       mov     %ax, %ds
-       mov     %ax, %es
-       mov     %ax, %ss
-
-
-       movl    $0xA5A5A5A5, trampoline_status - r_base
-                               # write marker for master knows we're running
-
-                                       # Setup stack
-       movw    $(trampoline_stack_end - r_base), %sp
-
-       call    verify_cpu              # Verify the cpu supports long mode
-       testl   %eax, %eax              # Check for return code
-       jnz     no_longmode
-
-       mov     %cs, %ax
-       movzx   %ax, %esi               # Find the 32bit trampoline location
-       shll    $4, %esi
-
-                                       # Fixup the absolute vectors
-       leal    (startup_32 - r_base)(%esi), %eax
-       movl    %eax, startup_32_vector - r_base
-       leal    (startup_64 - r_base)(%esi), %eax
-       movl    %eax, startup_64_vector - r_base
-       leal    (tgdt - r_base)(%esi), %eax
-       movl    %eax, (tgdt + 2 - r_base)
-
-       /*
-        * GDT tables in non default location kernel can be beyond 16MB and
-        * lgdt will not be able to load the address as in real mode default
-        * operand size is 16bit. Use lgdtl instead to force operand size
-        * to 32 bit.
-        */
-
-       lidtl   tidt - r_base   # load idt with 0, 0
-       lgdtl   tgdt - r_base   # load gdt with whatever is appropriate
-
-       mov     $X86_CR0_PE, %ax        # protected mode (PE) bit
-       lmsw    %ax                     # into protected mode
-
-       # flush prefetch and jump to startup_32
-       ljmpl   *(startup_32_vector - r_base)
-
-       .code32
-       .balign 4
-startup_32:
-       movl    $__KERNEL_DS, %eax      # Initialize the %ds segment register
-       movl    %eax, %ds
-
-       movl    $X86_CR4_PAE, %eax
-       movl    %eax, %cr4              # Enable PAE mode
-
-                                       # Setup trampoline 4 level pagetables
-       leal    (trampoline_level4_pgt - r_base)(%esi), %eax
-       movl    %eax, %cr3
-
-       movl    $MSR_EFER, %ecx
-       movl    $(1 << _EFER_LME), %eax # Enable Long Mode
-       xorl    %edx, %edx
-       wrmsr
-
-       # Enable paging and in turn activate Long Mode
-       # Enable protected mode
-       movl    $(X86_CR0_PG | X86_CR0_PE), %eax
-       movl    %eax, %cr0
-
-       /*
-        * At this point we're in long mode but in 32bit compatibility mode
-        * with EFER.LME = 1, CS.L = 0, CS.D = 1 (and in turn
-        * EFER.LMA = 1). Now we want to jump in 64bit mode, to do that we use
-        * the new gdt/idt that has __KERNEL_CS with CS.L = 1.
-        */
-       ljmp    *(startup_64_vector - r_base)(%esi)
-
-       .code64
-       .balign 4
-startup_64:
-       # Now jump into the kernel using virtual addresses
-       movq    $secondary_startup_64, %rax
-       jmp     *%rax
-
-       .code16
-no_longmode:
-       hlt
-       jmp no_longmode
-#include "verify_cpu.S"
-
-       .balign 4
-       # Careful these need to be in the same 64K segment as the above;
-tidt:
-       .word   0                       # idt limit = 0
-       .word   0, 0                    # idt base = 0L
-
-       # Duplicate the global descriptor table
-       # so the kernel can live anywhere
-       .balign 4
-tgdt:
-       .short  tgdt_end - tgdt         # gdt limit
-       .long   tgdt - r_base
-       .short 0
-       .quad   0x00cf9b000000ffff      # __KERNEL32_CS
-       .quad   0x00af9b000000ffff      # __KERNEL_CS
-       .quad   0x00cf93000000ffff      # __KERNEL_DS
-tgdt_end:
-
-       .balign 4
-startup_32_vector:
-       .long   startup_32 - r_base
-       .word   __KERNEL32_CS, 0
-
-       .balign 4
-startup_64_vector:
-       .long   startup_64 - r_base
-       .word   __KERNEL_CS, 0
-
-       .balign 4
-ENTRY(trampoline_status)
-       .long   0
-
-trampoline_stack:
-       .org 0x1000
-trampoline_stack_end:
-ENTRY(trampoline_level4_pgt)
-       .quad   level3_ident_pgt - __START_KERNEL_map + _KERNPG_TABLE
-       .fill   510,8,0
-       .quad   level3_kernel_pgt - __START_KERNEL_map + _KERNPG_TABLE
-
-ENTRY(trampoline_end)
index 0f703f10901a96d6b2d24e9f93559d62bcc2f63a..22a1530146a8740ab14efed3ca8c0fce5ce68959 100644 (file)
@@ -197,18 +197,6 @@ SECTIONS
 
        INIT_DATA_SECTION(16)
 
-       /*
-        * Code and data for a variety of lowlevel trampolines, to be
-        * copied into base memory (< 1 MiB) during initialization.
-        * Since it is copied early, the main copy can be discarded
-        * afterwards.
-        */
-        .x86_trampoline : AT(ADDR(.x86_trampoline) - LOAD_OFFSET) {
-               x86_trampoline_start = .;
-               *(.x86_trampoline)
-               x86_trampoline_end = .;
-       }
-
        .x86_cpu_dev.init : AT(ADDR(.x86_cpu_dev.init) - LOAD_OFFSET) {
                __x86_cpu_dev_start = .;
                *(.x86_cpu_dev.init)
index 72102e0ab7cb3a0ae2302aa10eadd6bdb73d939a..be3cea4407ffad63824c068332079baf16336012 100644 (file)
@@ -2595,8 +2595,7 @@ static void transparent_hugepage_adjust(struct kvm_vcpu *vcpu,
                        *gfnp = gfn;
                        kvm_release_pfn_clean(pfn);
                        pfn &= ~mask;
-                       if (!get_page_unless_zero(pfn_to_page(pfn)))
-                               BUG();
+                       kvm_get_pfn(pfn);
                        *pfnp = pfn;
                }
        }
index 319b6f2fb8b9dc56300c082ab06a266673b091c9..97141c26a13ac8400cfce07c36d55cca50962b26 100644 (file)
@@ -84,8 +84,9 @@ static void __init find_early_table_space(struct map_range *mr, unsigned long en
        pgt_buf_end = pgt_buf_start;
        pgt_buf_top = pgt_buf_start + (tables >> PAGE_SHIFT);
 
-       printk(KERN_DEBUG "kernel direct mapping tables up to %lx @ %lx-%lx\n",
-               end, pgt_buf_start << PAGE_SHIFT, pgt_buf_top << PAGE_SHIFT);
+       printk(KERN_DEBUG "kernel direct mapping tables up to %#lx @ [mem %#010lx-%#010lx]\n",
+               end - 1, pgt_buf_start << PAGE_SHIFT,
+               (pgt_buf_top << PAGE_SHIFT) - 1);
 }
 
 void __init native_pagetable_reserve(u64 start, u64 end)
@@ -132,7 +133,8 @@ unsigned long __init_refok init_memory_mapping(unsigned long start,
        int nr_range, i;
        int use_pse, use_gbpages;
 
-       printk(KERN_INFO "init_memory_mapping: %016lx-%016lx\n", start, end);
+       printk(KERN_INFO "init_memory_mapping: [mem %#010lx-%#010lx]\n",
+              start, end - 1);
 
 #if defined(CONFIG_DEBUG_PAGEALLOC) || defined(CONFIG_KMEMCHECK)
        /*
@@ -251,8 +253,8 @@ unsigned long __init_refok init_memory_mapping(unsigned long start,
        }
 
        for (i = 0; i < nr_range; i++)
-               printk(KERN_DEBUG " %010lx - %010lx page %s\n",
-                               mr[i].start, mr[i].end,
+               printk(KERN_DEBUG " [mem %#010lx-%#010lx] page %s\n",
+                               mr[i].start, mr[i].end - 1,
                        (mr[i].page_size_mask & (1<<PG_LEVEL_1G))?"1G":(
                         (mr[i].page_size_mask & (1<<PG_LEVEL_2M))?"2M":"4k"));
 
@@ -350,8 +352,8 @@ void free_init_pages(char *what, unsigned long begin, unsigned long end)
         * create a kernel page fault:
         */
 #ifdef CONFIG_DEBUG_PAGEALLOC
-       printk(KERN_INFO "debug: unmapping init memory %08lx..%08lx\n",
-               begin, end);
+       printk(KERN_INFO "debug: unmapping init [mem %#010lx-%#010lx]\n",
+               begin, end - 1);
        set_memory_np(begin, (end - begin) >> PAGE_SHIFT);
 #else
        /*
index 19d3fa08b1191493cda6415e56f8f26d86415bf7..2d125be1bae9f2c229b835b8ea1421e685c38879 100644 (file)
@@ -141,8 +141,8 @@ static int __init numa_add_memblk_to(int nid, u64 start, u64 end,
 
        /* whine about and ignore invalid blks */
        if (start > end || nid < 0 || nid >= MAX_NUMNODES) {
-               pr_warning("NUMA: Warning: invalid memblk node %d (%Lx-%Lx)\n",
-                          nid, start, end);
+               pr_warning("NUMA: Warning: invalid memblk node %d [mem %#010Lx-%#010Lx]\n",
+                          nid, start, end - 1);
                return 0;
        }
 
@@ -210,8 +210,8 @@ static void __init setup_node_data(int nid, u64 start, u64 end)
 
        start = roundup(start, ZONE_ALIGN);
 
-       printk(KERN_INFO "Initmem setup node %d %016Lx-%016Lx\n",
-              nid, start, end);
+       printk(KERN_INFO "Initmem setup node %d [mem %#010Lx-%#010Lx]\n",
+              nid, start, end - 1);
 
        /*
         * Allocate node data.  Try remap allocator first, node-local
@@ -232,7 +232,7 @@ static void __init setup_node_data(int nid, u64 start, u64 end)
        }
 
        /* report and initialize */
-       printk(KERN_INFO "  NODE_DATA [%016Lx - %016Lx]%s\n",
+       printk(KERN_INFO "  NODE_DATA [mem %#010Lx-%#010Lx]%s\n",
               nd_pa, nd_pa + nd_size - 1, remapped ? " (remapped)" : "");
        tnid = early_pfn_to_nid(nd_pa >> PAGE_SHIFT);
        if (!remapped && tnid != nid)
@@ -291,14 +291,14 @@ int __init numa_cleanup_meminfo(struct numa_meminfo *mi)
                         */
                        if (bi->end > bj->start && bi->start < bj->end) {
                                if (bi->nid != bj->nid) {
-                                       pr_err("NUMA: node %d (%Lx-%Lx) overlaps with node %d (%Lx-%Lx)\n",
-                                              bi->nid, bi->start, bi->end,
-                                              bj->nid, bj->start, bj->end);
+                                       pr_err("NUMA: node %d [mem %#010Lx-%#010Lx] overlaps with node %d [mem %#010Lx-%#010Lx]\n",
+                                              bi->nid, bi->start, bi->end - 1,
+                                              bj->nid, bj->start, bj->end - 1);
                                        return -EINVAL;
                                }
-                               pr_warning("NUMA: Warning: node %d (%Lx-%Lx) overlaps with itself (%Lx-%Lx)\n",
-                                          bi->nid, bi->start, bi->end,
-                                          bj->start, bj->end);
+                               pr_warning("NUMA: Warning: node %d [mem %#010Lx-%#010Lx] overlaps with itself [mem %#010Lx-%#010Lx]\n",
+                                          bi->nid, bi->start, bi->end - 1,
+                                          bj->start, bj->end - 1);
                        }
 
                        /*
@@ -320,9 +320,9 @@ int __init numa_cleanup_meminfo(struct numa_meminfo *mi)
                        }
                        if (k < mi->nr_blks)
                                continue;
-                       printk(KERN_INFO "NUMA: Node %d [%Lx,%Lx) + [%Lx,%Lx) -> [%Lx,%Lx)\n",
-                              bi->nid, bi->start, bi->end, bj->start, bj->end,
-                              start, end);
+                       printk(KERN_INFO "NUMA: Node %d [mem %#010Lx-%#010Lx] + [mem %#010Lx-%#010Lx] -> [mem %#010Lx-%#010Lx]\n",
+                              bi->nid, bi->start, bi->end - 1, bj->start,
+                              bj->end - 1, start, end - 1);
                        bi->start = start;
                        bi->end = end;
                        numa_remove_memblk_from(j--, mi);
@@ -616,8 +616,8 @@ static int __init dummy_numa_init(void)
 {
        printk(KERN_INFO "%s\n",
               numa_off ? "NUMA turned off" : "No NUMA configuration found");
-       printk(KERN_INFO "Faking a node at %016Lx-%016Lx\n",
-              0LLU, PFN_PHYS(max_pfn));
+       printk(KERN_INFO "Faking a node at [mem %#018Lx-%#018Lx]\n",
+              0LLU, PFN_PHYS(max_pfn) - 1);
 
        node_set(0, numa_nodes_parsed);
        numa_add_memblk(0, 0, PFN_PHYS(max_pfn));
index 871dd886817014597fadfb273a895dd0893c9595..dbbbb47260ccc28b8dad5bb14f82420187a83ff3 100644 (file)
@@ -68,8 +68,8 @@ static int __init emu_setup_memblk(struct numa_meminfo *ei,
                numa_remove_memblk_from(phys_blk, pi);
        }
 
-       printk(KERN_INFO "Faking node %d at %016Lx-%016Lx (%LuMB)\n", nid,
-              eb->start, eb->end, (eb->end - eb->start) >> 20);
+       printk(KERN_INFO "Faking node %d at [mem %#018Lx-%#018Lx] (%LuMB)\n",
+              nid, eb->start, eb->end - 1, (eb->end - eb->start) >> 20);
        return 0;
 }
 
index f6ff57b7efa514e0a7e1ec47ac8472ea868c13fa..3d68ef6d2266cb66b3d07c578191b80c5348e0e2 100644 (file)
@@ -158,31 +158,47 @@ static unsigned long pat_x_mtrr_type(u64 start, u64 end, unsigned long req_type)
        return req_type;
 }
 
+struct pagerange_state {
+       unsigned long           cur_pfn;
+       int                     ram;
+       int                     not_ram;
+};
+
+static int
+pagerange_is_ram_callback(unsigned long initial_pfn, unsigned long total_nr_pages, void *arg)
+{
+       struct pagerange_state *state = arg;
+
+       state->not_ram  |= initial_pfn > state->cur_pfn;
+       state->ram      |= total_nr_pages > 0;
+       state->cur_pfn   = initial_pfn + total_nr_pages;
+
+       return state->ram && state->not_ram;
+}
+
 static int pat_pagerange_is_ram(resource_size_t start, resource_size_t end)
 {
-       int ram_page = 0, not_rampage = 0;
-       unsigned long page_nr;
+       int ret = 0;
+       unsigned long start_pfn = start >> PAGE_SHIFT;
+       unsigned long end_pfn = (end + PAGE_SIZE - 1) >> PAGE_SHIFT;
+       struct pagerange_state state = {start_pfn, 0, 0};
 
-       for (page_nr = (start >> PAGE_SHIFT); page_nr < (end >> PAGE_SHIFT);
-            ++page_nr) {
-               /*
-                * For legacy reasons, physical address range in the legacy ISA
-                * region is tracked as non-RAM. This will allow users of
-                * /dev/mem to map portions of legacy ISA region, even when
-                * some of those portions are listed(or not even listed) with
-                * different e820 types(RAM/reserved/..)
-                */
-               if (page_nr >= (ISA_END_ADDRESS >> PAGE_SHIFT) &&
-                   page_is_ram(page_nr))
-                       ram_page = 1;
-               else
-                       not_rampage = 1;
-
-               if (ram_page == not_rampage)
-                       return -1;
+       /*
+        * For legacy reasons, physical address range in the legacy ISA
+        * region is tracked as non-RAM. This will allow users of
+        * /dev/mem to map portions of legacy ISA region, even when
+        * some of those portions are listed(or not even listed) with
+        * different e820 types(RAM/reserved/..)
+        */
+       if (start_pfn < ISA_END_ADDRESS >> PAGE_SHIFT)
+               start_pfn = ISA_END_ADDRESS >> PAGE_SHIFT;
+
+       if (start_pfn < end_pfn) {
+               ret = walk_system_ram_range(start_pfn, end_pfn - start_pfn,
+                               &state, pagerange_is_ram_callback);
        }
 
-       return ram_page;
+       return (ret > 0) ? -1 : (state.ram ? 1 : 0);
 }
 
 /*
@@ -209,9 +225,8 @@ static int reserve_ram_pages_type(u64 start, u64 end, unsigned long req_type,
                page = pfn_to_page(pfn);
                type = get_page_memtype(page);
                if (type != -1) {
-                       printk(KERN_INFO "reserve_ram_pages_type failed "
-                               "0x%Lx-0x%Lx, track 0x%lx, req 0x%lx\n",
-                               start, end, type, req_type);
+                       printk(KERN_INFO "reserve_ram_pages_type failed [mem %#010Lx-%#010Lx], track 0x%lx, req 0x%lx\n",
+                               start, end - 1, type, req_type);
                        if (new_type)
                                *new_type = type;
 
@@ -314,9 +329,9 @@ int reserve_memtype(u64 start, u64 end, unsigned long req_type,
 
        err = rbt_memtype_check_insert(new, new_type);
        if (err) {
-               printk(KERN_INFO "reserve_memtype failed 0x%Lx-0x%Lx, "
-                      "track %s, req %s\n",
-                      start, end, cattr_name(new->type), cattr_name(req_type));
+               printk(KERN_INFO "reserve_memtype failed [mem %#010Lx-%#010Lx], track %s, req %s\n",
+                      start, end - 1,
+                      cattr_name(new->type), cattr_name(req_type));
                kfree(new);
                spin_unlock(&memtype_lock);
 
@@ -325,8 +340,8 @@ int reserve_memtype(u64 start, u64 end, unsigned long req_type,
 
        spin_unlock(&memtype_lock);
 
-       dprintk("reserve_memtype added 0x%Lx-0x%Lx, track %s, req %s, ret %s\n",
-               start, end, cattr_name(new->type), cattr_name(req_type),
+       dprintk("reserve_memtype added [mem %#010Lx-%#010Lx], track %s, req %s, ret %s\n",
+               start, end - 1, cattr_name(new->type), cattr_name(req_type),
                new_type ? cattr_name(*new_type) : "-");
 
        return err;
@@ -360,14 +375,14 @@ int free_memtype(u64 start, u64 end)
        spin_unlock(&memtype_lock);
 
        if (!entry) {
-               printk(KERN_INFO "%s:%d freeing invalid memtype %Lx-%Lx\n",
-                       current->comm, current->pid, start, end);
+               printk(KERN_INFO "%s:%d freeing invalid memtype [mem %#010Lx-%#010Lx]\n",
+                      current->comm, current->pid, start, end - 1);
                return -EINVAL;
        }
 
        kfree(entry);
 
-       dprintk("free_memtype request 0x%Lx-0x%Lx\n", start, end);
+       dprintk("free_memtype request [mem %#010Lx-%#010Lx]\n", start, end - 1);
 
        return 0;
 }
@@ -491,9 +506,8 @@ static inline int range_is_allowed(unsigned long pfn, unsigned long size)
 
        while (cursor < to) {
                if (!devmem_is_allowed(pfn)) {
-                       printk(KERN_INFO
-               "Program %s tried to access /dev/mem between %Lx->%Lx.\n",
-                               current->comm, from, to);
+                       printk(KERN_INFO "Program %s tried to access /dev/mem between [mem %#010Lx-%#010Lx]\n",
+                               current->comm, from, to - 1);
                        return 0;
                }
                cursor += PAGE_SIZE;
@@ -554,12 +568,11 @@ int kernel_map_sync_memtype(u64 base, unsigned long size, unsigned long flags)
                                size;
 
        if (ioremap_change_attr((unsigned long)__va(base), id_sz, flags) < 0) {
-               printk(KERN_INFO
-                       "%s:%d ioremap_change_attr failed %s "
-                       "for %Lx-%Lx\n",
+               printk(KERN_INFO "%s:%d ioremap_change_attr failed %s "
+                       "for [mem %#010Lx-%#010Lx]\n",
                        current->comm, current->pid,
                        cattr_name(flags),
-                       base, (unsigned long long)(base + size));
+                       base, (unsigned long long)(base + size-1));
                return -EINVAL;
        }
        return 0;
@@ -591,12 +604,11 @@ static int reserve_pfn_range(u64 paddr, unsigned long size, pgprot_t *vma_prot,
 
                flags = lookup_memtype(paddr);
                if (want_flags != flags) {
-                       printk(KERN_WARNING
-                       "%s:%d map pfn RAM range req %s for %Lx-%Lx, got %s\n",
+                       printk(KERN_WARNING "%s:%d map pfn RAM range req %s for [mem %#010Lx-%#010Lx], got %s\n",
                                current->comm, current->pid,
                                cattr_name(want_flags),
                                (unsigned long long)paddr,
-                               (unsigned long long)(paddr + size),
+                               (unsigned long long)(paddr + size - 1),
                                cattr_name(flags));
                        *vma_prot = __pgprot((pgprot_val(*vma_prot) &
                                              (~_PAGE_CACHE_MASK)) |
@@ -614,11 +626,11 @@ static int reserve_pfn_range(u64 paddr, unsigned long size, pgprot_t *vma_prot,
                    !is_new_memtype_allowed(paddr, size, want_flags, flags)) {
                        free_memtype(paddr, paddr + size);
                        printk(KERN_ERR "%s:%d map pfn expected mapping type %s"
-                               " for %Lx-%Lx, got %s\n",
+                               " for [mem %#010Lx-%#010Lx], got %s\n",
                                current->comm, current->pid,
                                cattr_name(want_flags),
                                (unsigned long long)paddr,
-                               (unsigned long long)(paddr + size),
+                               (unsigned long long)(paddr + size - 1),
                                cattr_name(flags));
                        return -EINVAL;
                }
index efb5b4b93711cec20dd095e0e0a9065a9cc060fb..732af3a9618375da189f3da4651d75a7fbd4693b 100644 (file)
@@ -176,8 +176,9 @@ acpi_numa_memory_affinity_init(struct acpi_srat_mem_affinity *ma)
                return;
        }
 
-       printk(KERN_INFO "SRAT: Node %u PXM %u %Lx-%Lx\n", node, pxm,
-              start, end);
+       printk(KERN_INFO "SRAT: Node %u PXM %u [mem %#010Lx-%#010Lx]\n",
+              node, pxm,
+              (unsigned long long) start, (unsigned long long) end - 1);
 }
 
 void __init acpi_numa_arch_fixup(void) {}
diff --git a/arch/x86/realmode/Makefile b/arch/x86/realmode/Makefile
new file mode 100644 (file)
index 0000000..94f7fbe
--- /dev/null
@@ -0,0 +1,18 @@
+#
+# arch/x86/realmode/Makefile
+#
+# This file is subject to the terms and conditions of the GNU General Public
+# License.  See the file "COPYING" in the main directory of this archive
+# for more details.
+#
+#
+
+subdir- := rm
+
+obj-y += init.o
+obj-y += rmpiggy.o
+
+$(obj)/rmpiggy.o: $(obj)/rm/realmode.bin
+
+$(obj)/rm/realmode.bin: FORCE
+       $(Q)$(MAKE) $(build)=$(obj)/rm $@
diff --git a/arch/x86/realmode/init.c b/arch/x86/realmode/init.c
new file mode 100644 (file)
index 0000000..cbca565
--- /dev/null
@@ -0,0 +1,115 @@
+#include <linux/io.h>
+#include <linux/memblock.h>
+
+#include <asm/cacheflush.h>
+#include <asm/pgtable.h>
+#include <asm/realmode.h>
+
+struct real_mode_header *real_mode_header;
+u32 *trampoline_cr4_features;
+
+void __init setup_real_mode(void)
+{
+       phys_addr_t mem;
+       u16 real_mode_seg;
+       u32 *rel;
+       u32 count;
+       u32 *ptr;
+       u16 *seg;
+       int i;
+       unsigned char *base;
+       struct trampoline_header *trampoline_header;
+       size_t size = PAGE_ALIGN(real_mode_blob_end - real_mode_blob);
+#ifdef CONFIG_X86_64
+       u64 *trampoline_pgd;
+       u64 efer;
+#endif
+
+       /* Has to be in very low memory so we can execute real-mode AP code. */
+       mem = memblock_find_in_range(0, 1<<20, size, PAGE_SIZE);
+       if (!mem)
+               panic("Cannot allocate trampoline\n");
+
+       base = __va(mem);
+       memblock_reserve(mem, size);
+       real_mode_header = (struct real_mode_header *) base;
+       printk(KERN_DEBUG "Base memory trampoline at [%p] %llx size %zu\n",
+              base, (unsigned long long)mem, size);
+
+       memcpy(base, real_mode_blob, size);
+
+       real_mode_seg = __pa(base) >> 4;
+       rel = (u32 *) real_mode_relocs;
+
+       /* 16-bit segment relocations. */
+       count = rel[0];
+       rel = &rel[1];
+       for (i = 0; i < count; i++) {
+               seg = (u16 *) (base + rel[i]);
+               *seg = real_mode_seg;
+       }
+
+       /* 32-bit linear relocations. */
+       count = rel[i];
+       rel =  &rel[i + 1];
+       for (i = 0; i < count; i++) {
+               ptr = (u32 *) (base + rel[i]);
+               *ptr += __pa(base);
+       }
+
+       /* Must be perfomed *after* relocation. */
+       trampoline_header = (struct trampoline_header *)
+               __va(real_mode_header->trampoline_header);
+
+#ifdef CONFIG_X86_32
+       trampoline_header->start = __pa(startup_32_smp);
+       trampoline_header->gdt_limit = __BOOT_DS + 7;
+       trampoline_header->gdt_base = __pa(boot_gdt);
+#else
+       /*
+        * Some AMD processors will #GP(0) if EFER.LMA is set in WRMSR
+        * so we need to mask it out.
+        */
+       rdmsrl(MSR_EFER, efer);
+       trampoline_header->efer = efer & ~EFER_LMA;
+
+       trampoline_header->start = (u64) secondary_startup_64;
+       trampoline_cr4_features = &trampoline_header->cr4;
+       *trampoline_cr4_features = read_cr4();
+
+       trampoline_pgd = (u64 *) __va(real_mode_header->trampoline_pgd);
+       trampoline_pgd[0] = __pa(level3_ident_pgt) + _KERNPG_TABLE;
+       trampoline_pgd[511] = __pa(level3_kernel_pgt) + _KERNPG_TABLE;
+#endif
+}
+
+/*
+ * set_real_mode_permissions() gets called very early, to guarantee the
+ * availability of low memory.  This is before the proper kernel page
+ * tables are set up, so we cannot set page permissions in that
+ * function.  Thus, we use an arch_initcall instead.
+ */
+static int __init set_real_mode_permissions(void)
+{
+       unsigned char *base = (unsigned char *) real_mode_header;
+       size_t size = PAGE_ALIGN(real_mode_blob_end - real_mode_blob);
+
+       size_t ro_size =
+               PAGE_ALIGN(real_mode_header->ro_end) -
+               __pa(base);
+
+       size_t text_size =
+               PAGE_ALIGN(real_mode_header->ro_end) -
+               real_mode_header->text_start;
+
+       unsigned long text_start =
+               (unsigned long) __va(real_mode_header->text_start);
+
+       set_memory_nx((unsigned long) base, size >> PAGE_SHIFT);
+       set_memory_ro((unsigned long) base, ro_size >> PAGE_SHIFT);
+       set_memory_x((unsigned long) text_start, text_size >> PAGE_SHIFT);
+
+       return 0;
+}
+
+arch_initcall(set_real_mode_permissions);
diff --git a/arch/x86/realmode/rm/.gitignore b/arch/x86/realmode/rm/.gitignore
new file mode 100644 (file)
index 0000000..b6ed3a2
--- /dev/null
@@ -0,0 +1,3 @@
+pasyms.h
+realmode.lds
+realmode.relocs
diff --git a/arch/x86/realmode/rm/Makefile b/arch/x86/realmode/rm/Makefile
new file mode 100644 (file)
index 0000000..5b84a2d
--- /dev/null
@@ -0,0 +1,82 @@
+#
+# arch/x86/realmode/Makefile
+#
+# This file is subject to the terms and conditions of the GNU General Public
+# License.  See the file "COPYING" in the main directory of this archive
+# for more details.
+#
+#
+
+always := realmode.bin realmode.relocs
+
+wakeup-objs    := wakeup_asm.o wakemain.o video-mode.o
+wakeup-objs    += copy.o bioscall.o regs.o
+# The link order of the video-*.o modules can matter.  In particular,
+# video-vga.o *must* be listed first, followed by video-vesa.o.
+# Hardware-specific drivers should follow in the order they should be
+# probed, and video-bios.o should typically be last.
+wakeup-objs    += video-vga.o
+wakeup-objs    += video-vesa.o
+wakeup-objs    += video-bios.o
+
+realmode-y                     += header.o
+realmode-y                     += trampoline_$(BITS).o
+realmode-y                     += stack.o
+realmode-$(CONFIG_X86_32)      += reboot_32.o
+realmode-$(CONFIG_ACPI_SLEEP)  += $(wakeup-objs)
+
+targets        += $(realmode-y)
+
+REALMODE_OBJS = $(addprefix $(obj)/,$(realmode-y))
+
+sed-pasyms := -n -r -e 's/^([0-9a-fA-F]+) [ABCDGRSTVW] (.+)$$/pa_\2 = \2;/p'
+
+quiet_cmd_pasyms = PASYMS  $@
+      cmd_pasyms = $(NM) $(filter-out FORCE,$^) | \
+                  sed $(sed-pasyms) | sort | uniq > $@
+
+targets += pasyms.h
+$(obj)/pasyms.h: $(REALMODE_OBJS) FORCE
+       $(call if_changed,pasyms)
+
+targets += realmode.lds
+$(obj)/realmode.lds: $(obj)/pasyms.h
+
+LDFLAGS_realmode.elf := --emit-relocs -T
+CPPFLAGS_realmode.lds += -P -C -I$(obj)
+
+targets += realmode.elf
+$(obj)/realmode.elf: $(obj)/realmode.lds $(REALMODE_OBJS) FORCE
+       $(call if_changed,ld)
+
+OBJCOPYFLAGS_realmode.bin := -O binary
+
+targets += realmode.bin
+$(obj)/realmode.bin: $(obj)/realmode.elf $(obj)/realmode.relocs
+       $(call if_changed,objcopy)
+
+quiet_cmd_relocs = RELOCS  $@
+      cmd_relocs = arch/x86/tools/relocs --realmode $< > $@
+
+targets += realmode.relocs
+$(obj)/realmode.relocs: $(obj)/realmode.elf FORCE
+       $(call if_changed,relocs)
+
+# ---------------------------------------------------------------------------
+
+# How to compile the 16-bit code.  Note we always compile for -march=i386,
+# that way we can complain to the user if the CPU is insufficient.
+KBUILD_CFLAGS  := $(LINUXINCLUDE) -m32 -g -Os -D_SETUP -D__KERNEL__ -D_WAKEUP \
+                  -I$(srctree)/arch/x86/boot \
+                  -DDISABLE_BRANCH_PROFILING \
+                  -Wall -Wstrict-prototypes \
+                  -march=i386 -mregparm=3 \
+                  -include $(srctree)/$(src)/../../boot/code16gcc.h \
+                  -fno-strict-aliasing -fomit-frame-pointer \
+                  $(call cc-option, -ffreestanding) \
+                  $(call cc-option, -fno-toplevel-reorder,\
+                       $(call cc-option, -fno-unit-at-a-time)) \
+                  $(call cc-option, -fno-stack-protector) \
+                  $(call cc-option, -mpreferred-stack-boundary=2)
+KBUILD_AFLAGS  := $(KBUILD_CFLAGS) -D__ASSEMBLY__
+GCOV_PROFILE := n
diff --git a/arch/x86/realmode/rm/bioscall.S b/arch/x86/realmode/rm/bioscall.S
new file mode 100644 (file)
index 0000000..16162d1
--- /dev/null
@@ -0,0 +1 @@
+#include "../../boot/bioscall.S"
diff --git a/arch/x86/realmode/rm/copy.S b/arch/x86/realmode/rm/copy.S
new file mode 100644 (file)
index 0000000..b785e6f
--- /dev/null
@@ -0,0 +1 @@
+#include "../../boot/copy.S"
diff --git a/arch/x86/realmode/rm/header.S b/arch/x86/realmode/rm/header.S
new file mode 100644 (file)
index 0000000..fadf483
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Real-mode blob header; this should match realmode.h and be
+ * readonly; for mutable data instead add pointers into the .data
+ * or .bss sections as appropriate.
+ */
+
+#include <linux/linkage.h>
+#include <asm/page_types.h>
+
+#include "realmode.h"
+       
+       .section ".header", "a"
+
+       .balign 16
+GLOBAL(real_mode_header)
+       .long   pa_text_start
+       .long   pa_ro_end
+       /* SMP trampoline */
+       .long   pa_trampoline_start
+       .long   pa_trampoline_status
+       .long   pa_trampoline_header
+#ifdef CONFIG_X86_64
+       .long   pa_trampoline_pgd;
+#endif
+       /* ACPI S3 wakeup */
+#ifdef CONFIG_ACPI_SLEEP
+       .long   pa_wakeup_start
+       .long   pa_wakeup_header
+#endif
+       /* APM/BIOS reboot */
+#ifdef CONFIG_X86_32
+       .long   pa_machine_real_restart_asm
+#endif
+END(real_mode_header)
+
+       /* End signature, used to verify integrity */
+       .section ".signature","a"
+       .balign 4
+GLOBAL(end_signature)
+       .long   REALMODE_END_SIGNATURE
+END(end_signature)
diff --git a/arch/x86/realmode/rm/realmode.h b/arch/x86/realmode/rm/realmode.h
new file mode 100644 (file)
index 0000000..d74cff6
--- /dev/null
@@ -0,0 +1,21 @@
+#ifndef ARCH_X86_REALMODE_RM_REALMODE_H
+#define ARCH_X86_REALMODE_RM_REALMODE_H
+
+#ifdef __ASSEMBLY__
+
+/*
+ * 16-bit ljmpw to the real_mode_seg
+ *
+ * This must be open-coded since gas will choke on using a
+ * relocatable symbol for the segment portion.
+ */
+#define LJMPW_RM(to)   .byte 0xea ; .word (to), real_mode_seg
+
+#endif /* __ASSEMBLY__ */
+
+/*
+ * Signature at the end of the realmode region
+ */
+#define REALMODE_END_SIGNATURE 0x65a22c82
+
+#endif /* ARCH_X86_REALMODE_RM_REALMODE_H */
diff --git a/arch/x86/realmode/rm/realmode.lds.S b/arch/x86/realmode/rm/realmode.lds.S
new file mode 100644 (file)
index 0000000..86b2e8d
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * realmode.lds.S
+ *
+ * Linker script for the real-mode code
+ */
+
+#include <asm/page_types.h>
+
+#undef i386
+
+OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
+OUTPUT_ARCH(i386)
+
+SECTIONS
+{
+       real_mode_seg = 0;
+
+       . = 0;
+       .header : {
+               pa_real_mode_base = .;
+               *(.header)
+       }
+
+       . = ALIGN(4);
+       .rodata : {
+               *(.rodata)
+               *(.rodata.*)
+               . = ALIGN(16);
+               video_cards = .;
+               *(.videocards)
+               video_cards_end = .;
+       }
+
+       . = ALIGN(PAGE_SIZE);
+       pa_text_start = .;
+       .text : {
+               *(.text)
+               *(.text.*)
+       }
+
+       .text32 : {
+               *(.text32)
+               *(.text32.*)
+       }
+
+       .text64 : {
+               *(.text64)
+               *(.text64.*)
+       }
+       pa_ro_end = .;
+
+       . = ALIGN(PAGE_SIZE);
+       .data : {
+               *(.data)
+               *(.data.*)
+       }
+
+       . = ALIGN(128);
+       .bss : {
+               *(.bss*)
+       }
+
+       /* End signature for integrity checking */
+       . = ALIGN(4);
+       .signature : {
+               *(.signature)
+       }
+
+       /DISCARD/ : {
+               *(.note*)
+               *(.debug*)
+               *(.eh_frame*)
+       }
+
+#include "pasyms.h"
+}
diff --git a/arch/x86/realmode/rm/reboot_32.S b/arch/x86/realmode/rm/reboot_32.S
new file mode 100644 (file)
index 0000000..1140448
--- /dev/null
@@ -0,0 +1,132 @@
+#include <linux/linkage.h>
+#include <linux/init.h>
+#include <asm/segment.h>
+#include <asm/page_types.h>
+#include "realmode.h"
+
+/*
+ * The following code and data reboots the machine by switching to real
+ * mode and jumping to the BIOS reset entry point, as if the CPU has
+ * really been reset.  The previous version asked the keyboard
+ * controller to pulse the CPU reset line, which is more thorough, but
+ * doesn't work with at least one type of 486 motherboard.  It is easy
+ * to stop this code working; hence the copious comments.
+ *
+ * This code is called with the restart type (0 = BIOS, 1 = APM) in %eax.
+ */
+       .section ".text32", "ax"
+       .code32
+
+       .balign 16
+ENTRY(machine_real_restart_asm)
+       /* Set up the IDT for real mode. */
+       lidtl   pa_machine_real_restart_idt
+
+       /*
+        * Set up a GDT from which we can load segment descriptors for real
+        * mode.  The GDT is not used in real mode; it is just needed here to
+        * prepare the descriptors.
+        */
+       lgdtl   pa_machine_real_restart_gdt
+
+       /*
+        * Load the data segment registers with 16-bit compatible values
+        */
+       movl    $16, %ecx
+       movl    %ecx, %ds
+       movl    %ecx, %es
+       movl    %ecx, %fs
+       movl    %ecx, %gs
+       movl    %ecx, %ss
+       ljmpw   $8, $1f
+
+/*
+ * This is 16-bit protected mode code to disable paging and the cache,
+ * switch to real mode and jump to the BIOS reset code.
+ *
+ * The instruction that switches to real mode by writing to CR0 must be
+ * followed immediately by a far jump instruction, which set CS to a
+ * valid value for real mode, and flushes the prefetch queue to avoid
+ * running instructions that have already been decoded in protected
+ * mode.
+ *
+ * Clears all the flags except ET, especially PG (paging), PE
+ * (protected-mode enable) and TS (task switch for coprocessor state
+ * save).  Flushes the TLB after paging has been disabled.  Sets CD and
+ * NW, to disable the cache on a 486, and invalidates the cache.  This
+ * is more like the state of a 486 after reset.  I don't know if
+ * something else should be done for other chips.
+ *
+ * More could be done here to set up the registers as if a CPU reset had
+ * occurred; hopefully real BIOSs don't assume much.  This is not the
+ * actual BIOS entry point, anyway (that is at 0xfffffff0).
+ *
+ * Most of this work is probably excessive, but it is what is tested.
+ */
+       .text
+       .code16
+
+       .balign 16
+machine_real_restart_asm16:
+1:
+       xorl    %ecx, %ecx
+       movl    %cr0, %edx
+       andl    $0x00000011, %edx
+       orl     $0x60000000, %edx
+       movl    %edx, %cr0
+       movl    %ecx, %cr3
+       movl    %cr0, %edx
+       testl   $0x60000000, %edx       /* If no cache bits -> no wbinvd */
+       jz      2f
+       wbinvd
+2:
+       andb    $0x10, %dl
+       movl    %edx, %cr0
+       LJMPW_RM(3f)
+3:
+       andw    %ax, %ax
+       jz      bios
+
+apm:
+       movw    $0x1000, %ax
+       movw    %ax, %ss
+       movw    $0xf000, %sp
+       movw    $0x5307, %ax
+       movw    $0x0001, %bx
+       movw    $0x0003, %cx
+       int     $0x15
+       /* This should never return... */
+
+bios:
+       ljmpw   $0xf000, $0xfff0
+
+       .section ".rodata", "a"
+
+       .balign 16
+GLOBAL(machine_real_restart_idt)
+       .word   0xffff          /* Length - real mode default value */
+       .long   0               /* Base - real mode default value */
+END(machine_real_restart_idt)
+
+       .balign 16
+GLOBAL(machine_real_restart_gdt)
+       /* Self-pointer */
+       .word   0xffff          /* Length - real mode default value */
+       .long   pa_machine_real_restart_gdt
+       .word   0
+
+       /*
+        * 16-bit code segment pointing to real_mode_seg
+        * Selector value 8
+        */
+       .word   0xffff          /* Limit */
+       .long   0x9b000000 + pa_real_mode_base
+       .word   0
+
+       /*
+        * 16-bit data segment with the selector value 16 = 0x10 and
+        * base value 0x100; since this is consistent with real mode
+        * semantics we don't have to reload the segments once CR0.PE = 0.
+        */
+       .quad   GDT_ENTRY(0x0093, 0x100, 0xffff)
+END(machine_real_restart_gdt)
diff --git a/arch/x86/realmode/rm/regs.c b/arch/x86/realmode/rm/regs.c
new file mode 100644 (file)
index 0000000..fbb15b9
--- /dev/null
@@ -0,0 +1 @@
+#include "../../boot/regs.c"
diff --git a/arch/x86/realmode/rm/stack.S b/arch/x86/realmode/rm/stack.S
new file mode 100644 (file)
index 0000000..867ae87
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Common heap and stack allocations
+ */
+
+#include <linux/linkage.h>
+
+       .data
+GLOBAL(HEAP)
+       .long   rm_heap
+GLOBAL(heap_end)
+       .long   rm_stack
+
+       .bss
+       .balign 16
+GLOBAL(rm_heap)
+       .space  2048
+GLOBAL(rm_stack)
+       .space  2048
+GLOBAL(rm_stack_end)
diff --git a/arch/x86/realmode/rm/trampoline_32.S b/arch/x86/realmode/rm/trampoline_32.S
new file mode 100644 (file)
index 0000000..c1b2791
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ *
+ *     Trampoline.S    Derived from Setup.S by Linus Torvalds
+ *
+ *     4 Jan 1997 Michael Chastain: changed to gnu as.
+ *
+ *     This is only used for booting secondary CPUs in SMP machine
+ *
+ *     Entry: CS:IP point to the start of our code, we are
+ *     in real mode with no stack, but the rest of the
+ *     trampoline page to make our stack and everything else
+ *     is a mystery.
+ *
+ *     We jump into arch/x86/kernel/head_32.S.
+ *
+ *     On entry to trampoline_start, the processor is in real mode
+ *     with 16-bit addressing and 16-bit data.  CS has some value
+ *     and IP is zero.  Thus, we load CS to the physical segment
+ *     of the real mode code before doing anything further.
+ */
+
+#include <linux/linkage.h>
+#include <linux/init.h>
+#include <asm/segment.h>
+#include <asm/page_types.h>
+#include "realmode.h"
+
+       .text
+       .code16
+
+       .balign PAGE_SIZE
+ENTRY(trampoline_start)
+       wbinvd                  # Needed for NUMA-Q should be harmless for others
+
+       LJMPW_RM(1f)
+1:
+       mov     %cs, %ax        # Code and data in the same place
+       mov     %ax, %ds
+
+       cli                     # We should be safe anyway
+
+       movl    tr_start, %eax  # where we need to go
+
+       movl    $0xA5A5A5A5, trampoline_status
+                               # write marker for master knows we're running
+
+       /*
+        * GDT tables in non default location kernel can be beyond 16MB and
+        * lgdt will not be able to load the address as in real mode default
+        * operand size is 16bit. Use lgdtl instead to force operand size
+        * to 32 bit.
+        */
+       lidtl   tr_idt                  # load idt with 0, 0
+       lgdtl   tr_gdt                  # load gdt with whatever is appropriate
+
+       movw    $1, %dx                 # protected mode (PE) bit
+       lmsw    %dx                     # into protected mode
+
+       ljmpl   $__BOOT_CS, $pa_startup_32
+
+       .section ".text32","ax"
+       .code32
+ENTRY(startup_32)                      # note: also used from wakeup_asm.S
+       jmp     *%eax
+
+       .bss
+       .balign 8
+GLOBAL(trampoline_header)
+       tr_start:               .space  4
+       tr_gdt_pad:             .space  2
+       tr_gdt:                 .space  6
+END(trampoline_header)
+       
+#include "trampoline_common.S"
diff --git a/arch/x86/realmode/rm/trampoline_64.S b/arch/x86/realmode/rm/trampoline_64.S
new file mode 100644 (file)
index 0000000..bb360dc
--- /dev/null
@@ -0,0 +1,153 @@
+/*
+ *
+ *     Trampoline.S    Derived from Setup.S by Linus Torvalds
+ *
+ *     4 Jan 1997 Michael Chastain: changed to gnu as.
+ *     15 Sept 2005 Eric Biederman: 64bit PIC support
+ *
+ *     Entry: CS:IP point to the start of our code, we are
+ *     in real mode with no stack, but the rest of the
+ *     trampoline page to make our stack and everything else
+ *     is a mystery.
+ *
+ *     On entry to trampoline_start, the processor is in real mode
+ *     with 16-bit addressing and 16-bit data.  CS has some value
+ *     and IP is zero.  Thus, data addresses need to be absolute
+ *     (no relocation) and are taken with regard to r_base.
+ *
+ *     With the addition of trampoline_level4_pgt this code can
+ *     now enter a 64bit kernel that lives at arbitrary 64bit
+ *     physical addresses.
+ *
+ *     If you work on this file, check the object module with objdump
+ *     --full-contents --reloc to make sure there are no relocation
+ *     entries.
+ */
+
+#include <linux/linkage.h>
+#include <linux/init.h>
+#include <asm/pgtable_types.h>
+#include <asm/page_types.h>
+#include <asm/msr.h>
+#include <asm/segment.h>
+#include <asm/processor-flags.h>
+#include "realmode.h"
+
+       .text
+       .code16
+
+       .balign PAGE_SIZE
+ENTRY(trampoline_start)
+       cli                     # We should be safe anyway
+       wbinvd
+
+       LJMPW_RM(1f)
+1:
+       mov     %cs, %ax        # Code and data in the same place
+       mov     %ax, %ds
+       mov     %ax, %es
+       mov     %ax, %ss
+
+       movl    $0xA5A5A5A5, trampoline_status
+       # write marker for master knows we're running
+
+       # Setup stack
+       movl    $rm_stack_end, %esp
+
+       call    verify_cpu              # Verify the cpu supports long mode
+       testl   %eax, %eax              # Check for return code
+       jnz     no_longmode
+
+       /*
+        * GDT tables in non default location kernel can be beyond 16MB and
+        * lgdt will not be able to load the address as in real mode default
+        * operand size is 16bit. Use lgdtl instead to force operand size
+        * to 32 bit.
+        */
+
+       lidtl   tr_idt  # load idt with 0, 0
+       lgdtl   tr_gdt  # load gdt with whatever is appropriate
+
+       movw    $__KERNEL_DS, %dx       # Data segment descriptor
+
+       # Enable protected mode
+       movl    $X86_CR0_PE, %eax       # protected mode (PE) bit
+       movl    %eax, %cr0              # into protected mode
+
+       # flush prefetch and jump to startup_32
+       ljmpl   $__KERNEL32_CS, $pa_startup_32
+
+no_longmode:
+       hlt
+       jmp no_longmode
+#include "../kernel/verify_cpu.S"
+
+       .section ".text32","ax"
+       .code32
+       .balign 4
+ENTRY(startup_32)
+       movl    %edx, %ss
+       addl    $pa_real_mode_base, %esp
+       movl    %edx, %ds
+       movl    %edx, %es
+       movl    %edx, %fs
+       movl    %edx, %gs
+
+       movl    pa_tr_cr4, %eax
+       movl    %eax, %cr4              # Enable PAE mode
+
+       # Setup trampoline 4 level pagetables
+       movl    $pa_trampoline_pgd, %eax
+       movl    %eax, %cr3
+
+       # Set up EFER
+       movl    pa_tr_efer, %eax
+       movl    pa_tr_efer + 4, %edx
+       movl    $MSR_EFER, %ecx
+       wrmsr
+
+       # Enable paging and in turn activate Long Mode
+       movl    $(X86_CR0_PG | X86_CR0_WP | X86_CR0_PE), %eax
+       movl    %eax, %cr0
+
+       /*
+        * At this point we're in long mode but in 32bit compatibility mode
+        * with EFER.LME = 1, CS.L = 0, CS.D = 1 (and in turn
+        * EFER.LMA = 1). Now we want to jump in 64bit mode, to do that we use
+        * the new gdt/idt that has __KERNEL_CS with CS.L = 1.
+        */
+       ljmpl   $__KERNEL_CS, $pa_startup_64
+
+       .section ".text64","ax"
+       .code64
+       .balign 4
+ENTRY(startup_64)
+       # Now jump into the kernel using virtual addresses
+       jmpq    *tr_start(%rip)
+
+       .section ".rodata","a"
+       # Duplicate the global descriptor table
+       # so the kernel can live anywhere
+       .balign 16
+       .globl tr_gdt
+tr_gdt:
+       .short  tr_gdt_end - tr_gdt - 1 # gdt limit
+       .long   pa_tr_gdt
+       .short  0
+       .quad   0x00cf9b000000ffff      # __KERNEL32_CS
+       .quad   0x00af9b000000ffff      # __KERNEL_CS
+       .quad   0x00cf93000000ffff      # __KERNEL_DS
+tr_gdt_end:
+
+       .bss
+       .balign PAGE_SIZE
+GLOBAL(trampoline_pgd)         .space  PAGE_SIZE
+
+       .balign 8
+GLOBAL(trampoline_header)
+       tr_start:               .space  8
+       GLOBAL(tr_efer)         .space  8
+       GLOBAL(tr_cr4)          .space  4
+END(trampoline_header)
+
+#include "trampoline_common.S"
diff --git a/arch/x86/realmode/rm/trampoline_common.S b/arch/x86/realmode/rm/trampoline_common.S
new file mode 100644 (file)
index 0000000..b1ecdb9
--- /dev/null
@@ -0,0 +1,7 @@
+       .section ".rodata","a"
+       .balign 16
+tr_idt: .fill 1, 6, 0
+
+       .bss
+       .balign 4
+GLOBAL(trampoline_status)      .space  4
diff --git a/arch/x86/realmode/rm/video-bios.c b/arch/x86/realmode/rm/video-bios.c
new file mode 100644 (file)
index 0000000..848b25a
--- /dev/null
@@ -0,0 +1 @@
+#include "../../boot/video-bios.c"
diff --git a/arch/x86/realmode/rm/video-mode.c b/arch/x86/realmode/rm/video-mode.c
new file mode 100644 (file)
index 0000000..2a98b7e
--- /dev/null
@@ -0,0 +1 @@
+#include "../../boot/video-mode.c"
diff --git a/arch/x86/realmode/rm/video-vesa.c b/arch/x86/realmode/rm/video-vesa.c
new file mode 100644 (file)
index 0000000..413eddd
--- /dev/null
@@ -0,0 +1 @@
+#include "../../boot/video-vesa.c"
diff --git a/arch/x86/realmode/rm/video-vga.c b/arch/x86/realmode/rm/video-vga.c
new file mode 100644 (file)
index 0000000..3085f5c
--- /dev/null
@@ -0,0 +1 @@
+#include "../../boot/video-vga.c"
diff --git a/arch/x86/realmode/rm/wakemain.c b/arch/x86/realmode/rm/wakemain.c
new file mode 100644 (file)
index 0000000..91405d5
--- /dev/null
@@ -0,0 +1,82 @@
+#include "wakeup.h"
+#include "boot.h"
+
+static void udelay(int loops)
+{
+       while (loops--)
+               io_delay();     /* Approximately 1 us */
+}
+
+static void beep(unsigned int hz)
+{
+       u8 enable;
+
+       if (!hz) {
+               enable = 0x00;          /* Turn off speaker */
+       } else {
+               u16 div = 1193181/hz;
+
+               outb(0xb6, 0x43);       /* Ctr 2, squarewave, load, binary */
+               io_delay();
+               outb(div, 0x42);        /* LSB of counter */
+               io_delay();
+               outb(div >> 8, 0x42);   /* MSB of counter */
+               io_delay();
+
+               enable = 0x03;          /* Turn on speaker */
+       }
+       inb(0x61);              /* Dummy read of System Control Port B */
+       io_delay();
+       outb(enable, 0x61);     /* Enable timer 2 output to speaker */
+       io_delay();
+}
+
+#define DOT_HZ         880
+#define DASH_HZ                587
+#define US_PER_DOT     125000
+
+/* Okay, this is totally silly, but it's kind of fun. */
+static void send_morse(const char *pattern)
+{
+       char s;
+
+       while ((s = *pattern++)) {
+               switch (s) {
+               case '.':
+                       beep(DOT_HZ);
+                       udelay(US_PER_DOT);
+                       beep(0);
+                       udelay(US_PER_DOT);
+                       break;
+               case '-':
+                       beep(DASH_HZ);
+                       udelay(US_PER_DOT * 3);
+                       beep(0);
+                       udelay(US_PER_DOT);
+                       break;
+               default:        /* Assume it's a space */
+                       udelay(US_PER_DOT * 3);
+                       break;
+               }
+       }
+}
+
+void main(void)
+{
+       /* Kill machine if structures are wrong */
+       if (wakeup_header.real_magic != 0x12345678)
+               while (1)
+                       ;
+
+       if (wakeup_header.realmode_flags & 4)
+               send_morse("...-");
+
+       if (wakeup_header.realmode_flags & 1)
+               asm volatile("lcallw   $0xc000,$3");
+
+       if (wakeup_header.realmode_flags & 2) {
+               /* Need to call BIOS */
+               probe_cards(0);
+               set_mode(wakeup_header.video_mode);
+       }
+}
diff --git a/arch/x86/realmode/rm/wakeup.h b/arch/x86/realmode/rm/wakeup.h
new file mode 100644 (file)
index 0000000..9317e00
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Definitions for the wakeup data structure at the head of the
+ * wakeup code.
+ */
+
+#ifndef ARCH_X86_KERNEL_ACPI_RM_WAKEUP_H
+#define ARCH_X86_KERNEL_ACPI_RM_WAKEUP_H
+
+#ifndef __ASSEMBLY__
+#include <linux/types.h>
+
+/* This must match data at wakeup.S */
+struct wakeup_header {
+       u16 video_mode;         /* Video mode number */
+       u32 pmode_entry;        /* Protected mode resume point, 32-bit only */
+       u16 pmode_cs;
+       u32 pmode_cr0;          /* Protected mode cr0 */
+       u32 pmode_cr3;          /* Protected mode cr3 */
+       u32 pmode_cr4;          /* Protected mode cr4 */
+       u32 pmode_efer_low;     /* Protected mode EFER */
+       u32 pmode_efer_high;
+       u64 pmode_gdt;
+       u32 pmode_misc_en_low;  /* Protected mode MISC_ENABLE */
+       u32 pmode_misc_en_high;
+       u32 pmode_behavior;     /* Wakeup routine behavior flags */
+       u32 realmode_flags;
+       u32 real_magic;
+       u32 signature;          /* To check we have correct structure */
+} __attribute__((__packed__));
+
+extern struct wakeup_header wakeup_header;
+#endif
+
+#define WAKEUP_HEADER_OFFSET   8
+#define WAKEUP_HEADER_SIGNATURE 0x51ee1111
+
+/* Wakeup behavior bits */
+#define WAKEUP_BEHAVIOR_RESTORE_MISC_ENABLE     0
+
+#endif /* ARCH_X86_KERNEL_ACPI_RM_WAKEUP_H */
diff --git a/arch/x86/realmode/rm/wakeup_asm.S b/arch/x86/realmode/rm/wakeup_asm.S
new file mode 100644 (file)
index 0000000..8905166
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+ * ACPI wakeup real mode startup stub
+ */
+#include <linux/linkage.h>
+#include <asm/segment.h>
+#include <asm/msr-index.h>
+#include <asm/page_types.h>
+#include <asm/pgtable_types.h>
+#include <asm/processor-flags.h>
+#include "realmode.h"
+#include "wakeup.h"
+
+       .code16
+
+/* This should match the structure in wakeup.h */
+       .section ".data", "aw"
+
+       .balign 16
+GLOBAL(wakeup_header)
+       video_mode:     .short  0       /* Video mode number */
+       pmode_entry:    .long   0
+       pmode_cs:       .short  __KERNEL_CS
+       pmode_cr0:      .long   0       /* Saved %cr0 */
+       pmode_cr3:      .long   0       /* Saved %cr3 */
+       pmode_cr4:      .long   0       /* Saved %cr4 */
+       pmode_efer:     .quad   0       /* Saved EFER */
+       pmode_gdt:      .quad   0
+       pmode_misc_en:  .quad   0       /* Saved MISC_ENABLE MSR */
+       pmode_behavior: .long   0       /* Wakeup behavior flags */
+       realmode_flags: .long   0
+       real_magic:     .long   0
+       signature:      .long   WAKEUP_HEADER_SIGNATURE
+END(wakeup_header)
+
+       .text
+       .code16
+
+       .balign 16
+ENTRY(wakeup_start)
+       cli
+       cld
+
+       LJMPW_RM(3f)
+3:
+       /* Apparently some dimwit BIOS programmers don't know how to
+          program a PM to RM transition, and we might end up here with
+          junk in the data segment descriptor registers.  The only way
+          to repair that is to go into PM and fix it ourselves... */
+       movw    $16, %cx
+       lgdtl   %cs:wakeup_gdt
+       movl    %cr0, %eax
+       orb     $X86_CR0_PE, %al
+       movl    %eax, %cr0
+       ljmpw   $8, $2f
+2:
+       movw    %cx, %ds
+       movw    %cx, %es
+       movw    %cx, %ss
+       movw    %cx, %fs
+       movw    %cx, %gs
+
+       andb    $~X86_CR0_PE, %al
+       movl    %eax, %cr0
+       LJMPW_RM(3f)
+3:
+       /* Set up segments */
+       movw    %cs, %ax
+       movw    %ax, %ss
+       movl    $rm_stack_end, %esp
+       movw    %ax, %ds
+       movw    %ax, %es
+       movw    %ax, %fs
+       movw    %ax, %gs
+
+       lidtl   wakeup_idt
+
+       /* Clear the EFLAGS */
+       pushl   $0
+       popfl
+
+       /* Check header signature... */
+       movl    signature, %eax
+       cmpl    $WAKEUP_HEADER_SIGNATURE, %eax
+       jne     bogus_real_magic
+
+       /* Check we really have everything... */
+       movl    end_signature, %eax
+       cmpl    $REALMODE_END_SIGNATURE, %eax
+       jne     bogus_real_magic
+
+       /* Call the C code */
+       calll   main
+
+       /* Restore MISC_ENABLE before entering protected mode, in case
+          BIOS decided to clear XD_DISABLE during S3. */
+       movl    pmode_behavior, %eax
+       btl     $WAKEUP_BEHAVIOR_RESTORE_MISC_ENABLE, %eax
+       jnc     1f
+
+       movl    pmode_misc_en, %eax
+       movl    pmode_misc_en + 4, %edx
+       movl    $MSR_IA32_MISC_ENABLE, %ecx
+       wrmsr
+1:
+
+       /* Do any other stuff... */
+
+#ifndef CONFIG_64BIT
+       /* This could also be done in C code... */
+       movl    pmode_cr3, %eax
+       movl    %eax, %cr3
+
+       movl    pmode_cr4, %ecx
+       jecxz   1f
+       movl    %ecx, %cr4
+1:
+       movl    pmode_efer, %eax
+       movl    pmode_efer + 4, %edx
+       movl    %eax, %ecx
+       orl     %edx, %ecx
+       jz      1f
+       movl    $MSR_EFER, %ecx
+       wrmsr
+1:
+
+       lgdtl   pmode_gdt
+
+       /* This really couldn't... */
+       movl    pmode_entry, %eax
+       movl    pmode_cr0, %ecx
+       movl    %ecx, %cr0
+       ljmpl   $__KERNEL_CS, $pa_startup_32
+       /* -> jmp *%eax in trampoline_32.S */
+#else
+       jmp     trampoline_start
+#endif
+
+bogus_real_magic:
+1:
+       hlt
+       jmp     1b
+
+       .section ".rodata","a"
+
+       /*
+        * Set up the wakeup GDT.  We set these up as Big Real Mode,
+        * that is, with limits set to 4 GB.  At least the Lenovo
+        * Thinkpad X61 is known to need this for the video BIOS
+        * initialization quirk to work; this is likely to also
+        * be the case for other laptops or integrated video devices.
+        */
+
+       .balign 16
+GLOBAL(wakeup_gdt)
+       .word   3*8-1           /* Self-descriptor */
+       .long   pa_wakeup_gdt
+       .word   0
+
+       .word   0xffff          /* 16-bit code segment @ real_mode_base */
+       .long   0x9b000000 + pa_real_mode_base
+       .word   0x008f          /* big real mode */
+
+       .word   0xffff          /* 16-bit data segment @ real_mode_base */
+       .long   0x93000000 + pa_real_mode_base
+       .word   0x008f          /* big real mode */
+END(wakeup_gdt)
+
+       .section ".rodata","a"
+       .balign 8
+
+       /* This is the standard real-mode IDT */
+       .balign 16
+GLOBAL(wakeup_idt)
+       .word   0xffff          /* limit */
+       .long   0               /* address */
+       .word   0
+END(wakeup_idt)
diff --git a/arch/x86/realmode/rmpiggy.S b/arch/x86/realmode/rmpiggy.S
new file mode 100644 (file)
index 0000000..204c6ec
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Wrapper script for the realmode binary as a transport object
+ * before copying to low memory.
+ */
+#include <linux/linkage.h>
+#include <asm/page_types.h>
+
+       .section ".init.data","aw"
+
+       .balign PAGE_SIZE
+
+GLOBAL(real_mode_blob)
+       .incbin "arch/x86/realmode/rm/realmode.bin"
+END(real_mode_blob)
+
+GLOBAL(real_mode_blob_end);
+
+GLOBAL(real_mode_relocs)
+       .incbin "arch/x86/realmode/rm/realmode.relocs"
+END(real_mode_relocs)
index 29f9f0554f7de0244e7120ea69fec26640bf7dce..7a35a6e71d44332d351cdeb9ec28e96c6467c7b6 100644 (file)
 346    i386    setns                   sys_setns
 347    i386    process_vm_readv        sys_process_vm_readv            compat_sys_process_vm_readv
 348    i386    process_vm_writev       sys_process_vm_writev           compat_sys_process_vm_writev
+349    i386    kcmp                    sys_kcmp
index dd29a9ea27c560a9d2fcb6e1c2983f8b8e9be407..51171aeff0dc31483cdc6526e641459b0d64deb3 100644 (file)
 309    common  getcpu                  sys_getcpu
 310    64      process_vm_readv        sys_process_vm_readv
 311    64      process_vm_writev       sys_process_vm_writev
+312    64      kcmp                    sys_kcmp
+
 #
 # x32-specific system call numbers start at 512 to avoid cache impact
 # for native 64-bit operation.
index b685296d44641b091f5d9c565e7e596914e97d39..5a1847d619306e5f0ed5ed5570c53c69a8ce2315 100644 (file)
@@ -77,6 +77,13 @@ static const char * const sym_regex_kernel[S_NSYMTYPES] = {
 
 
 static const char * const sym_regex_realmode[S_NSYMTYPES] = {
+/*
+ * These symbols are known to be relative, even if the linker marks them
+ * as absolute (typically defined outside any section in the linker script.)
+ */
+       [S_REL] =
+       "^pa_",
+
 /*
  * These are 16-bit segment symbols when compiling 16-bit code.
  */
index bb0fb03b9f85f268912e7b6851f74787c8ee8e4a..a508cea135033eba3c1c7facba8aebd04a1a0983 100644 (file)
@@ -486,7 +486,6 @@ long sys_sigreturn(struct pt_regs *regs)
            copy_from_user(&set.sig[1], extramask, sig_size))
                goto segfault;
 
-       sigdelsetmask(&set, ~_BLOCKABLE);
        set_current_blocked(&set);
 
        if (copy_sc_from_user(&current->thread.regs, sc))
@@ -600,7 +599,6 @@ long sys_rt_sigreturn(struct pt_regs *regs)
        if (copy_from_user(&set, &uc->uc_sigmask, sizeof(set)))
                goto segfault;
 
-       sigdelsetmask(&set, ~_BLOCKABLE);
        set_current_blocked(&set);
 
        if (copy_sc_from_user(&current->thread.regs, &uc->uc_mcontext))
index 75f33b2a59336a865d7f8abf1dada19763696b70..e74df9548a025c2d66b3052f8ac0002695e59498 100644 (file)
@@ -1116,7 +1116,10 @@ static const struct pv_cpu_ops xen_cpu_ops __initconst = {
        .wbinvd = native_wbinvd,
 
        .read_msr = native_read_msr_safe,
+       .rdmsr_regs = native_rdmsr_safe_regs,
        .write_msr = xen_write_msr_safe,
+       .wrmsr_regs = native_wrmsr_safe_regs,
+
        .read_tsc = native_read_tsc,
        .read_pmc = native_read_pmc,
 
index c5e4ec0598d24b8ce1406eb73ce83cb580da014b..b9f8e5850d3a4e1c9bc1dbe3ff6ee0b7dbc47cc9 100644 (file)
@@ -30,8 +30,6 @@
 
 #define DEBUG_SIG  0
 
-#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
-
 extern struct task_struct *coproc_owners[];
 
 struct rt_sigframe
@@ -261,7 +259,6 @@ asmlinkage long xtensa_rt_sigreturn(long a0, long a1, long a2, long a3,
        if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
                goto badframe;
 
-       sigdelsetmask(&set, ~_BLOCKABLE);
        set_current_blocked(&set);
 
        if (restore_sigcontext(regs, frame))
@@ -452,15 +449,6 @@ static void do_signal(struct pt_regs *regs)
        siginfo_t info;
        int signr;
        struct k_sigaction ka;
-       sigset_t oldset;
-
-       if (try_to_freeze())
-               goto no_signal;
-
-       if (test_thread_flag(TIF_RESTORE_SIGMASK))
-               oldset = &current->saved_sigmask;
-       else
-               oldset = &current->blocked;
 
        task_pt_regs(current)->icountlevel = 0;
 
@@ -501,19 +489,17 @@ static void do_signal(struct pt_regs *regs)
 
                /* Whee!  Actually deliver the signal.  */
                /* Set up the stack frame */
-               ret = setup_frame(signr, &ka, &info, oldset, regs);
+               ret = setup_frame(signr, &ka, &info, sigmask_to_save(), regs);
                if (ret)
                        return;
 
-               clear_thread_flag(TIF_RESTORE_SIGMASK);
-               block_sigmask(&ka, signr);
+               signal_delivered(signr, info, ka, regs, 0);
                if (current->ptrace & PT_SINGLESTEP)
                        task_pt_regs(current)->icountlevel = 1;
 
                return;
        }
 
-no_signal:
        /* Did we come from a system call? */
        if ((signed) regs->syscall >= 0) {
                /* Restart the system call - no handlers present */
@@ -532,8 +518,7 @@ no_signal:
        }
 
        /* If there's no signal to deliver, we just restore the saved mask.  */
-       if (test_and_clear_thread_flag(TIF_RESTORE_SIGMASK))
-               set_current_blocked(&current->saved_sigmask);
+       restore_saved_sigmask();
 
        if (current->ptrace & PT_SINGLESTEP)
                task_pt_regs(current)->icountlevel = 1;
@@ -548,9 +533,6 @@ void do_notify_resume(struct pt_regs *regs)
        if (test_thread_flag(TIF_SIGPENDING))
                do_signal(regs);
 
-       if (test_and_clear_thread_flag(TIF_NOTIFY_RESUME)) {
+       if (test_and_clear_thread_flag(TIF_NOTIFY_RESUME))
                tracehook_notify_resume(regs);
-               if (current->replacement_session_keyring)
-                       key_replace_session_keyring();
-       }
 }
index 3199b76f795de33f9db31e140aad75a716a51d44..421bef9c4c48d26c035792716064e9f273253c6e 100644 (file)
@@ -23,8 +23,6 @@ config IOSCHED_DEADLINE
 
 config IOSCHED_CFQ
        tristate "CFQ I/O scheduler"
-       # If BLK_CGROUP is a module, CFQ has to be built as module.
-       depends on (BLK_CGROUP=m && m) || !BLK_CGROUP || BLK_CGROUP=y
        default y
        ---help---
          The CFQ I/O scheduler tries to distribute bandwidth equally
@@ -34,8 +32,6 @@ config IOSCHED_CFQ
 
          This is the default I/O scheduler.
 
-         Note: If BLK_CGROUP=m, then CFQ can be built only as module.
-
 config CFQ_GROUP_IOSCHED
        bool "CFQ Group Scheduling support"
        depends on IOSCHED_CFQ && BLK_CGROUP
index 126c341955de162cba789040d0908b1d147c943c..02cf6335e9bdc5bb940ec89fcdbfe63746f9b5df 100644 (file)
  *                   Nauman Rafique <nauman@google.com>
  */
 #include <linux/ioprio.h>
-#include <linux/seq_file.h>
 #include <linux/kdev_t.h>
 #include <linux/module.h>
 #include <linux/err.h>
 #include <linux/blkdev.h>
 #include <linux/slab.h>
-#include "blk-cgroup.h"
 #include <linux/genhd.h>
+#include <linux/delay.h>
+#include <linux/atomic.h>
+#include "blk-cgroup.h"
+#include "blk.h"
 
 #define MAX_KEY_LEN 100
 
-static DEFINE_SPINLOCK(blkio_list_lock);
-static LIST_HEAD(blkio_list);
+static DEFINE_MUTEX(blkcg_pol_mutex);
 
-struct blkio_cgroup blkio_root_cgroup = { .weight = 2*BLKIO_WEIGHT_DEFAULT };
-EXPORT_SYMBOL_GPL(blkio_root_cgroup);
+struct blkcg blkcg_root = { .cfq_weight = 2 * CFQ_WEIGHT_DEFAULT };
+EXPORT_SYMBOL_GPL(blkcg_root);
 
-/* for encoding cft->private value on file */
-#define BLKIOFILE_PRIVATE(x, val)      (((x) << 16) | (val))
-/* What policy owns the file, proportional or throttle */
-#define BLKIOFILE_POLICY(val)          (((val) >> 16) & 0xffff)
-#define BLKIOFILE_ATTR(val)            ((val) & 0xffff)
+static struct blkcg_policy *blkcg_policy[BLKCG_MAX_POLS];
 
-static inline void blkio_policy_insert_node(struct blkio_cgroup *blkcg,
-                                           struct blkio_policy_node *pn)
+struct blkcg *cgroup_to_blkcg(struct cgroup *cgroup)
 {
-       list_add(&pn->node, &blkcg->policy_list);
+       return container_of(cgroup_subsys_state(cgroup, blkio_subsys_id),
+                           struct blkcg, css);
 }
+EXPORT_SYMBOL_GPL(cgroup_to_blkcg);
 
-static inline bool cftype_blkg_same_policy(struct cftype *cft,
-                       struct blkio_group *blkg)
+static struct blkcg *task_blkcg(struct task_struct *tsk)
 {
-       enum blkio_policy_id plid = BLKIOFILE_POLICY(cft->private);
-
-       if (blkg->plid == plid)
-               return 1;
-
-       return 0;
+       return container_of(task_subsys_state(tsk, blkio_subsys_id),
+                           struct blkcg, css);
 }
 
-/* Determines if policy node matches cgroup file being accessed */
-static inline bool pn_matches_cftype(struct cftype *cft,
-                       struct blkio_policy_node *pn)
+struct blkcg *bio_blkcg(struct bio *bio)
 {
-       enum blkio_policy_id plid = BLKIOFILE_POLICY(cft->private);
-       int fileid = BLKIOFILE_ATTR(cft->private);
-
-       return (plid == pn->plid && fileid == pn->fileid);
+       if (bio && bio->bi_css)
+               return container_of(bio->bi_css, struct blkcg, css);
+       return task_blkcg(current);
 }
+EXPORT_SYMBOL_GPL(bio_blkcg);
 
-/* Must be called with blkcg->lock held */
-static inline void blkio_policy_delete_node(struct blkio_policy_node *pn)
+static bool blkcg_policy_enabled(struct request_queue *q,
+                                const struct blkcg_policy *pol)
 {
-       list_del(&pn->node);
+       return pol && test_bit(pol->plid, q->blkcg_pols);
 }
 
-/* Must be called with blkcg->lock held */
-static struct blkio_policy_node *
-blkio_policy_search_node(const struct blkio_cgroup *blkcg, dev_t dev,
-               enum blkio_policy_id plid, int fileid)
+/**
+ * blkg_free - free a blkg
+ * @blkg: blkg to free
+ *
+ * Free @blkg which may be partially allocated.
+ */
+static void blkg_free(struct blkcg_gq *blkg)
 {
-       struct blkio_policy_node *pn;
-
-       list_for_each_entry(pn, &blkcg->policy_list, node) {
-               if (pn->dev == dev && pn->plid == plid && pn->fileid == fileid)
-                       return pn;
-       }
+       int i;
 
-       return NULL;
-}
+       if (!blkg)
+               return;
 
-struct blkio_cgroup *cgroup_to_blkio_cgroup(struct cgroup *cgroup)
-{
-       return container_of(cgroup_subsys_state(cgroup, blkio_subsys_id),
-                           struct blkio_cgroup, css);
-}
-EXPORT_SYMBOL_GPL(cgroup_to_blkio_cgroup);
+       for (i = 0; i < BLKCG_MAX_POLS; i++) {
+               struct blkcg_policy *pol = blkcg_policy[i];
+               struct blkg_policy_data *pd = blkg->pd[i];
 
-struct blkio_cgroup *task_blkio_cgroup(struct task_struct *tsk)
-{
-       return container_of(task_subsys_state(tsk, blkio_subsys_id),
-                           struct blkio_cgroup, css);
-}
-EXPORT_SYMBOL_GPL(task_blkio_cgroup);
+               if (!pd)
+                       continue;
 
-static inline void
-blkio_update_group_weight(struct blkio_group *blkg, unsigned int weight)
-{
-       struct blkio_policy_type *blkiop;
+               if (pol && pol->pd_exit_fn)
+                       pol->pd_exit_fn(blkg);
 
-       list_for_each_entry(blkiop, &blkio_list, list) {
-               /* If this policy does not own the blkg, do not send updates */
-               if (blkiop->plid != blkg->plid)
-                       continue;
-               if (blkiop->ops.blkio_update_group_weight_fn)
-                       blkiop->ops.blkio_update_group_weight_fn(blkg->key,
-                                                       blkg, weight);
+               kfree(pd);
        }
+
+       kfree(blkg);
 }
 
-static inline void blkio_update_group_bps(struct blkio_group *blkg, u64 bps,
-                               int fileid)
+/**
+ * blkg_alloc - allocate a blkg
+ * @blkcg: block cgroup the new blkg is associated with
+ * @q: request_queue the new blkg is associated with
+ *
+ * Allocate a new blkg assocating @blkcg and @q.
+ */
+static struct blkcg_gq *blkg_alloc(struct blkcg *blkcg, struct request_queue *q)
 {
-       struct blkio_policy_type *blkiop;
-
-       list_for_each_entry(blkiop, &blkio_list, list) {
-
-               /* If this policy does not own the blkg, do not send updates */
-               if (blkiop->plid != blkg->plid)
-                       continue;
-
-               if (fileid == BLKIO_THROTL_read_bps_device
-                   && blkiop->ops.blkio_update_group_read_bps_fn)
-                       blkiop->ops.blkio_update_group_read_bps_fn(blkg->key,
-                                                               blkg, bps);
+       struct blkcg_gq *blkg;
+       int i;
 
-               if (fileid == BLKIO_THROTL_write_bps_device
-                   && blkiop->ops.blkio_update_group_write_bps_fn)
-                       blkiop->ops.blkio_update_group_write_bps_fn(blkg->key,
-                                                               blkg, bps);
-       }
-}
+       /* alloc and init base part */
+       blkg = kzalloc_node(sizeof(*blkg), GFP_ATOMIC, q->node);
+       if (!blkg)
+               return NULL;
 
-static inline void blkio_update_group_iops(struct blkio_group *blkg,
-                       unsigned int iops, int fileid)
-{
-       struct blkio_policy_type *blkiop;
+       blkg->q = q;
+       INIT_LIST_HEAD(&blkg->q_node);
+       blkg->blkcg = blkcg;
+       blkg->refcnt = 1;
 
-       list_for_each_entry(blkiop, &blkio_list, list) {
+       for (i = 0; i < BLKCG_MAX_POLS; i++) {
+               struct blkcg_policy *pol = blkcg_policy[i];
+               struct blkg_policy_data *pd;
 
-               /* If this policy does not own the blkg, do not send updates */
-               if (blkiop->plid != blkg->plid)
+               if (!blkcg_policy_enabled(q, pol))
                        continue;
 
-               if (fileid == BLKIO_THROTL_read_iops_device
-                   && blkiop->ops.blkio_update_group_read_iops_fn)
-                       blkiop->ops.blkio_update_group_read_iops_fn(blkg->key,
-                                                               blkg, iops);
+               /* alloc per-policy data and attach it to blkg */
+               pd = kzalloc_node(pol->pd_size, GFP_ATOMIC, q->node);
+               if (!pd) {
+                       blkg_free(blkg);
+                       return NULL;
+               }
 
-               if (fileid == BLKIO_THROTL_write_iops_device
-                   && blkiop->ops.blkio_update_group_write_iops_fn)
-                       blkiop->ops.blkio_update_group_write_iops_fn(blkg->key,
-                                                               blkg,iops);
+               blkg->pd[i] = pd;
+               pd->blkg = blkg;
        }
-}
 
-/*
- * Add to the appropriate stat variable depending on the request type.
- * This should be called with the blkg->stats_lock held.
- */
-static void blkio_add_stat(uint64_t *stat, uint64_t add, bool direction,
-                               bool sync)
-{
-       if (direction)
-               stat[BLKIO_STAT_WRITE] += add;
-       else
-               stat[BLKIO_STAT_READ] += add;
-       if (sync)
-               stat[BLKIO_STAT_SYNC] += add;
-       else
-               stat[BLKIO_STAT_ASYNC] += add;
-}
+       /* invoke per-policy init */
+       for (i = 0; i < BLKCG_MAX_POLS; i++) {
+               struct blkcg_policy *pol = blkcg_policy[i];
 
-/*
- * Decrements the appropriate stat variable if non-zero depending on the
- * request type. Panics on value being zero.
- * This should be called with the blkg->stats_lock held.
- */
-static void blkio_check_and_dec_stat(uint64_t *stat, bool direction, bool sync)
-{
-       if (direction) {
-               BUG_ON(stat[BLKIO_STAT_WRITE] == 0);
-               stat[BLKIO_STAT_WRITE]--;
-       } else {
-               BUG_ON(stat[BLKIO_STAT_READ] == 0);
-               stat[BLKIO_STAT_READ]--;
-       }
-       if (sync) {
-               BUG_ON(stat[BLKIO_STAT_SYNC] == 0);
-               stat[BLKIO_STAT_SYNC]--;
-       } else {
-               BUG_ON(stat[BLKIO_STAT_ASYNC] == 0);
-               stat[BLKIO_STAT_ASYNC]--;
+               if (blkcg_policy_enabled(blkg->q, pol))
+                       pol->pd_init_fn(blkg);
        }
-}
 
-#ifdef CONFIG_DEBUG_BLK_CGROUP
-/* This should be called with the blkg->stats_lock held. */
-static void blkio_set_start_group_wait_time(struct blkio_group *blkg,
-                                               struct blkio_group *curr_blkg)
-{
-       if (blkio_blkg_waiting(&blkg->stats))
-               return;
-       if (blkg == curr_blkg)
-               return;
-       blkg->stats.start_group_wait_time = sched_clock();
-       blkio_mark_blkg_waiting(&blkg->stats);
+       return blkg;
 }
 
-/* This should be called with the blkg->stats_lock held. */
-static void blkio_update_group_wait_time(struct blkio_group_stats *stats)
+static struct blkcg_gq *__blkg_lookup(struct blkcg *blkcg,
+                                     struct request_queue *q)
 {
-       unsigned long long now;
+       struct blkcg_gq *blkg;
 
-       if (!blkio_blkg_waiting(stats))
-               return;
+       blkg = rcu_dereference(blkcg->blkg_hint);
+       if (blkg && blkg->q == q)
+               return blkg;
 
-       now = sched_clock();
-       if (time_after64(now, stats->start_group_wait_time))
-               stats->group_wait_time += now - stats->start_group_wait_time;
-       blkio_clear_blkg_waiting(stats);
+       /*
+        * Hint didn't match.  Look up from the radix tree.  Note that we
+        * may not be holding queue_lock and thus are not sure whether
+        * @blkg from blkg_tree has already been removed or not, so we
+        * can't update hint to the lookup result.  Leave it to the caller.
+        */
+       blkg = radix_tree_lookup(&blkcg->blkg_tree, q->id);
+       if (blkg && blkg->q == q)
+               return blkg;
+
+       return NULL;
 }
 
-/* This should be called with the blkg->stats_lock held. */
-static void blkio_end_empty_time(struct blkio_group_stats *stats)
+/**
+ * blkg_lookup - lookup blkg for the specified blkcg - q pair
+ * @blkcg: blkcg of interest
+ * @q: request_queue of interest
+ *
+ * Lookup blkg for the @blkcg - @q pair.  This function should be called
+ * under RCU read lock and is guaranteed to return %NULL if @q is bypassing
+ * - see blk_queue_bypass_start() for details.
+ */
+struct blkcg_gq *blkg_lookup(struct blkcg *blkcg, struct request_queue *q)
 {
-       unsigned long long now;
-
-       if (!blkio_blkg_empty(stats))
-               return;
+       WARN_ON_ONCE(!rcu_read_lock_held());
 
-       now = sched_clock();
-       if (time_after64(now, stats->start_empty_time))
-               stats->empty_time += now - stats->start_empty_time;
-       blkio_clear_blkg_empty(stats);
+       if (unlikely(blk_queue_bypass(q)))
+               return NULL;
+       return __blkg_lookup(blkcg, q);
 }
+EXPORT_SYMBOL_GPL(blkg_lookup);
 
-void blkiocg_update_set_idle_time_stats(struct blkio_group *blkg)
+static struct blkcg_gq *__blkg_lookup_create(struct blkcg *blkcg,
+                                            struct request_queue *q)
+       __releases(q->queue_lock) __acquires(q->queue_lock)
 {
-       unsigned long flags;
+       struct blkcg_gq *blkg;
+       int ret;
 
-       spin_lock_irqsave(&blkg->stats_lock, flags);
-       BUG_ON(blkio_blkg_idling(&blkg->stats));
-       blkg->stats.start_idle_time = sched_clock();
-       blkio_mark_blkg_idling(&blkg->stats);
-       spin_unlock_irqrestore(&blkg->stats_lock, flags);
-}
-EXPORT_SYMBOL_GPL(blkiocg_update_set_idle_time_stats);
+       WARN_ON_ONCE(!rcu_read_lock_held());
+       lockdep_assert_held(q->queue_lock);
 
-void blkiocg_update_idle_time_stats(struct blkio_group *blkg)
-{
-       unsigned long flags;
-       unsigned long long now;
-       struct blkio_group_stats *stats;
-
-       spin_lock_irqsave(&blkg->stats_lock, flags);
-       stats = &blkg->stats;
-       if (blkio_blkg_idling(stats)) {
-               now = sched_clock();
-               if (time_after64(now, stats->start_idle_time))
-                       stats->idle_time += now - stats->start_idle_time;
-               blkio_clear_blkg_idling(stats);
+       /* lookup and update hint on success, see __blkg_lookup() for details */
+       blkg = __blkg_lookup(blkcg, q);
+       if (blkg) {
+               rcu_assign_pointer(blkcg->blkg_hint, blkg);
+               return blkg;
        }
-       spin_unlock_irqrestore(&blkg->stats_lock, flags);
-}
-EXPORT_SYMBOL_GPL(blkiocg_update_idle_time_stats);
 
-void blkiocg_update_avg_queue_size_stats(struct blkio_group *blkg)
-{
-       unsigned long flags;
-       struct blkio_group_stats *stats;
-
-       spin_lock_irqsave(&blkg->stats_lock, flags);
-       stats = &blkg->stats;
-       stats->avg_queue_size_sum +=
-                       stats->stat_arr[BLKIO_STAT_QUEUED][BLKIO_STAT_READ] +
-                       stats->stat_arr[BLKIO_STAT_QUEUED][BLKIO_STAT_WRITE];
-       stats->avg_queue_size_samples++;
-       blkio_update_group_wait_time(stats);
-       spin_unlock_irqrestore(&blkg->stats_lock, flags);
-}
-EXPORT_SYMBOL_GPL(blkiocg_update_avg_queue_size_stats);
+       /* blkg holds a reference to blkcg */
+       if (!css_tryget(&blkcg->css))
+               return ERR_PTR(-EINVAL);
 
-void blkiocg_set_start_empty_time(struct blkio_group *blkg)
-{
-       unsigned long flags;
-       struct blkio_group_stats *stats;
+       /* allocate */
+       ret = -ENOMEM;
+       blkg = blkg_alloc(blkcg, q);
+       if (unlikely(!blkg))
+               goto err_put;
 
-       spin_lock_irqsave(&blkg->stats_lock, flags);
-       stats = &blkg->stats;
+       /* insert */
+       ret = radix_tree_preload(GFP_ATOMIC);
+       if (ret)
+               goto err_free;
 
-       if (stats->stat_arr[BLKIO_STAT_QUEUED][BLKIO_STAT_READ] ||
-                       stats->stat_arr[BLKIO_STAT_QUEUED][BLKIO_STAT_WRITE]) {
-               spin_unlock_irqrestore(&blkg->stats_lock, flags);
-               return;
+       spin_lock(&blkcg->lock);
+       ret = radix_tree_insert(&blkcg->blkg_tree, q->id, blkg);
+       if (likely(!ret)) {
+               hlist_add_head_rcu(&blkg->blkcg_node, &blkcg->blkg_list);
+               list_add(&blkg->q_node, &q->blkg_list);
        }
+       spin_unlock(&blkcg->lock);
 
-       /*
-        * group is already marked empty. This can happen if cfqq got new
-        * request in parent group and moved to this group while being added
-        * to service tree. Just ignore the event and move on.
-        */
-       if(blkio_blkg_empty(stats)) {
-               spin_unlock_irqrestore(&blkg->stats_lock, flags);
-               return;
-       }
+       radix_tree_preload_end();
 
-       stats->start_empty_time = sched_clock();
-       blkio_mark_blkg_empty(stats);
-       spin_unlock_irqrestore(&blkg->stats_lock, flags);
+       if (!ret)
+               return blkg;
+err_free:
+       blkg_free(blkg);
+err_put:
+       css_put(&blkcg->css);
+       return ERR_PTR(ret);
 }
-EXPORT_SYMBOL_GPL(blkiocg_set_start_empty_time);
 
-void blkiocg_update_dequeue_stats(struct blkio_group *blkg,
-                       unsigned long dequeue)
+struct blkcg_gq *blkg_lookup_create(struct blkcg *blkcg,
+                                   struct request_queue *q)
 {
-       blkg->stats.dequeue += dequeue;
-}
-EXPORT_SYMBOL_GPL(blkiocg_update_dequeue_stats);
-#else
-static inline void blkio_set_start_group_wait_time(struct blkio_group *blkg,
-                                       struct blkio_group *curr_blkg) {}
-static inline void blkio_end_empty_time(struct blkio_group_stats *stats) {}
-#endif
-
-void blkiocg_update_io_add_stats(struct blkio_group *blkg,
-                       struct blkio_group *curr_blkg, bool direction,
-                       bool sync)
-{
-       unsigned long flags;
-
-       spin_lock_irqsave(&blkg->stats_lock, flags);
-       blkio_add_stat(blkg->stats.stat_arr[BLKIO_STAT_QUEUED], 1, direction,
-                       sync);
-       blkio_end_empty_time(&blkg->stats);
-       blkio_set_start_group_wait_time(blkg, curr_blkg);
-       spin_unlock_irqrestore(&blkg->stats_lock, flags);
+       /*
+        * This could be the first entry point of blkcg implementation and
+        * we shouldn't allow anything to go through for a bypassing queue.
+        */
+       if (unlikely(blk_queue_bypass(q)))
+               return ERR_PTR(blk_queue_dead(q) ? -EINVAL : -EBUSY);
+       return __blkg_lookup_create(blkcg, q);
 }
-EXPORT_SYMBOL_GPL(blkiocg_update_io_add_stats);
+EXPORT_SYMBOL_GPL(blkg_lookup_create);
 
-void blkiocg_update_io_remove_stats(struct blkio_group *blkg,
-                                               bool direction, bool sync)
+static void blkg_destroy(struct blkcg_gq *blkg)
 {
-       unsigned long flags;
+       struct request_queue *q = blkg->q;
+       struct blkcg *blkcg = blkg->blkcg;
 
-       spin_lock_irqsave(&blkg->stats_lock, flags);
-       blkio_check_and_dec_stat(blkg->stats.stat_arr[BLKIO_STAT_QUEUED],
-                                       direction, sync);
-       spin_unlock_irqrestore(&blkg->stats_lock, flags);
-}
-EXPORT_SYMBOL_GPL(blkiocg_update_io_remove_stats);
+       lockdep_assert_held(q->queue_lock);
+       lockdep_assert_held(&blkcg->lock);
 
-void blkiocg_update_timeslice_used(struct blkio_group *blkg, unsigned long time,
-                               unsigned long unaccounted_time)
-{
-       unsigned long flags;
-
-       spin_lock_irqsave(&blkg->stats_lock, flags);
-       blkg->stats.time += time;
-#ifdef CONFIG_DEBUG_BLK_CGROUP
-       blkg->stats.unaccounted_time += unaccounted_time;
-#endif
-       spin_unlock_irqrestore(&blkg->stats_lock, flags);
-}
-EXPORT_SYMBOL_GPL(blkiocg_update_timeslice_used);
+       /* Something wrong if we are trying to remove same group twice */
+       WARN_ON_ONCE(list_empty(&blkg->q_node));
+       WARN_ON_ONCE(hlist_unhashed(&blkg->blkcg_node));
 
-/*
- * should be called under rcu read lock or queue lock to make sure blkg pointer
- * is valid.
- */
-void blkiocg_update_dispatch_stats(struct blkio_group *blkg,
-                               uint64_t bytes, bool direction, bool sync)
-{
-       struct blkio_group_stats_cpu *stats_cpu;
-       unsigned long flags;
+       radix_tree_delete(&blkcg->blkg_tree, blkg->q->id);
+       list_del_init(&blkg->q_node);
+       hlist_del_init_rcu(&blkg->blkcg_node);
 
        /*
-        * Disabling interrupts to provide mutual exclusion between two
-        * writes on same cpu. It probably is not needed for 64bit. Not
-        * optimizing that case yet.
+        * Both setting lookup hint to and clearing it from @blkg are done
+        * under queue_lock.  If it's not pointing to @blkg now, it never
+        * will.  Hint assignment itself can race safely.
         */
-       local_irq_save(flags);
-
-       stats_cpu = this_cpu_ptr(blkg->stats_cpu);
-
-       u64_stats_update_begin(&stats_cpu->syncp);
-       stats_cpu->sectors += bytes >> 9;
-       blkio_add_stat(stats_cpu->stat_arr_cpu[BLKIO_STAT_CPU_SERVICED],
-                       1, direction, sync);
-       blkio_add_stat(stats_cpu->stat_arr_cpu[BLKIO_STAT_CPU_SERVICE_BYTES],
-                       bytes, direction, sync);
-       u64_stats_update_end(&stats_cpu->syncp);
-       local_irq_restore(flags);
-}
-EXPORT_SYMBOL_GPL(blkiocg_update_dispatch_stats);
-
-void blkiocg_update_completion_stats(struct blkio_group *blkg,
-       uint64_t start_time, uint64_t io_start_time, bool direction, bool sync)
-{
-       struct blkio_group_stats *stats;
-       unsigned long flags;
-       unsigned long long now = sched_clock();
-
-       spin_lock_irqsave(&blkg->stats_lock, flags);
-       stats = &blkg->stats;
-       if (time_after64(now, io_start_time))
-               blkio_add_stat(stats->stat_arr[BLKIO_STAT_SERVICE_TIME],
-                               now - io_start_time, direction, sync);
-       if (time_after64(io_start_time, start_time))
-               blkio_add_stat(stats->stat_arr[BLKIO_STAT_WAIT_TIME],
-                               io_start_time - start_time, direction, sync);
-       spin_unlock_irqrestore(&blkg->stats_lock, flags);
-}
-EXPORT_SYMBOL_GPL(blkiocg_update_completion_stats);
-
-/*  Merged stats are per cpu.  */
-void blkiocg_update_io_merged_stats(struct blkio_group *blkg, bool direction,
-                                       bool sync)
-{
-       struct blkio_group_stats_cpu *stats_cpu;
-       unsigned long flags;
+       if (rcu_dereference_raw(blkcg->blkg_hint) == blkg)
+               rcu_assign_pointer(blkcg->blkg_hint, NULL);
 
        /*
-        * Disabling interrupts to provide mutual exclusion between two
-        * writes on same cpu. It probably is not needed for 64bit. Not
-        * optimizing that case yet.
+        * Put the reference taken at the time of creation so that when all
+        * queues are gone, group can be destroyed.
         */
-       local_irq_save(flags);
-
-       stats_cpu = this_cpu_ptr(blkg->stats_cpu);
-
-       u64_stats_update_begin(&stats_cpu->syncp);
-       blkio_add_stat(stats_cpu->stat_arr_cpu[BLKIO_STAT_CPU_MERGED], 1,
-                               direction, sync);
-       u64_stats_update_end(&stats_cpu->syncp);
-       local_irq_restore(flags);
+       blkg_put(blkg);
 }
-EXPORT_SYMBOL_GPL(blkiocg_update_io_merged_stats);
 
-/*
- * This function allocates the per cpu stats for blkio_group. Should be called
- * from sleepable context as alloc_per_cpu() requires that.
+/**
+ * blkg_destroy_all - destroy all blkgs associated with a request_queue
+ * @q: request_queue of interest
+ *
+ * Destroy all blkgs associated with @q.
  */
-int blkio_alloc_blkg_stats(struct blkio_group *blkg)
+static void blkg_destroy_all(struct request_queue *q)
 {
-       /* Allocate memory for per cpu stats */
-       blkg->stats_cpu = alloc_percpu(struct blkio_group_stats_cpu);
-       if (!blkg->stats_cpu)
-               return -ENOMEM;
-       return 0;
-}
-EXPORT_SYMBOL_GPL(blkio_alloc_blkg_stats);
+       struct blkcg_gq *blkg, *n;
 
-void blkiocg_add_blkio_group(struct blkio_cgroup *blkcg,
-               struct blkio_group *blkg, void *key, dev_t dev,
-               enum blkio_policy_id plid)
-{
-       unsigned long flags;
-
-       spin_lock_irqsave(&blkcg->lock, flags);
-       spin_lock_init(&blkg->stats_lock);
-       rcu_assign_pointer(blkg->key, key);
-       blkg->blkcg_id = css_id(&blkcg->css);
-       hlist_add_head_rcu(&blkg->blkcg_node, &blkcg->blkg_list);
-       blkg->plid = plid;
-       spin_unlock_irqrestore(&blkcg->lock, flags);
-       /* Need to take css reference ? */
-       cgroup_path(blkcg->css.cgroup, blkg->path, sizeof(blkg->path));
-       blkg->dev = dev;
-}
-EXPORT_SYMBOL_GPL(blkiocg_add_blkio_group);
+       lockdep_assert_held(q->queue_lock);
 
-static void __blkiocg_del_blkio_group(struct blkio_group *blkg)
-{
-       hlist_del_init_rcu(&blkg->blkcg_node);
-       blkg->blkcg_id = 0;
-}
+       list_for_each_entry_safe(blkg, n, &q->blkg_list, q_node) {
+               struct blkcg *blkcg = blkg->blkcg;
 
-/*
- * returns 0 if blkio_group was still on cgroup list. Otherwise returns 1
- * indicating that blk_group was unhashed by the time we got to it.
- */
-int blkiocg_del_blkio_group(struct blkio_group *blkg)
-{
-       struct blkio_cgroup *blkcg;
-       unsigned long flags;
-       struct cgroup_subsys_state *css;
-       int ret = 1;
-
-       rcu_read_lock();
-       css = css_lookup(&blkio_subsys, blkg->blkcg_id);
-       if (css) {
-               blkcg = container_of(css, struct blkio_cgroup, css);
-               spin_lock_irqsave(&blkcg->lock, flags);
-               if (!hlist_unhashed(&blkg->blkcg_node)) {
-                       __blkiocg_del_blkio_group(blkg);
-                       ret = 0;
-               }
-               spin_unlock_irqrestore(&blkcg->lock, flags);
+               spin_lock(&blkcg->lock);
+               blkg_destroy(blkg);
+               spin_unlock(&blkcg->lock);
        }
-
-       rcu_read_unlock();
-       return ret;
 }
-EXPORT_SYMBOL_GPL(blkiocg_del_blkio_group);
 
-/* called under rcu_read_lock(). */
-struct blkio_group *blkiocg_lookup_group(struct blkio_cgroup *blkcg, void *key)
+static void blkg_rcu_free(struct rcu_head *rcu_head)
 {
-       struct blkio_group *blkg;
-       struct hlist_node *n;
-       void *__key;
-
-       hlist_for_each_entry_rcu(blkg, n, &blkcg->blkg_list, blkcg_node) {
-               __key = blkg->key;
-               if (__key == key)
-                       return blkg;
-       }
-
-       return NULL;
+       blkg_free(container_of(rcu_head, struct blkcg_gq, rcu_head));
 }
-EXPORT_SYMBOL_GPL(blkiocg_lookup_group);
 
-static void blkio_reset_stats_cpu(struct blkio_group *blkg)
+void __blkg_release(struct blkcg_gq *blkg)
 {
-       struct blkio_group_stats_cpu *stats_cpu;
-       int i, j, k;
+       /* release the extra blkcg reference this blkg has been holding */
+       css_put(&blkg->blkcg->css);
+
        /*
-        * Note: On 64 bit arch this should not be an issue. This has the
-        * possibility of returning some inconsistent value on 32bit arch
-        * as 64bit update on 32bit is non atomic. Taking care of this
-        * corner case makes code very complicated, like sending IPIs to
-        * cpus, taking care of stats of offline cpus etc.
+        * A group is freed in rcu manner. But having an rcu lock does not
+        * mean that one can access all the fields of blkg and assume these
+        * are valid. For example, don't try to follow throtl_data and
+        * request queue links.
         *
-        * reset stats is anyway more of a debug feature and this sounds a
-        * corner case. So I am not complicating the code yet until and
-        * unless this becomes a real issue.
+        * Having a reference to blkg under an rcu allows acess to only
+        * values local to groups like group stats and group rate limits
         */
-       for_each_possible_cpu(i) {
-               stats_cpu = per_cpu_ptr(blkg->stats_cpu, i);
-               stats_cpu->sectors = 0;
-               for(j = 0; j < BLKIO_STAT_CPU_NR; j++)
-                       for (k = 0; k < BLKIO_STAT_TOTAL; k++)
-                               stats_cpu->stat_arr_cpu[j][k] = 0;
-       }
+       call_rcu(&blkg->rcu_head, blkg_rcu_free);
 }
+EXPORT_SYMBOL_GPL(__blkg_release);
 
-static int
-blkiocg_reset_stats(struct cgroup *cgroup, struct cftype *cftype, u64 val)
+static int blkcg_reset_stats(struct cgroup *cgroup, struct cftype *cftype,
+                            u64 val)
 {
-       struct blkio_cgroup *blkcg;
-       struct blkio_group *blkg;
-       struct blkio_group_stats *stats;
+       struct blkcg *blkcg = cgroup_to_blkcg(cgroup);
+       struct blkcg_gq *blkg;
        struct hlist_node *n;
-       uint64_t queued[BLKIO_STAT_TOTAL];
        int i;
-#ifdef CONFIG_DEBUG_BLK_CGROUP
-       bool idling, waiting, empty;
-       unsigned long long now = sched_clock();
-#endif
 
-       blkcg = cgroup_to_blkio_cgroup(cgroup);
+       mutex_lock(&blkcg_pol_mutex);
        spin_lock_irq(&blkcg->lock);
-       hlist_for_each_entry(blkg, n, &blkcg->blkg_list, blkcg_node) {
-               spin_lock(&blkg->stats_lock);
-               stats = &blkg->stats;
-#ifdef CONFIG_DEBUG_BLK_CGROUP
-               idling = blkio_blkg_idling(stats);
-               waiting = blkio_blkg_waiting(stats);
-               empty = blkio_blkg_empty(stats);
-#endif
-               for (i = 0; i < BLKIO_STAT_TOTAL; i++)
-                       queued[i] = stats->stat_arr[BLKIO_STAT_QUEUED][i];
-               memset(stats, 0, sizeof(struct blkio_group_stats));
-               for (i = 0; i < BLKIO_STAT_TOTAL; i++)
-                       stats->stat_arr[BLKIO_STAT_QUEUED][i] = queued[i];
-#ifdef CONFIG_DEBUG_BLK_CGROUP
-               if (idling) {
-                       blkio_mark_blkg_idling(stats);
-                       stats->start_idle_time = now;
-               }
-               if (waiting) {
-                       blkio_mark_blkg_waiting(stats);
-                       stats->start_group_wait_time = now;
-               }
-               if (empty) {
-                       blkio_mark_blkg_empty(stats);
-                       stats->start_empty_time = now;
-               }
-#endif
-               spin_unlock(&blkg->stats_lock);
-
-               /* Reset Per cpu stats which don't take blkg->stats_lock */
-               blkio_reset_stats_cpu(blkg);
-       }
-
-       spin_unlock_irq(&blkcg->lock);
-       return 0;
-}
-
-static void blkio_get_key_name(enum stat_sub_type type, dev_t dev, char *str,
-                               int chars_left, bool diskname_only)
-{
-       snprintf(str, chars_left, "%d:%d", MAJOR(dev), MINOR(dev));
-       chars_left -= strlen(str);
-       if (chars_left <= 0) {
-               printk(KERN_WARNING
-                       "Possibly incorrect cgroup stat display format");
-               return;
-       }
-       if (diskname_only)
-               return;
-       switch (type) {
-       case BLKIO_STAT_READ:
-               strlcat(str, " Read", chars_left);
-               break;
-       case BLKIO_STAT_WRITE:
-               strlcat(str, " Write", chars_left);
-               break;
-       case BLKIO_STAT_SYNC:
-               strlcat(str, " Sync", chars_left);
-               break;
-       case BLKIO_STAT_ASYNC:
-               strlcat(str, " Async", chars_left);
-               break;
-       case BLKIO_STAT_TOTAL:
-               strlcat(str, " Total", chars_left);
-               break;
-       default:
-               strlcat(str, " Invalid", chars_left);
-       }
-}
-
-static uint64_t blkio_fill_stat(char *str, int chars_left, uint64_t val,
-                               struct cgroup_map_cb *cb, dev_t dev)
-{
-       blkio_get_key_name(0, dev, str, chars_left, true);
-       cb->fill(cb, str, val);
-       return val;
-}
-
-
-static uint64_t blkio_read_stat_cpu(struct blkio_group *blkg,
-                       enum stat_type_cpu type, enum stat_sub_type sub_type)
-{
-       int cpu;
-       struct blkio_group_stats_cpu *stats_cpu;
-       u64 val = 0, tval;
-
-       for_each_possible_cpu(cpu) {
-               unsigned int start;
-               stats_cpu  = per_cpu_ptr(blkg->stats_cpu, cpu);
-
-               do {
-                       start = u64_stats_fetch_begin(&stats_cpu->syncp);
-                       if (type == BLKIO_STAT_CPU_SECTORS)
-                               tval = stats_cpu->sectors;
-                       else
-                               tval = stats_cpu->stat_arr_cpu[type][sub_type];
-               } while(u64_stats_fetch_retry(&stats_cpu->syncp, start));
-
-               val += tval;
-       }
-
-       return val;
-}
-
-static uint64_t blkio_get_stat_cpu(struct blkio_group *blkg,
-               struct cgroup_map_cb *cb, dev_t dev, enum stat_type_cpu type)
-{
-       uint64_t disk_total, val;
-       char key_str[MAX_KEY_LEN];
-       enum stat_sub_type sub_type;
 
-       if (type == BLKIO_STAT_CPU_SECTORS) {
-               val = blkio_read_stat_cpu(blkg, type, 0);
-               return blkio_fill_stat(key_str, MAX_KEY_LEN - 1, val, cb, dev);
-       }
-
-       for (sub_type = BLKIO_STAT_READ; sub_type < BLKIO_STAT_TOTAL;
-                       sub_type++) {
-               blkio_get_key_name(sub_type, dev, key_str, MAX_KEY_LEN, false);
-               val = blkio_read_stat_cpu(blkg, type, sub_type);
-               cb->fill(cb, key_str, val);
-       }
-
-       disk_total = blkio_read_stat_cpu(blkg, type, BLKIO_STAT_READ) +
-                       blkio_read_stat_cpu(blkg, type, BLKIO_STAT_WRITE);
-
-       blkio_get_key_name(BLKIO_STAT_TOTAL, dev, key_str, MAX_KEY_LEN, false);
-       cb->fill(cb, key_str, disk_total);
-       return disk_total;
-}
-
-/* This should be called with blkg->stats_lock held */
-static uint64_t blkio_get_stat(struct blkio_group *blkg,
-               struct cgroup_map_cb *cb, dev_t dev, enum stat_type type)
-{
-       uint64_t disk_total;
-       char key_str[MAX_KEY_LEN];
-       enum stat_sub_type sub_type;
-
-       if (type == BLKIO_STAT_TIME)
-               return blkio_fill_stat(key_str, MAX_KEY_LEN - 1,
-                                       blkg->stats.time, cb, dev);
-#ifdef CONFIG_DEBUG_BLK_CGROUP
-       if (type == BLKIO_STAT_UNACCOUNTED_TIME)
-               return blkio_fill_stat(key_str, MAX_KEY_LEN - 1,
-                                       blkg->stats.unaccounted_time, cb, dev);
-       if (type == BLKIO_STAT_AVG_QUEUE_SIZE) {
-               uint64_t sum = blkg->stats.avg_queue_size_sum;
-               uint64_t samples = blkg->stats.avg_queue_size_samples;
-               if (samples)
-                       do_div(sum, samples);
-               else
-                       sum = 0;
-               return blkio_fill_stat(key_str, MAX_KEY_LEN - 1, sum, cb, dev);
-       }
-       if (type == BLKIO_STAT_GROUP_WAIT_TIME)
-               return blkio_fill_stat(key_str, MAX_KEY_LEN - 1,
-                                       blkg->stats.group_wait_time, cb, dev);
-       if (type == BLKIO_STAT_IDLE_TIME)
-               return blkio_fill_stat(key_str, MAX_KEY_LEN - 1,
-                                       blkg->stats.idle_time, cb, dev);
-       if (type == BLKIO_STAT_EMPTY_TIME)
-               return blkio_fill_stat(key_str, MAX_KEY_LEN - 1,
-                                       blkg->stats.empty_time, cb, dev);
-       if (type == BLKIO_STAT_DEQUEUE)
-               return blkio_fill_stat(key_str, MAX_KEY_LEN - 1,
-                                       blkg->stats.dequeue, cb, dev);
-#endif
-
-       for (sub_type = BLKIO_STAT_READ; sub_type < BLKIO_STAT_TOTAL;
-                       sub_type++) {
-               blkio_get_key_name(sub_type, dev, key_str, MAX_KEY_LEN, false);
-               cb->fill(cb, key_str, blkg->stats.stat_arr[type][sub_type]);
-       }
-       disk_total = blkg->stats.stat_arr[type][BLKIO_STAT_READ] +
-                       blkg->stats.stat_arr[type][BLKIO_STAT_WRITE];
-       blkio_get_key_name(BLKIO_STAT_TOTAL, dev, key_str, MAX_KEY_LEN, false);
-       cb->fill(cb, key_str, disk_total);
-       return disk_total;
-}
-
-static int blkio_policy_parse_and_set(char *buf,
-       struct blkio_policy_node *newpn, enum blkio_policy_id plid, int fileid)
-{
-       struct gendisk *disk = NULL;
-       char *s[4], *p, *major_s = NULL, *minor_s = NULL;
-       unsigned long major, minor;
-       int i = 0, ret = -EINVAL;
-       int part;
-       dev_t dev;
-       u64 temp;
-
-       memset(s, 0, sizeof(s));
-
-       while ((p = strsep(&buf, " ")) != NULL) {
-               if (!*p)
-                       continue;
-
-               s[i++] = p;
-
-               /* Prevent from inputing too many things */
-               if (i == 3)
-                       break;
-       }
-
-       if (i != 2)
-               goto out;
-
-       p = strsep(&s[0], ":");
-       if (p != NULL)
-               major_s = p;
-       else
-               goto out;
-
-       minor_s = s[0];
-       if (!minor_s)
-               goto out;
-
-       if (strict_strtoul(major_s, 10, &major))
-               goto out;
-
-       if (strict_strtoul(minor_s, 10, &minor))
-               goto out;
-
-       dev = MKDEV(major, minor);
-
-       if (strict_strtoull(s[1], 10, &temp))
-               goto out;
-
-       /* For rule removal, do not check for device presence. */
-       if (temp) {
-               disk = get_gendisk(dev, &part);
-               if (!disk || part) {
-                       ret = -ENODEV;
-                       goto out;
-               }
-       }
-
-       newpn->dev = dev;
-
-       switch (plid) {
-       case BLKIO_POLICY_PROP:
-               if ((temp < BLKIO_WEIGHT_MIN && temp > 0) ||
-                    temp > BLKIO_WEIGHT_MAX)
-                       goto out;
-
-               newpn->plid = plid;
-               newpn->fileid = fileid;
-               newpn->val.weight = temp;
-               break;
-       case BLKIO_POLICY_THROTL:
-               switch(fileid) {
-               case BLKIO_THROTL_read_bps_device:
-               case BLKIO_THROTL_write_bps_device:
-                       newpn->plid = plid;
-                       newpn->fileid = fileid;
-                       newpn->val.bps = temp;
-                       break;
-               case BLKIO_THROTL_read_iops_device:
-               case BLKIO_THROTL_write_iops_device:
-                       if (temp > THROTL_IOPS_MAX)
-                               goto out;
-
-                       newpn->plid = plid;
-                       newpn->fileid = fileid;
-                       newpn->val.iops = (unsigned int)temp;
-                       break;
-               }
-               break;
-       default:
-               BUG();
-       }
-       ret = 0;
-out:
-       put_disk(disk);
-       return ret;
-}
-
-unsigned int blkcg_get_weight(struct blkio_cgroup *blkcg,
-                             dev_t dev)
-{
-       struct blkio_policy_node *pn;
-       unsigned long flags;
-       unsigned int weight;
-
-       spin_lock_irqsave(&blkcg->lock, flags);
-
-       pn = blkio_policy_search_node(blkcg, dev, BLKIO_POLICY_PROP,
-                               BLKIO_PROP_weight_device);
-       if (pn)
-               weight = pn->val.weight;
-       else
-               weight = blkcg->weight;
-
-       spin_unlock_irqrestore(&blkcg->lock, flags);
-
-       return weight;
-}
-EXPORT_SYMBOL_GPL(blkcg_get_weight);
-
-uint64_t blkcg_get_read_bps(struct blkio_cgroup *blkcg, dev_t dev)
-{
-       struct blkio_policy_node *pn;
-       unsigned long flags;
-       uint64_t bps = -1;
-
-       spin_lock_irqsave(&blkcg->lock, flags);
-       pn = blkio_policy_search_node(blkcg, dev, BLKIO_POLICY_THROTL,
-                               BLKIO_THROTL_read_bps_device);
-       if (pn)
-               bps = pn->val.bps;
-       spin_unlock_irqrestore(&blkcg->lock, flags);
-
-       return bps;
-}
-
-uint64_t blkcg_get_write_bps(struct blkio_cgroup *blkcg, dev_t dev)
-{
-       struct blkio_policy_node *pn;
-       unsigned long flags;
-       uint64_t bps = -1;
-
-       spin_lock_irqsave(&blkcg->lock, flags);
-       pn = blkio_policy_search_node(blkcg, dev, BLKIO_POLICY_THROTL,
-                               BLKIO_THROTL_write_bps_device);
-       if (pn)
-               bps = pn->val.bps;
-       spin_unlock_irqrestore(&blkcg->lock, flags);
-
-       return bps;
-}
-
-unsigned int blkcg_get_read_iops(struct blkio_cgroup *blkcg, dev_t dev)
-{
-       struct blkio_policy_node *pn;
-       unsigned long flags;
-       unsigned int iops = -1;
-
-       spin_lock_irqsave(&blkcg->lock, flags);
-       pn = blkio_policy_search_node(blkcg, dev, BLKIO_POLICY_THROTL,
-                               BLKIO_THROTL_read_iops_device);
-       if (pn)
-               iops = pn->val.iops;
-       spin_unlock_irqrestore(&blkcg->lock, flags);
-
-       return iops;
-}
-
-unsigned int blkcg_get_write_iops(struct blkio_cgroup *blkcg, dev_t dev)
-{
-       struct blkio_policy_node *pn;
-       unsigned long flags;
-       unsigned int iops = -1;
-
-       spin_lock_irqsave(&blkcg->lock, flags);
-       pn = blkio_policy_search_node(blkcg, dev, BLKIO_POLICY_THROTL,
-                               BLKIO_THROTL_write_iops_device);
-       if (pn)
-               iops = pn->val.iops;
-       spin_unlock_irqrestore(&blkcg->lock, flags);
-
-       return iops;
-}
+       /*
+        * Note that stat reset is racy - it doesn't synchronize against
+        * stat updates.  This is a debug feature which shouldn't exist
+        * anyway.  If you get hit by a race, retry.
+        */
+       hlist_for_each_entry(blkg, n, &blkcg->blkg_list, blkcg_node) {
+               for (i = 0; i < BLKCG_MAX_POLS; i++) {
+                       struct blkcg_policy *pol = blkcg_policy[i];
 
-/* Checks whether user asked for deleting a policy rule */
-static bool blkio_delete_rule_command(struct blkio_policy_node *pn)
-{
-       switch(pn->plid) {
-       case BLKIO_POLICY_PROP:
-               if (pn->val.weight == 0)
-                       return 1;
-               break;
-       case BLKIO_POLICY_THROTL:
-               switch(pn->fileid) {
-               case BLKIO_THROTL_read_bps_device:
-               case BLKIO_THROTL_write_bps_device:
-                       if (pn->val.bps == 0)
-                               return 1;
-                       break;
-               case BLKIO_THROTL_read_iops_device:
-               case BLKIO_THROTL_write_iops_device:
-                       if (pn->val.iops == 0)
-                               return 1;
+                       if (blkcg_policy_enabled(blkg->q, pol) &&
+                           pol->pd_reset_stats_fn)
+                               pol->pd_reset_stats_fn(blkg);
                }
-               break;
-       default:
-               BUG();
        }
 
+       spin_unlock_irq(&blkcg->lock);
+       mutex_unlock(&blkcg_pol_mutex);
        return 0;
 }
 
-static void blkio_update_policy_rule(struct blkio_policy_node *oldpn,
-                                       struct blkio_policy_node *newpn)
-{
-       switch(oldpn->plid) {
-       case BLKIO_POLICY_PROP:
-               oldpn->val.weight = newpn->val.weight;
-               break;
-       case BLKIO_POLICY_THROTL:
-               switch(newpn->fileid) {
-               case BLKIO_THROTL_read_bps_device:
-               case BLKIO_THROTL_write_bps_device:
-                       oldpn->val.bps = newpn->val.bps;
-                       break;
-               case BLKIO_THROTL_read_iops_device:
-               case BLKIO_THROTL_write_iops_device:
-                       oldpn->val.iops = newpn->val.iops;
-               }
-               break;
-       default:
-               BUG();
-       }
-}
-
-/*
- * Some rules/values in blkg have changed. Propagate those to respective
- * policies.
- */
-static void blkio_update_blkg_policy(struct blkio_cgroup *blkcg,
-               struct blkio_group *blkg, struct blkio_policy_node *pn)
+static const char *blkg_dev_name(struct blkcg_gq *blkg)
 {
-       unsigned int weight, iops;
-       u64 bps;
-
-       switch(pn->plid) {
-       case BLKIO_POLICY_PROP:
-               weight = pn->val.weight ? pn->val.weight :
-                               blkcg->weight;
-               blkio_update_group_weight(blkg, weight);
-               break;
-       case BLKIO_POLICY_THROTL:
-               switch(pn->fileid) {
-               case BLKIO_THROTL_read_bps_device:
-               case BLKIO_THROTL_write_bps_device:
-                       bps = pn->val.bps ? pn->val.bps : (-1);
-                       blkio_update_group_bps(blkg, bps, pn->fileid);
-                       break;
-               case BLKIO_THROTL_read_iops_device:
-               case BLKIO_THROTL_write_iops_device:
-                       iops = pn->val.iops ? pn->val.iops : (-1);
-                       blkio_update_group_iops(blkg, iops, pn->fileid);
-                       break;
-               }
-               break;
-       default:
-               BUG();
-       }
+       /* some drivers (floppy) instantiate a queue w/o disk registered */
+       if (blkg->q->backing_dev_info.dev)
+               return dev_name(blkg->q->backing_dev_info.dev);
+       return NULL;
 }
 
-/*
- * A policy node rule has been updated. Propagate this update to all the
- * block groups which might be affected by this update.
+/**
+ * blkcg_print_blkgs - helper for printing per-blkg data
+ * @sf: seq_file to print to
+ * @blkcg: blkcg of interest
+ * @prfill: fill function to print out a blkg
+ * @pol: policy in question
+ * @data: data to be passed to @prfill
+ * @show_total: to print out sum of prfill return values or not
+ *
+ * This function invokes @prfill on each blkg of @blkcg if pd for the
+ * policy specified by @pol exists.  @prfill is invoked with @sf, the
+ * policy data and @data.  If @show_total is %true, the sum of the return
+ * values from @prfill is printed with "Total" label at the end.
+ *
+ * This is to be used to construct print functions for
+ * cftype->read_seq_string method.
  */
-static void blkio_update_policy_node_blkg(struct blkio_cgroup *blkcg,
-                               struct blkio_policy_node *pn)
+void blkcg_print_blkgs(struct seq_file *sf, struct blkcg *blkcg,
+                      u64 (*prfill)(struct seq_file *,
+                                    struct blkg_policy_data *, int),
+                      const struct blkcg_policy *pol, int data,
+                      bool show_total)
 {
-       struct blkio_group *blkg;
+       struct blkcg_gq *blkg;
        struct hlist_node *n;
+       u64 total = 0;
 
-       spin_lock(&blkio_list_lock);
        spin_lock_irq(&blkcg->lock);
-
-       hlist_for_each_entry(blkg, n, &blkcg->blkg_list, blkcg_node) {
-               if (pn->dev != blkg->dev || pn->plid != blkg->plid)
-                       continue;
-               blkio_update_blkg_policy(blkcg, blkg, pn);
-       }
-
+       hlist_for_each_entry(blkg, n, &blkcg->blkg_list, blkcg_node)
+               if (blkcg_policy_enabled(blkg->q, pol))
+                       total += prfill(sf, blkg->pd[pol->plid], data);
        spin_unlock_irq(&blkcg->lock);
-       spin_unlock(&blkio_list_lock);
+
+       if (show_total)
+               seq_printf(sf, "Total %llu\n", (unsigned long long)total);
 }
+EXPORT_SYMBOL_GPL(blkcg_print_blkgs);
 
-static int blkiocg_file_write(struct cgroup *cgrp, struct cftype *cft,
-                                      const char *buffer)
+/**
+ * __blkg_prfill_u64 - prfill helper for a single u64 value
+ * @sf: seq_file to print to
+ * @pd: policy private data of interest
+ * @v: value to print
+ *
+ * Print @v to @sf for the device assocaited with @pd.
+ */
+u64 __blkg_prfill_u64(struct seq_file *sf, struct blkg_policy_data *pd, u64 v)
 {
-       int ret = 0;
-       char *buf;
-       struct blkio_policy_node *newpn, *pn;
-       struct blkio_cgroup *blkcg;
-       int keep_newpn = 0;
-       enum blkio_policy_id plid = BLKIOFILE_POLICY(cft->private);
-       int fileid = BLKIOFILE_ATTR(cft->private);
-
-       buf = kstrdup(buffer, GFP_KERNEL);
-       if (!buf)
-               return -ENOMEM;
-
-       newpn = kzalloc(sizeof(*newpn), GFP_KERNEL);
-       if (!newpn) {
-               ret = -ENOMEM;
-               goto free_buf;
-       }
-
-       ret = blkio_policy_parse_and_set(buf, newpn, plid, fileid);
-       if (ret)
-               goto free_newpn;
-
-       blkcg = cgroup_to_blkio_cgroup(cgrp);
-
-       spin_lock_irq(&blkcg->lock);
-
-       pn = blkio_policy_search_node(blkcg, newpn->dev, plid, fileid);
-       if (!pn) {
-               if (!blkio_delete_rule_command(newpn)) {
-                       blkio_policy_insert_node(blkcg, newpn);
-                       keep_newpn = 1;
-               }
-               spin_unlock_irq(&blkcg->lock);
-               goto update_io_group;
-       }
-
-       if (blkio_delete_rule_command(newpn)) {
-               blkio_policy_delete_node(pn);
-               kfree(pn);
-               spin_unlock_irq(&blkcg->lock);
-               goto update_io_group;
-       }
-       spin_unlock_irq(&blkcg->lock);
+       const char *dname = blkg_dev_name(pd->blkg);
 
-       blkio_update_policy_rule(pn, newpn);
+       if (!dname)
+               return 0;
 
-update_io_group:
-       blkio_update_policy_node_blkg(blkcg, newpn);
-
-free_newpn:
-       if (!keep_newpn)
-               kfree(newpn);
-free_buf:
-       kfree(buf);
-       return ret;
+       seq_printf(sf, "%s %llu\n", dname, (unsigned long long)v);
+       return v;
 }
+EXPORT_SYMBOL_GPL(__blkg_prfill_u64);
 
-static void
-blkio_print_policy_node(struct seq_file *m, struct blkio_policy_node *pn)
-{
-       switch(pn->plid) {
-               case BLKIO_POLICY_PROP:
-                       if (pn->fileid == BLKIO_PROP_weight_device)
-                               seq_printf(m, "%u:%u\t%u\n", MAJOR(pn->dev),
-                                       MINOR(pn->dev), pn->val.weight);
-                       break;
-               case BLKIO_POLICY_THROTL:
-                       switch(pn->fileid) {
-                       case BLKIO_THROTL_read_bps_device:
-                       case BLKIO_THROTL_write_bps_device:
-                               seq_printf(m, "%u:%u\t%llu\n", MAJOR(pn->dev),
-                                       MINOR(pn->dev), pn->val.bps);
-                               break;
-                       case BLKIO_THROTL_read_iops_device:
-                       case BLKIO_THROTL_write_iops_device:
-                               seq_printf(m, "%u:%u\t%u\n", MAJOR(pn->dev),
-                                       MINOR(pn->dev), pn->val.iops);
-                               break;
-                       }
-                       break;
-               default:
-                       BUG();
-       }
-}
+/**
+ * __blkg_prfill_rwstat - prfill helper for a blkg_rwstat
+ * @sf: seq_file to print to
+ * @pd: policy private data of interest
+ * @rwstat: rwstat to print
+ *
+ * Print @rwstat to @sf for the device assocaited with @pd.
+ */
+u64 __blkg_prfill_rwstat(struct seq_file *sf, struct blkg_policy_data *pd,
+                        const struct blkg_rwstat *rwstat)
+{
+       static const char *rwstr[] = {
+               [BLKG_RWSTAT_READ]      = "Read",
+               [BLKG_RWSTAT_WRITE]     = "Write",
+               [BLKG_RWSTAT_SYNC]      = "Sync",
+               [BLKG_RWSTAT_ASYNC]     = "Async",
+       };
+       const char *dname = blkg_dev_name(pd->blkg);
+       u64 v;
+       int i;
 
-/* cgroup files which read their data from policy nodes end up here */
-static void blkio_read_policy_node_files(struct cftype *cft,
-                       struct blkio_cgroup *blkcg, struct seq_file *m)
-{
-       struct blkio_policy_node *pn;
-
-       if (!list_empty(&blkcg->policy_list)) {
-               spin_lock_irq(&blkcg->lock);
-               list_for_each_entry(pn, &blkcg->policy_list, node) {
-                       if (!pn_matches_cftype(cft, pn))
-                               continue;
-                       blkio_print_policy_node(m, pn);
-               }
-               spin_unlock_irq(&blkcg->lock);
-       }
-}
+       if (!dname)
+               return 0;
 
-static int blkiocg_file_read(struct cgroup *cgrp, struct cftype *cft,
-                               struct seq_file *m)
-{
-       struct blkio_cgroup *blkcg;
-       enum blkio_policy_id plid = BLKIOFILE_POLICY(cft->private);
-       int name = BLKIOFILE_ATTR(cft->private);
-
-       blkcg = cgroup_to_blkio_cgroup(cgrp);
-
-       switch(plid) {
-       case BLKIO_POLICY_PROP:
-               switch(name) {
-               case BLKIO_PROP_weight_device:
-                       blkio_read_policy_node_files(cft, blkcg, m);
-                       return 0;
-               default:
-                       BUG();
-               }
-               break;
-       case BLKIO_POLICY_THROTL:
-               switch(name){
-               case BLKIO_THROTL_read_bps_device:
-               case BLKIO_THROTL_write_bps_device:
-               case BLKIO_THROTL_read_iops_device:
-               case BLKIO_THROTL_write_iops_device:
-                       blkio_read_policy_node_files(cft, blkcg, m);
-                       return 0;
-               default:
-                       BUG();
-               }
-               break;
-       default:
-               BUG();
-       }
+       for (i = 0; i < BLKG_RWSTAT_NR; i++)
+               seq_printf(sf, "%s %s %llu\n", dname, rwstr[i],
+                          (unsigned long long)rwstat->cnt[i]);
 
-       return 0;
+       v = rwstat->cnt[BLKG_RWSTAT_READ] + rwstat->cnt[BLKG_RWSTAT_WRITE];
+       seq_printf(sf, "%s Total %llu\n", dname, (unsigned long long)v);
+       return v;
 }
 
-static int blkio_read_blkg_stats(struct blkio_cgroup *blkcg,
-               struct cftype *cft, struct cgroup_map_cb *cb,
-               enum stat_type type, bool show_total, bool pcpu)
+/**
+ * blkg_prfill_stat - prfill callback for blkg_stat
+ * @sf: seq_file to print to
+ * @pd: policy private data of interest
+ * @off: offset to the blkg_stat in @pd
+ *
+ * prfill callback for printing a blkg_stat.
+ */
+u64 blkg_prfill_stat(struct seq_file *sf, struct blkg_policy_data *pd, int off)
 {
-       struct blkio_group *blkg;
-       struct hlist_node *n;
-       uint64_t cgroup_total = 0;
-
-       rcu_read_lock();
-       hlist_for_each_entry_rcu(blkg, n, &blkcg->blkg_list, blkcg_node) {
-               if (blkg->dev) {
-                       if (!cftype_blkg_same_policy(cft, blkg))
-                               continue;
-                       if (pcpu)
-                               cgroup_total += blkio_get_stat_cpu(blkg, cb,
-                                               blkg->dev, type);
-                       else {
-                               spin_lock_irq(&blkg->stats_lock);
-                               cgroup_total += blkio_get_stat(blkg, cb,
-                                               blkg->dev, type);
-                               spin_unlock_irq(&blkg->stats_lock);
-                       }
-               }
-       }
-       if (show_total)
-               cb->fill(cb, "Total", cgroup_total);
-       rcu_read_unlock();
-       return 0;
+       return __blkg_prfill_u64(sf, pd, blkg_stat_read((void *)pd + off));
 }
+EXPORT_SYMBOL_GPL(blkg_prfill_stat);
 
-/* All map kind of cgroup file get serviced by this function */
-static int blkiocg_file_read_map(struct cgroup *cgrp, struct cftype *cft,
-                               struct cgroup_map_cb *cb)
+/**
+ * blkg_prfill_rwstat - prfill callback for blkg_rwstat
+ * @sf: seq_file to print to
+ * @pd: policy private data of interest
+ * @off: offset to the blkg_rwstat in @pd
+ *
+ * prfill callback for printing a blkg_rwstat.
+ */
+u64 blkg_prfill_rwstat(struct seq_file *sf, struct blkg_policy_data *pd,
+                      int off)
 {
-       struct blkio_cgroup *blkcg;
-       enum blkio_policy_id plid = BLKIOFILE_POLICY(cft->private);
-       int name = BLKIOFILE_ATTR(cft->private);
-
-       blkcg = cgroup_to_blkio_cgroup(cgrp);
-
-       switch(plid) {
-       case BLKIO_POLICY_PROP:
-               switch(name) {
-               case BLKIO_PROP_time:
-                       return blkio_read_blkg_stats(blkcg, cft, cb,
-                                               BLKIO_STAT_TIME, 0, 0);
-               case BLKIO_PROP_sectors:
-                       return blkio_read_blkg_stats(blkcg, cft, cb,
-                                               BLKIO_STAT_CPU_SECTORS, 0, 1);
-               case BLKIO_PROP_io_service_bytes:
-                       return blkio_read_blkg_stats(blkcg, cft, cb,
-                                       BLKIO_STAT_CPU_SERVICE_BYTES, 1, 1);
-               case BLKIO_PROP_io_serviced:
-                       return blkio_read_blkg_stats(blkcg, cft, cb,
-                                               BLKIO_STAT_CPU_SERVICED, 1, 1);
-               case BLKIO_PROP_io_service_time:
-                       return blkio_read_blkg_stats(blkcg, cft, cb,
-                                               BLKIO_STAT_SERVICE_TIME, 1, 0);
-               case BLKIO_PROP_io_wait_time:
-                       return blkio_read_blkg_stats(blkcg, cft, cb,
-                                               BLKIO_STAT_WAIT_TIME, 1, 0);
-               case BLKIO_PROP_io_merged:
-                       return blkio_read_blkg_stats(blkcg, cft, cb,
-                                               BLKIO_STAT_CPU_MERGED, 1, 1);
-               case BLKIO_PROP_io_queued:
-                       return blkio_read_blkg_stats(blkcg, cft, cb,
-                                               BLKIO_STAT_QUEUED, 1, 0);
-#ifdef CONFIG_DEBUG_BLK_CGROUP
-               case BLKIO_PROP_unaccounted_time:
-                       return blkio_read_blkg_stats(blkcg, cft, cb,
-                                       BLKIO_STAT_UNACCOUNTED_TIME, 0, 0);
-               case BLKIO_PROP_dequeue:
-                       return blkio_read_blkg_stats(blkcg, cft, cb,
-                                               BLKIO_STAT_DEQUEUE, 0, 0);
-               case BLKIO_PROP_avg_queue_size:
-                       return blkio_read_blkg_stats(blkcg, cft, cb,
-                                       BLKIO_STAT_AVG_QUEUE_SIZE, 0, 0);
-               case BLKIO_PROP_group_wait_time:
-                       return blkio_read_blkg_stats(blkcg, cft, cb,
-                                       BLKIO_STAT_GROUP_WAIT_TIME, 0, 0);
-               case BLKIO_PROP_idle_time:
-                       return blkio_read_blkg_stats(blkcg, cft, cb,
-                                               BLKIO_STAT_IDLE_TIME, 0, 0);
-               case BLKIO_PROP_empty_time:
-                       return blkio_read_blkg_stats(blkcg, cft, cb,
-                                               BLKIO_STAT_EMPTY_TIME, 0, 0);
-#endif
-               default:
-                       BUG();
-               }
-               break;
-       case BLKIO_POLICY_THROTL:
-               switch(name){
-               case BLKIO_THROTL_io_service_bytes:
-                       return blkio_read_blkg_stats(blkcg, cft, cb,
-                                               BLKIO_STAT_CPU_SERVICE_BYTES, 1, 1);
-               case BLKIO_THROTL_io_serviced:
-                       return blkio_read_blkg_stats(blkcg, cft, cb,
-                                               BLKIO_STAT_CPU_SERVICED, 1, 1);
-               default:
-                       BUG();
-               }
-               break;
-       default:
-               BUG();
-       }
+       struct blkg_rwstat rwstat = blkg_rwstat_read((void *)pd + off);
 
-       return 0;
+       return __blkg_prfill_rwstat(sf, pd, &rwstat);
 }
+EXPORT_SYMBOL_GPL(blkg_prfill_rwstat);
 
-static int blkio_weight_write(struct blkio_cgroup *blkcg, u64 val)
+/**
+ * blkg_conf_prep - parse and prepare for per-blkg config update
+ * @blkcg: target block cgroup
+ * @pol: target policy
+ * @input: input string
+ * @ctx: blkg_conf_ctx to be filled
+ *
+ * Parse per-blkg config update from @input and initialize @ctx with the
+ * result.  @ctx->blkg points to the blkg to be updated and @ctx->v the new
+ * value.  This function returns with RCU read lock and queue lock held and
+ * must be paired with blkg_conf_finish().
+ */
+int blkg_conf_prep(struct blkcg *blkcg, const struct blkcg_policy *pol,
+                  const char *input, struct blkg_conf_ctx *ctx)
+       __acquires(rcu) __acquires(disk->queue->queue_lock)
 {
-       struct blkio_group *blkg;
-       struct hlist_node *n;
-       struct blkio_policy_node *pn;
+       struct gendisk *disk;
+       struct blkcg_gq *blkg;
+       unsigned int major, minor;
+       unsigned long long v;
+       int part, ret;
 
-       if (val < BLKIO_WEIGHT_MIN || val > BLKIO_WEIGHT_MAX)
+       if (sscanf(input, "%u:%u %llu", &major, &minor, &v) != 3)
                return -EINVAL;
 
-       spin_lock(&blkio_list_lock);
-       spin_lock_irq(&blkcg->lock);
-       blkcg->weight = (unsigned int)val;
-
-       hlist_for_each_entry(blkg, n, &blkcg->blkg_list, blkcg_node) {
-               pn = blkio_policy_search_node(blkcg, blkg->dev,
-                               BLKIO_POLICY_PROP, BLKIO_PROP_weight_device);
-               if (pn)
-                       continue;
-
-               blkio_update_group_weight(blkg, blkcg->weight);
-       }
-       spin_unlock_irq(&blkcg->lock);
-       spin_unlock(&blkio_list_lock);
-       return 0;
-}
+       disk = get_gendisk(MKDEV(major, minor), &part);
+       if (!disk || part)
+               return -EINVAL;
 
-static u64 blkiocg_file_read_u64 (struct cgroup *cgrp, struct cftype *cft) {
-       struct blkio_cgroup *blkcg;
-       enum blkio_policy_id plid = BLKIOFILE_POLICY(cft->private);
-       int name = BLKIOFILE_ATTR(cft->private);
+       rcu_read_lock();
+       spin_lock_irq(disk->queue->queue_lock);
 
-       blkcg = cgroup_to_blkio_cgroup(cgrp);
+       if (blkcg_policy_enabled(disk->queue, pol))
+               blkg = blkg_lookup_create(blkcg, disk->queue);
+       else
+               blkg = ERR_PTR(-EINVAL);
 
-       switch(plid) {
-       case BLKIO_POLICY_PROP:
-               switch(name) {
-               case BLKIO_PROP_weight:
-                       return (u64)blkcg->weight;
+       if (IS_ERR(blkg)) {
+               ret = PTR_ERR(blkg);
+               rcu_read_unlock();
+               spin_unlock_irq(disk->queue->queue_lock);
+               put_disk(disk);
+               /*
+                * If queue was bypassing, we should retry.  Do so after a
+                * short msleep().  It isn't strictly necessary but queue
+                * can be bypassing for some time and it's always nice to
+                * avoid busy looping.
+                */
+               if (ret == -EBUSY) {
+                       msleep(10);
+                       ret = restart_syscall();
                }
-               break;
-       default:
-               BUG();
+               return ret;
        }
+
+       ctx->disk = disk;
+       ctx->blkg = blkg;
+       ctx->v = v;
        return 0;
 }
+EXPORT_SYMBOL_GPL(blkg_conf_prep);
 
-static int
-blkiocg_file_write_u64(struct cgroup *cgrp, struct cftype *cft, u64 val)
+/**
+ * blkg_conf_finish - finish up per-blkg config update
+ * @ctx: blkg_conf_ctx intiailized by blkg_conf_prep()
+ *
+ * Finish up after per-blkg config update.  This function must be paired
+ * with blkg_conf_prep().
+ */
+void blkg_conf_finish(struct blkg_conf_ctx *ctx)
+       __releases(ctx->disk->queue->queue_lock) __releases(rcu)
 {
-       struct blkio_cgroup *blkcg;
-       enum blkio_policy_id plid = BLKIOFILE_POLICY(cft->private);
-       int name = BLKIOFILE_ATTR(cft->private);
-
-       blkcg = cgroup_to_blkio_cgroup(cgrp);
-
-       switch(plid) {
-       case BLKIO_POLICY_PROP:
-               switch(name) {
-               case BLKIO_PROP_weight:
-                       return blkio_weight_write(blkcg, val);
-               }
-               break;
-       default:
-               BUG();
-       }
-
-       return 0;
+       spin_unlock_irq(ctx->disk->queue->queue_lock);
+       rcu_read_unlock();
+       put_disk(ctx->disk);
 }
+EXPORT_SYMBOL_GPL(blkg_conf_finish);
 
-struct cftype blkio_files[] = {
-       {
-               .name = "weight_device",
-               .private = BLKIOFILE_PRIVATE(BLKIO_POLICY_PROP,
-                               BLKIO_PROP_weight_device),
-               .read_seq_string = blkiocg_file_read,
-               .write_string = blkiocg_file_write,
-               .max_write_len = 256,
-       },
-       {
-               .name = "weight",
-               .private = BLKIOFILE_PRIVATE(BLKIO_POLICY_PROP,
-                               BLKIO_PROP_weight),
-               .read_u64 = blkiocg_file_read_u64,
-               .write_u64 = blkiocg_file_write_u64,
-       },
-       {
-               .name = "time",
-               .private = BLKIOFILE_PRIVATE(BLKIO_POLICY_PROP,
-                               BLKIO_PROP_time),
-               .read_map = blkiocg_file_read_map,
-       },
-       {
-               .name = "sectors",
-               .private = BLKIOFILE_PRIVATE(BLKIO_POLICY_PROP,
-                               BLKIO_PROP_sectors),
-               .read_map = blkiocg_file_read_map,
-       },
-       {
-               .name = "io_service_bytes",
-               .private = BLKIOFILE_PRIVATE(BLKIO_POLICY_PROP,
-                               BLKIO_PROP_io_service_bytes),
-               .read_map = blkiocg_file_read_map,
-       },
-       {
-               .name = "io_serviced",
-               .private = BLKIOFILE_PRIVATE(BLKIO_POLICY_PROP,
-                               BLKIO_PROP_io_serviced),
-               .read_map = blkiocg_file_read_map,
-       },
-       {
-               .name = "io_service_time",
-               .private = BLKIOFILE_PRIVATE(BLKIO_POLICY_PROP,
-                               BLKIO_PROP_io_service_time),
-               .read_map = blkiocg_file_read_map,
-       },
-       {
-               .name = "io_wait_time",
-               .private = BLKIOFILE_PRIVATE(BLKIO_POLICY_PROP,
-                               BLKIO_PROP_io_wait_time),
-               .read_map = blkiocg_file_read_map,
-       },
-       {
-               .name = "io_merged",
-               .private = BLKIOFILE_PRIVATE(BLKIO_POLICY_PROP,
-                               BLKIO_PROP_io_merged),
-               .read_map = blkiocg_file_read_map,
-       },
-       {
-               .name = "io_queued",
-               .private = BLKIOFILE_PRIVATE(BLKIO_POLICY_PROP,
-                               BLKIO_PROP_io_queued),
-               .read_map = blkiocg_file_read_map,
-       },
+struct cftype blkcg_files[] = {
        {
                .name = "reset_stats",
-               .write_u64 = blkiocg_reset_stats,
-       },
-#ifdef CONFIG_BLK_DEV_THROTTLING
-       {
-               .name = "throttle.read_bps_device",
-               .private = BLKIOFILE_PRIVATE(BLKIO_POLICY_THROTL,
-                               BLKIO_THROTL_read_bps_device),
-               .read_seq_string = blkiocg_file_read,
-               .write_string = blkiocg_file_write,
-               .max_write_len = 256,
-       },
-
-       {
-               .name = "throttle.write_bps_device",
-               .private = BLKIOFILE_PRIVATE(BLKIO_POLICY_THROTL,
-                               BLKIO_THROTL_write_bps_device),
-               .read_seq_string = blkiocg_file_read,
-               .write_string = blkiocg_file_write,
-               .max_write_len = 256,
-       },
-
-       {
-               .name = "throttle.read_iops_device",
-               .private = BLKIOFILE_PRIVATE(BLKIO_POLICY_THROTL,
-                               BLKIO_THROTL_read_iops_device),
-               .read_seq_string = blkiocg_file_read,
-               .write_string = blkiocg_file_write,
-               .max_write_len = 256,
-       },
-
-       {
-               .name = "throttle.write_iops_device",
-               .private = BLKIOFILE_PRIVATE(BLKIO_POLICY_THROTL,
-                               BLKIO_THROTL_write_iops_device),
-               .read_seq_string = blkiocg_file_read,
-               .write_string = blkiocg_file_write,
-               .max_write_len = 256,
-       },
-       {
-               .name = "throttle.io_service_bytes",
-               .private = BLKIOFILE_PRIVATE(BLKIO_POLICY_THROTL,
-                               BLKIO_THROTL_io_service_bytes),
-               .read_map = blkiocg_file_read_map,
-       },
-       {
-               .name = "throttle.io_serviced",
-               .private = BLKIOFILE_PRIVATE(BLKIO_POLICY_THROTL,
-                               BLKIO_THROTL_io_serviced),
-               .read_map = blkiocg_file_read_map,
-       },
-#endif /* CONFIG_BLK_DEV_THROTTLING */
-
-#ifdef CONFIG_DEBUG_BLK_CGROUP
-       {
-               .name = "avg_queue_size",
-               .private = BLKIOFILE_PRIVATE(BLKIO_POLICY_PROP,
-                               BLKIO_PROP_avg_queue_size),
-               .read_map = blkiocg_file_read_map,
+               .write_u64 = blkcg_reset_stats,
        },
-       {
-               .name = "group_wait_time",
-               .private = BLKIOFILE_PRIVATE(BLKIO_POLICY_PROP,
-                               BLKIO_PROP_group_wait_time),
-               .read_map = blkiocg_file_read_map,
-       },
-       {
-               .name = "idle_time",
-               .private = BLKIOFILE_PRIVATE(BLKIO_POLICY_PROP,
-                               BLKIO_PROP_idle_time),
-               .read_map = blkiocg_file_read_map,
-       },
-       {
-               .name = "empty_time",
-               .private = BLKIOFILE_PRIVATE(BLKIO_POLICY_PROP,
-                               BLKIO_PROP_empty_time),
-               .read_map = blkiocg_file_read_map,
-       },
-       {
-               .name = "dequeue",
-               .private = BLKIOFILE_PRIVATE(BLKIO_POLICY_PROP,
-                               BLKIO_PROP_dequeue),
-               .read_map = blkiocg_file_read_map,
-       },
-       {
-               .name = "unaccounted_time",
-               .private = BLKIOFILE_PRIVATE(BLKIO_POLICY_PROP,
-                               BLKIO_PROP_unaccounted_time),
-               .read_map = blkiocg_file_read_map,
-       },
-#endif
        { }     /* terminate */
 };
 
-static void blkiocg_destroy(struct cgroup *cgroup)
+/**
+ * blkcg_pre_destroy - cgroup pre_destroy callback
+ * @cgroup: cgroup of interest
+ *
+ * This function is called when @cgroup is about to go away and responsible
+ * for shooting down all blkgs associated with @cgroup.  blkgs should be
+ * removed while holding both q and blkcg locks.  As blkcg lock is nested
+ * inside q lock, this function performs reverse double lock dancing.
+ *
+ * This is the blkcg counterpart of ioc_release_fn().
+ */
+static int blkcg_pre_destroy(struct cgroup *cgroup)
 {
-       struct blkio_cgroup *blkcg = cgroup_to_blkio_cgroup(cgroup);
-       unsigned long flags;
-       struct blkio_group *blkg;
-       void *key;
-       struct blkio_policy_type *blkiop;
-       struct blkio_policy_node *pn, *pntmp;
+       struct blkcg *blkcg = cgroup_to_blkcg(cgroup);
 
-       rcu_read_lock();
-       do {
-               spin_lock_irqsave(&blkcg->lock, flags);
+       spin_lock_irq(&blkcg->lock);
 
-               if (hlist_empty(&blkcg->blkg_list)) {
-                       spin_unlock_irqrestore(&blkcg->lock, flags);
-                       break;
+       while (!hlist_empty(&blkcg->blkg_list)) {
+               struct blkcg_gq *blkg = hlist_entry(blkcg->blkg_list.first,
+                                               struct blkcg_gq, blkcg_node);
+               struct request_queue *q = blkg->q;
+
+               if (spin_trylock(q->queue_lock)) {
+                       blkg_destroy(blkg);
+                       spin_unlock(q->queue_lock);
+               } else {
+                       spin_unlock_irq(&blkcg->lock);
+                       cpu_relax();
+                       spin_lock_irq(&blkcg->lock);
                }
+       }
 
-               blkg = hlist_entry(blkcg->blkg_list.first, struct blkio_group,
-                                       blkcg_node);
-               key = rcu_dereference(blkg->key);
-               __blkiocg_del_blkio_group(blkg);
-
-               spin_unlock_irqrestore(&blkcg->lock, flags);
-
-               /*
-                * This blkio_group is being unlinked as associated cgroup is
-                * going away. Let all the IO controlling policies know about
-                * this event.
-                */
-               spin_lock(&blkio_list_lock);
-               list_for_each_entry(blkiop, &blkio_list, list) {
-                       if (blkiop->plid != blkg->plid)
-                               continue;
-                       blkiop->ops.blkio_unlink_group_fn(key, blkg);
-               }
-               spin_unlock(&blkio_list_lock);
-       } while (1);
+       spin_unlock_irq(&blkcg->lock);
+       return 0;
+}
 
-       list_for_each_entry_safe(pn, pntmp, &blkcg->policy_list, node) {
-               blkio_policy_delete_node(pn);
-               kfree(pn);
-       }
+static void blkcg_destroy(struct cgroup *cgroup)
+{
+       struct blkcg *blkcg = cgroup_to_blkcg(cgroup);
 
-       free_css_id(&blkio_subsys, &blkcg->css);
-       rcu_read_unlock();
-       if (blkcg != &blkio_root_cgroup)
+       if (blkcg != &blkcg_root)
                kfree(blkcg);
 }
 
-static struct cgroup_subsys_state *blkiocg_create(struct cgroup *cgroup)
+static struct cgroup_subsys_state *blkcg_create(struct cgroup *cgroup)
 {
-       struct blkio_cgroup *blkcg;
+       static atomic64_t id_seq = ATOMIC64_INIT(0);
+       struct blkcg *blkcg;
        struct cgroup *parent = cgroup->parent;
 
        if (!parent) {
-               blkcg = &blkio_root_cgroup;
+               blkcg = &blkcg_root;
                goto done;
        }
 
@@ -1582,22 +624,68 @@ static struct cgroup_subsys_state *blkiocg_create(struct cgroup *cgroup)
        if (!blkcg)
                return ERR_PTR(-ENOMEM);
 
-       blkcg->weight = BLKIO_WEIGHT_DEFAULT;
+       blkcg->cfq_weight = CFQ_WEIGHT_DEFAULT;
+       blkcg->id = atomic64_inc_return(&id_seq); /* root is 0, start from 1 */
 done:
        spin_lock_init(&blkcg->lock);
+       INIT_RADIX_TREE(&blkcg->blkg_tree, GFP_ATOMIC);
        INIT_HLIST_HEAD(&blkcg->blkg_list);
 
-       INIT_LIST_HEAD(&blkcg->policy_list);
        return &blkcg->css;
 }
 
+/**
+ * blkcg_init_queue - initialize blkcg part of request queue
+ * @q: request_queue to initialize
+ *
+ * Called from blk_alloc_queue_node(). Responsible for initializing blkcg
+ * part of new request_queue @q.
+ *
+ * RETURNS:
+ * 0 on success, -errno on failure.
+ */
+int blkcg_init_queue(struct request_queue *q)
+{
+       might_sleep();
+
+       return blk_throtl_init(q);
+}
+
+/**
+ * blkcg_drain_queue - drain blkcg part of request_queue
+ * @q: request_queue to drain
+ *
+ * Called from blk_drain_queue().  Responsible for draining blkcg part.
+ */
+void blkcg_drain_queue(struct request_queue *q)
+{
+       lockdep_assert_held(q->queue_lock);
+
+       blk_throtl_drain(q);
+}
+
+/**
+ * blkcg_exit_queue - exit and release blkcg part of request_queue
+ * @q: request_queue being released
+ *
+ * Called from blk_release_queue().  Responsible for exiting blkcg part.
+ */
+void blkcg_exit_queue(struct request_queue *q)
+{
+       spin_lock_irq(q->queue_lock);
+       blkg_destroy_all(q);
+       spin_unlock_irq(q->queue_lock);
+
+       blk_throtl_exit(q);
+}
+
 /*
  * We cannot support shared io contexts, as we have no mean to support
  * two tasks with the same ioc in two different groups without major rework
  * of the main cic data structures.  For now we allow a task to change
  * its cgroup only if it's the only owner of its ioc.
  */
-static int blkiocg_can_attach(struct cgroup *cgrp, struct cgroup_taskset *tset)
+static int blkcg_can_attach(struct cgroup *cgrp, struct cgroup_taskset *tset)
 {
        struct task_struct *task;
        struct io_context *ioc;
@@ -1616,63 +704,213 @@ static int blkiocg_can_attach(struct cgroup *cgrp, struct cgroup_taskset *tset)
        return ret;
 }
 
-static void blkiocg_attach(struct cgroup *cgrp, struct cgroup_taskset *tset)
-{
-       struct task_struct *task;
-       struct io_context *ioc;
-
-       cgroup_taskset_for_each(task, cgrp, tset) {
-               /* we don't lose anything even if ioc allocation fails */
-               ioc = get_task_io_context(task, GFP_ATOMIC, NUMA_NO_NODE);
-               if (ioc) {
-                       ioc_cgroup_changed(ioc);
-                       put_io_context(ioc);
-               }
-       }
-}
-
 struct cgroup_subsys blkio_subsys = {
        .name = "blkio",
-       .create = blkiocg_create,
-       .can_attach = blkiocg_can_attach,
-       .attach = blkiocg_attach,
-       .destroy = blkiocg_destroy,
-#ifdef CONFIG_BLK_CGROUP
-       /* note: blkio_subsys_id is otherwise defined in blk-cgroup.h */
+       .create = blkcg_create,
+       .can_attach = blkcg_can_attach,
+       .pre_destroy = blkcg_pre_destroy,
+       .destroy = blkcg_destroy,
        .subsys_id = blkio_subsys_id,
-#endif
-       .base_cftypes = blkio_files,
-       .use_id = 1,
+       .base_cftypes = blkcg_files,
        .module = THIS_MODULE,
 };
 EXPORT_SYMBOL_GPL(blkio_subsys);
 
-void blkio_policy_register(struct blkio_policy_type *blkiop)
+/**
+ * blkcg_activate_policy - activate a blkcg policy on a request_queue
+ * @q: request_queue of interest
+ * @pol: blkcg policy to activate
+ *
+ * Activate @pol on @q.  Requires %GFP_KERNEL context.  @q goes through
+ * bypass mode to populate its blkgs with policy_data for @pol.
+ *
+ * Activation happens with @q bypassed, so nobody would be accessing blkgs
+ * from IO path.  Update of each blkg is protected by both queue and blkcg
+ * locks so that holding either lock and testing blkcg_policy_enabled() is
+ * always enough for dereferencing policy data.
+ *
+ * The caller is responsible for synchronizing [de]activations and policy
+ * [un]registerations.  Returns 0 on success, -errno on failure.
+ */
+int blkcg_activate_policy(struct request_queue *q,
+                         const struct blkcg_policy *pol)
 {
-       spin_lock(&blkio_list_lock);
-       list_add_tail(&blkiop->list, &blkio_list);
-       spin_unlock(&blkio_list_lock);
+       LIST_HEAD(pds);
+       struct blkcg_gq *blkg;
+       struct blkg_policy_data *pd, *n;
+       int cnt = 0, ret;
+
+       if (blkcg_policy_enabled(q, pol))
+               return 0;
+
+       blk_queue_bypass_start(q);
+
+       /* make sure the root blkg exists and count the existing blkgs */
+       spin_lock_irq(q->queue_lock);
+
+       rcu_read_lock();
+       blkg = __blkg_lookup_create(&blkcg_root, q);
+       rcu_read_unlock();
+
+       if (IS_ERR(blkg)) {
+               ret = PTR_ERR(blkg);
+               goto out_unlock;
+       }
+       q->root_blkg = blkg;
+
+       list_for_each_entry(blkg, &q->blkg_list, q_node)
+               cnt++;
+
+       spin_unlock_irq(q->queue_lock);
+
+       /* allocate policy_data for all existing blkgs */
+       while (cnt--) {
+               pd = kzalloc_node(pol->pd_size, GFP_KERNEL, q->node);
+               if (!pd) {
+                       ret = -ENOMEM;
+                       goto out_free;
+               }
+               list_add_tail(&pd->alloc_node, &pds);
+       }
+
+       /*
+        * Install the allocated pds.  With @q bypassing, no new blkg
+        * should have been created while the queue lock was dropped.
+        */
+       spin_lock_irq(q->queue_lock);
+
+       list_for_each_entry(blkg, &q->blkg_list, q_node) {
+               if (WARN_ON(list_empty(&pds))) {
+                       /* umm... this shouldn't happen, just abort */
+                       ret = -ENOMEM;
+                       goto out_unlock;
+               }
+               pd = list_first_entry(&pds, struct blkg_policy_data, alloc_node);
+               list_del_init(&pd->alloc_node);
+
+               /* grab blkcg lock too while installing @pd on @blkg */
+               spin_lock(&blkg->blkcg->lock);
+
+               blkg->pd[pol->plid] = pd;
+               pd->blkg = blkg;
+               pol->pd_init_fn(blkg);
+
+               spin_unlock(&blkg->blkcg->lock);
+       }
+
+       __set_bit(pol->plid, q->blkcg_pols);
+       ret = 0;
+out_unlock:
+       spin_unlock_irq(q->queue_lock);
+out_free:
+       blk_queue_bypass_end(q);
+       list_for_each_entry_safe(pd, n, &pds, alloc_node)
+               kfree(pd);
+       return ret;
 }
-EXPORT_SYMBOL_GPL(blkio_policy_register);
+EXPORT_SYMBOL_GPL(blkcg_activate_policy);
 
-void blkio_policy_unregister(struct blkio_policy_type *blkiop)
+/**
+ * blkcg_deactivate_policy - deactivate a blkcg policy on a request_queue
+ * @q: request_queue of interest
+ * @pol: blkcg policy to deactivate
+ *
+ * Deactivate @pol on @q.  Follows the same synchronization rules as
+ * blkcg_activate_policy().
+ */
+void blkcg_deactivate_policy(struct request_queue *q,
+                            const struct blkcg_policy *pol)
 {
-       spin_lock(&blkio_list_lock);
-       list_del_init(&blkiop->list);
-       spin_unlock(&blkio_list_lock);
+       struct blkcg_gq *blkg;
+
+       if (!blkcg_policy_enabled(q, pol))
+               return;
+
+       blk_queue_bypass_start(q);
+       spin_lock_irq(q->queue_lock);
+
+       __clear_bit(pol->plid, q->blkcg_pols);
+
+       /* if no policy is left, no need for blkgs - shoot them down */
+       if (bitmap_empty(q->blkcg_pols, BLKCG_MAX_POLS))
+               blkg_destroy_all(q);
+
+       list_for_each_entry(blkg, &q->blkg_list, q_node) {
+               /* grab blkcg lock too while removing @pd from @blkg */
+               spin_lock(&blkg->blkcg->lock);
+
+               if (pol->pd_exit_fn)
+                       pol->pd_exit_fn(blkg);
+
+               kfree(blkg->pd[pol->plid]);
+               blkg->pd[pol->plid] = NULL;
+
+               spin_unlock(&blkg->blkcg->lock);
+       }
+
+       spin_unlock_irq(q->queue_lock);
+       blk_queue_bypass_end(q);
 }
-EXPORT_SYMBOL_GPL(blkio_policy_unregister);
+EXPORT_SYMBOL_GPL(blkcg_deactivate_policy);
 
-static int __init init_cgroup_blkio(void)
+/**
+ * blkcg_policy_register - register a blkcg policy
+ * @pol: blkcg policy to register
+ *
+ * Register @pol with blkcg core.  Might sleep and @pol may be modified on
+ * successful registration.  Returns 0 on success and -errno on failure.
+ */
+int blkcg_policy_register(struct blkcg_policy *pol)
 {
-       return cgroup_load_subsys(&blkio_subsys);
+       int i, ret;
+
+       if (WARN_ON(pol->pd_size < sizeof(struct blkg_policy_data)))
+               return -EINVAL;
+
+       mutex_lock(&blkcg_pol_mutex);
+
+       /* find an empty slot */
+       ret = -ENOSPC;
+       for (i = 0; i < BLKCG_MAX_POLS; i++)
+               if (!blkcg_policy[i])
+                       break;
+       if (i >= BLKCG_MAX_POLS)
+               goto out_unlock;
+
+       /* register and update blkgs */
+       pol->plid = i;
+       blkcg_policy[i] = pol;
+
+       /* everything is in place, add intf files for the new policy */
+       if (pol->cftypes)
+               WARN_ON(cgroup_add_cftypes(&blkio_subsys, pol->cftypes));
+       ret = 0;
+out_unlock:
+       mutex_unlock(&blkcg_pol_mutex);
+       return ret;
 }
+EXPORT_SYMBOL_GPL(blkcg_policy_register);
 
-static void __exit exit_cgroup_blkio(void)
+/**
+ * blkcg_policy_unregister - unregister a blkcg policy
+ * @pol: blkcg policy to unregister
+ *
+ * Undo blkcg_policy_register(@pol).  Might sleep.
+ */
+void blkcg_policy_unregister(struct blkcg_policy *pol)
 {
-       cgroup_unload_subsys(&blkio_subsys);
-}
+       mutex_lock(&blkcg_pol_mutex);
 
-module_init(init_cgroup_blkio);
-module_exit(exit_cgroup_blkio);
-MODULE_LICENSE("GPL");
+       if (WARN_ON(blkcg_policy[pol->plid] != pol))
+               goto out_unlock;
+
+       /* kill the intf files first */
+       if (pol->cftypes)
+               cgroup_rm_cftypes(&blkio_subsys, pol->cftypes);
+
+       /* unregister and update blkgs */
+       blkcg_policy[pol->plid] = NULL;
+out_unlock:
+       mutex_unlock(&blkcg_pol_mutex);
+}
+EXPORT_SYMBOL_GPL(blkcg_policy_unregister);
index 6f3ace7e792ff4336fe74ac2d796ec1fa1f5df68..8ac457ce7783847522c1340c008d3c7789f3a1b2 100644 (file)
 
 #include <linux/cgroup.h>
 #include <linux/u64_stats_sync.h>
-
-enum blkio_policy_id {
-       BLKIO_POLICY_PROP = 0,          /* Proportional Bandwidth division */
-       BLKIO_POLICY_THROTL,            /* Throttling */
-};
+#include <linux/seq_file.h>
+#include <linux/radix-tree.h>
 
 /* Max limits for throttle policy */
 #define THROTL_IOPS_MAX                UINT_MAX
 
-#if defined(CONFIG_BLK_CGROUP) || defined(CONFIG_BLK_CGROUP_MODULE)
-
-#ifndef CONFIG_BLK_CGROUP
-/* When blk-cgroup is a module, its subsys_id isn't a compile-time constant */
-extern struct cgroup_subsys blkio_subsys;
-#define blkio_subsys_id blkio_subsys.subsys_id
-#endif
-
-enum stat_type {
-       /* Total time spent (in ns) between request dispatch to the driver and
-        * request completion for IOs doen by this cgroup. This may not be
-        * accurate when NCQ is turned on. */
-       BLKIO_STAT_SERVICE_TIME = 0,
-       /* Total time spent waiting in scheduler queue in ns */
-       BLKIO_STAT_WAIT_TIME,
-       /* Number of IOs queued up */
-       BLKIO_STAT_QUEUED,
-       /* All the single valued stats go below this */
-       BLKIO_STAT_TIME,
-#ifdef CONFIG_DEBUG_BLK_CGROUP
-       /* Time not charged to this cgroup */
-       BLKIO_STAT_UNACCOUNTED_TIME,
-       BLKIO_STAT_AVG_QUEUE_SIZE,
-       BLKIO_STAT_IDLE_TIME,
-       BLKIO_STAT_EMPTY_TIME,
-       BLKIO_STAT_GROUP_WAIT_TIME,
-       BLKIO_STAT_DEQUEUE
-#endif
-};
+/* CFQ specific, out here for blkcg->cfq_weight */
+#define CFQ_WEIGHT_MIN         10
+#define CFQ_WEIGHT_MAX         1000
+#define CFQ_WEIGHT_DEFAULT     500
 
-/* Per cpu stats */
-enum stat_type_cpu {
-       BLKIO_STAT_CPU_SECTORS,
-       /* Total bytes transferred */
-       BLKIO_STAT_CPU_SERVICE_BYTES,
-       /* Total IOs serviced, post merge */
-       BLKIO_STAT_CPU_SERVICED,
-       /* Number of IOs merged */
-       BLKIO_STAT_CPU_MERGED,
-       BLKIO_STAT_CPU_NR
-};
+#ifdef CONFIG_BLK_CGROUP
 
-enum stat_sub_type {
-       BLKIO_STAT_READ = 0,
-       BLKIO_STAT_WRITE,
-       BLKIO_STAT_SYNC,
-       BLKIO_STAT_ASYNC,
-       BLKIO_STAT_TOTAL
-};
+enum blkg_rwstat_type {
+       BLKG_RWSTAT_READ,
+       BLKG_RWSTAT_WRITE,
+       BLKG_RWSTAT_SYNC,
+       BLKG_RWSTAT_ASYNC,
 
-/* blkg state flags */
-enum blkg_state_flags {
-       BLKG_waiting = 0,
-       BLKG_idling,
-       BLKG_empty,
+       BLKG_RWSTAT_NR,
+       BLKG_RWSTAT_TOTAL = BLKG_RWSTAT_NR,
 };
 
-/* cgroup files owned by proportional weight policy */
-enum blkcg_file_name_prop {
-       BLKIO_PROP_weight = 1,
-       BLKIO_PROP_weight_device,
-       BLKIO_PROP_io_service_bytes,
-       BLKIO_PROP_io_serviced,
-       BLKIO_PROP_time,
-       BLKIO_PROP_sectors,
-       BLKIO_PROP_unaccounted_time,
-       BLKIO_PROP_io_service_time,
-       BLKIO_PROP_io_wait_time,
-       BLKIO_PROP_io_merged,
-       BLKIO_PROP_io_queued,
-       BLKIO_PROP_avg_queue_size,
-       BLKIO_PROP_group_wait_time,
-       BLKIO_PROP_idle_time,
-       BLKIO_PROP_empty_time,
-       BLKIO_PROP_dequeue,
-};
+struct blkcg_gq;
 
-/* cgroup files owned by throttle policy */
-enum blkcg_file_name_throtl {
-       BLKIO_THROTL_read_bps_device,
-       BLKIO_THROTL_write_bps_device,
-       BLKIO_THROTL_read_iops_device,
-       BLKIO_THROTL_write_iops_device,
-       BLKIO_THROTL_io_service_bytes,
-       BLKIO_THROTL_io_serviced,
-};
+struct blkcg {
+       struct cgroup_subsys_state      css;
+       spinlock_t                      lock;
 
-struct blkio_cgroup {
-       struct cgroup_subsys_state css;
-       unsigned int weight;
-       spinlock_t lock;
-       struct hlist_head blkg_list;
-       struct list_head policy_list; /* list of blkio_policy_node */
-};
+       struct radix_tree_root          blkg_tree;
+       struct blkcg_gq                 *blkg_hint;
+       struct hlist_head               blkg_list;
+
+       /* for policies to test whether associated blkcg has changed */
+       uint64_t                        id;
 
-struct blkio_group_stats {
-       /* total disk time and nr sectors dispatched by this group */
-       uint64_t time;
-       uint64_t stat_arr[BLKIO_STAT_QUEUED + 1][BLKIO_STAT_TOTAL];
-#ifdef CONFIG_DEBUG_BLK_CGROUP
-       /* Time not charged to this cgroup */
-       uint64_t unaccounted_time;
-
-       /* Sum of number of IOs queued across all samples */
-       uint64_t avg_queue_size_sum;
-       /* Count of samples taken for average */
-       uint64_t avg_queue_size_samples;
-       /* How many times this group has been removed from service tree */
-       unsigned long dequeue;
-
-       /* Total time spent waiting for it to be assigned a timeslice. */
-       uint64_t group_wait_time;
-       uint64_t start_group_wait_time;
-
-       /* Time spent idling for this blkio_group */
-       uint64_t idle_time;
-       uint64_t start_idle_time;
-       /*
-        * Total time when we have requests queued and do not contain the
-        * current active queue.
-        */
-       uint64_t empty_time;
-       uint64_t start_empty_time;
-       uint16_t flags;
-#endif
+       /* TODO: per-policy storage in blkcg */
+       unsigned int                    cfq_weight;     /* belongs to cfq */
 };
 
-/* Per cpu blkio group stats */
-struct blkio_group_stats_cpu {
-       uint64_t sectors;
-       uint64_t stat_arr_cpu[BLKIO_STAT_CPU_NR][BLKIO_STAT_TOTAL];
-       struct u64_stats_sync syncp;
+struct blkg_stat {
+       struct u64_stats_sync           syncp;
+       uint64_t                        cnt;
 };
 
-struct blkio_group {
-       /* An rcu protected unique identifier for the group */
-       void *key;
-       struct hlist_node blkcg_node;
-       unsigned short blkcg_id;
-       /* Store cgroup path */
-       char path[128];
-       /* The device MKDEV(major, minor), this group has been created for */
-       dev_t dev;
-       /* policy which owns this blk group */
-       enum blkio_policy_id plid;
-
-       /* Need to serialize the stats in the case of reset/update */
-       spinlock_t stats_lock;
-       struct blkio_group_stats stats;
-       /* Per cpu stats pointer */
-       struct blkio_group_stats_cpu __percpu *stats_cpu;
+struct blkg_rwstat {
+       struct u64_stats_sync           syncp;
+       uint64_t                        cnt[BLKG_RWSTAT_NR];
 };
 
-struct blkio_policy_node {
-       struct list_head node;
-       dev_t dev;
-       /* This node belongs to max bw policy or porportional weight policy */
-       enum blkio_policy_id plid;
-       /* cgroup file to which this rule belongs to */
-       int fileid;
-
-       union {
-               unsigned int weight;
-               /*
-                * Rate read/write in terms of bytes per second
-                * Whether this rate represents read or write is determined
-                * by file type "fileid".
-                */
-               u64 bps;
-               unsigned int iops;
-       } val;
+/*
+ * A blkcg_gq (blkg) is association between a block cgroup (blkcg) and a
+ * request_queue (q).  This is used by blkcg policies which need to track
+ * information per blkcg - q pair.
+ *
+ * There can be multiple active blkcg policies and each has its private
+ * data on each blkg, the size of which is determined by
+ * blkcg_policy->pd_size.  blkcg core allocates and frees such areas
+ * together with blkg and invokes pd_init/exit_fn() methods.
+ *
+ * Such private data must embed struct blkg_policy_data (pd) at the
+ * beginning and pd_size can't be smaller than pd.
+ */
+struct blkg_policy_data {
+       /* the blkg this per-policy data belongs to */
+       struct blkcg_gq                 *blkg;
+
+       /* used during policy activation */
+       struct list_head                alloc_node;
 };
 
-extern unsigned int blkcg_get_weight(struct blkio_cgroup *blkcg,
-                                    dev_t dev);
-extern uint64_t blkcg_get_read_bps(struct blkio_cgroup *blkcg,
-                                    dev_t dev);
-extern uint64_t blkcg_get_write_bps(struct blkio_cgroup *blkcg,
-                                    dev_t dev);
-extern unsigned int blkcg_get_read_iops(struct blkio_cgroup *blkcg,
-                                    dev_t dev);
-extern unsigned int blkcg_get_write_iops(struct blkio_cgroup *blkcg,
-                                    dev_t dev);
-
-typedef void (blkio_unlink_group_fn) (void *key, struct blkio_group *blkg);
-
-typedef void (blkio_update_group_weight_fn) (void *key,
-                       struct blkio_group *blkg, unsigned int weight);
-typedef void (blkio_update_group_read_bps_fn) (void * key,
-                       struct blkio_group *blkg, u64 read_bps);
-typedef void (blkio_update_group_write_bps_fn) (void *key,
-                       struct blkio_group *blkg, u64 write_bps);
-typedef void (blkio_update_group_read_iops_fn) (void *key,
-                       struct blkio_group *blkg, unsigned int read_iops);
-typedef void (blkio_update_group_write_iops_fn) (void *key,
-                       struct blkio_group *blkg, unsigned int write_iops);
-
-struct blkio_policy_ops {
-       blkio_unlink_group_fn *blkio_unlink_group_fn;
-       blkio_update_group_weight_fn *blkio_update_group_weight_fn;
-       blkio_update_group_read_bps_fn *blkio_update_group_read_bps_fn;
-       blkio_update_group_write_bps_fn *blkio_update_group_write_bps_fn;
-       blkio_update_group_read_iops_fn *blkio_update_group_read_iops_fn;
-       blkio_update_group_write_iops_fn *blkio_update_group_write_iops_fn;
+/* association between a blk cgroup and a request queue */
+struct blkcg_gq {
+       /* Pointer to the associated request_queue */
+       struct request_queue            *q;
+       struct list_head                q_node;
+       struct hlist_node               blkcg_node;
+       struct blkcg                    *blkcg;
+       /* reference count */
+       int                             refcnt;
+
+       struct blkg_policy_data         *pd[BLKCG_MAX_POLS];
+
+       struct rcu_head                 rcu_head;
 };
 
-struct blkio_policy_type {
-       struct list_head list;
-       struct blkio_policy_ops ops;
-       enum blkio_policy_id plid;
+typedef void (blkcg_pol_init_pd_fn)(struct blkcg_gq *blkg);
+typedef void (blkcg_pol_exit_pd_fn)(struct blkcg_gq *blkg);
+typedef void (blkcg_pol_reset_pd_stats_fn)(struct blkcg_gq *blkg);
+
+struct blkcg_policy {
+       int                             plid;
+       /* policy specific private data size */
+       size_t                          pd_size;
+       /* cgroup files for the policy */
+       struct cftype                   *cftypes;
+
+       /* operations */
+       blkcg_pol_init_pd_fn            *pd_init_fn;
+       blkcg_pol_exit_pd_fn            *pd_exit_fn;
+       blkcg_pol_reset_pd_stats_fn     *pd_reset_stats_fn;
 };
 
+extern struct blkcg blkcg_root;
+
+struct blkcg *cgroup_to_blkcg(struct cgroup *cgroup);
+struct blkcg *bio_blkcg(struct bio *bio);
+struct blkcg_gq *blkg_lookup(struct blkcg *blkcg, struct request_queue *q);
+struct blkcg_gq *blkg_lookup_create(struct blkcg *blkcg,
+                                   struct request_queue *q);
+int blkcg_init_queue(struct request_queue *q);
+void blkcg_drain_queue(struct request_queue *q);
+void blkcg_exit_queue(struct request_queue *q);
+
 /* Blkio controller policy registration */
-extern void blkio_policy_register(struct blkio_policy_type *);
-extern void blkio_policy_unregister(struct blkio_policy_type *);
+int blkcg_policy_register(struct blkcg_policy *pol);
+void blkcg_policy_unregister(struct blkcg_policy *pol);
+int blkcg_activate_policy(struct request_queue *q,
+                         const struct blkcg_policy *pol);
+void blkcg_deactivate_policy(struct request_queue *q,
+                            const struct blkcg_policy *pol);
+
+void blkcg_print_blkgs(struct seq_file *sf, struct blkcg *blkcg,
+                      u64 (*prfill)(struct seq_file *,
+                                    struct blkg_policy_data *, int),
+                      const struct blkcg_policy *pol, int data,
+                      bool show_total);
+u64 __blkg_prfill_u64(struct seq_file *sf, struct blkg_policy_data *pd, u64 v);
+u64 __blkg_prfill_rwstat(struct seq_file *sf, struct blkg_policy_data *pd,
+                        const struct blkg_rwstat *rwstat);
+u64 blkg_prfill_stat(struct seq_file *sf, struct blkg_policy_data *pd, int off);
+u64 blkg_prfill_rwstat(struct seq_file *sf, struct blkg_policy_data *pd,
+                      int off);
+
+struct blkg_conf_ctx {
+       struct gendisk                  *disk;
+       struct blkcg_gq                 *blkg;
+       u64                             v;
+};
+
+int blkg_conf_prep(struct blkcg *blkcg, const struct blkcg_policy *pol,
+                  const char *input, struct blkg_conf_ctx *ctx);
+void blkg_conf_finish(struct blkg_conf_ctx *ctx);
+
+
+/**
+ * blkg_to_pdata - get policy private data
+ * @blkg: blkg of interest
+ * @pol: policy of interest
+ *
+ * Return pointer to private data associated with the @blkg-@pol pair.
+ */
+static inline struct blkg_policy_data *blkg_to_pd(struct blkcg_gq *blkg,
+                                                 struct blkcg_policy *pol)
+{
+       return blkg ? blkg->pd[pol->plid] : NULL;
+}
+
+/**
+ * pdata_to_blkg - get blkg associated with policy private data
+ * @pd: policy private data of interest
+ *
+ * @pd is policy private data.  Determine the blkg it's associated with.
+ */
+static inline struct blkcg_gq *pd_to_blkg(struct blkg_policy_data *pd)
+{
+       return pd ? pd->blkg : NULL;
+}
+
+/**
+ * blkg_path - format cgroup path of blkg
+ * @blkg: blkg of interest
+ * @buf: target buffer
+ * @buflen: target buffer length
+ *
+ * Format the path of the cgroup of @blkg into @buf.
+ */
+static inline int blkg_path(struct blkcg_gq *blkg, char *buf, int buflen)
+{
+       int ret;
+
+       rcu_read_lock();
+       ret = cgroup_path(blkg->blkcg->css.cgroup, buf, buflen);
+       rcu_read_unlock();
+       if (ret)
+               strncpy(buf, "<unavailable>", buflen);
+       return ret;
+}
 
-static inline char *blkg_path(struct blkio_group *blkg)
+/**
+ * blkg_get - get a blkg reference
+ * @blkg: blkg to get
+ *
+ * The caller should be holding queue_lock and an existing reference.
+ */
+static inline void blkg_get(struct blkcg_gq *blkg)
 {
-       return blkg->path;
+       lockdep_assert_held(blkg->q->queue_lock);
+       WARN_ON_ONCE(!blkg->refcnt);
+       blkg->refcnt++;
 }
 
-#else
+void __blkg_release(struct blkcg_gq *blkg);
 
-struct blkio_group {
+/**
+ * blkg_put - put a blkg reference
+ * @blkg: blkg to put
+ *
+ * The caller should be holding queue_lock.
+ */
+static inline void blkg_put(struct blkcg_gq *blkg)
+{
+       lockdep_assert_held(blkg->q->queue_lock);
+       WARN_ON_ONCE(blkg->refcnt <= 0);
+       if (!--blkg->refcnt)
+               __blkg_release(blkg);
+}
+
+/**
+ * blkg_stat_add - add a value to a blkg_stat
+ * @stat: target blkg_stat
+ * @val: value to add
+ *
+ * Add @val to @stat.  The caller is responsible for synchronizing calls to
+ * this function.
+ */
+static inline void blkg_stat_add(struct blkg_stat *stat, uint64_t val)
+{
+       u64_stats_update_begin(&stat->syncp);
+       stat->cnt += val;
+       u64_stats_update_end(&stat->syncp);
+}
+
+/**
+ * blkg_stat_read - read the current value of a blkg_stat
+ * @stat: blkg_stat to read
+ *
+ * Read the current value of @stat.  This function can be called without
+ * synchroniztion and takes care of u64 atomicity.
+ */
+static inline uint64_t blkg_stat_read(struct blkg_stat *stat)
+{
+       unsigned int start;
+       uint64_t v;
+
+       do {
+               start = u64_stats_fetch_begin(&stat->syncp);
+               v = stat->cnt;
+       } while (u64_stats_fetch_retry(&stat->syncp, start));
+
+       return v;
+}
+
+/**
+ * blkg_stat_reset - reset a blkg_stat
+ * @stat: blkg_stat to reset
+ */
+static inline void blkg_stat_reset(struct blkg_stat *stat)
+{
+       stat->cnt = 0;
+}
+
+/**
+ * blkg_rwstat_add - add a value to a blkg_rwstat
+ * @rwstat: target blkg_rwstat
+ * @rw: mask of REQ_{WRITE|SYNC}
+ * @val: value to add
+ *
+ * Add @val to @rwstat.  The counters are chosen according to @rw.  The
+ * caller is responsible for synchronizing calls to this function.
+ */
+static inline void blkg_rwstat_add(struct blkg_rwstat *rwstat,
+                                  int rw, uint64_t val)
+{
+       u64_stats_update_begin(&rwstat->syncp);
+
+       if (rw & REQ_WRITE)
+               rwstat->cnt[BLKG_RWSTAT_WRITE] += val;
+       else
+               rwstat->cnt[BLKG_RWSTAT_READ] += val;
+       if (rw & REQ_SYNC)
+               rwstat->cnt[BLKG_RWSTAT_SYNC] += val;
+       else
+               rwstat->cnt[BLKG_RWSTAT_ASYNC] += val;
+
+       u64_stats_update_end(&rwstat->syncp);
+}
+
+/**
+ * blkg_rwstat_read - read the current values of a blkg_rwstat
+ * @rwstat: blkg_rwstat to read
+ *
+ * Read the current snapshot of @rwstat and return it as the return value.
+ * This function can be called without synchronization and takes care of
+ * u64 atomicity.
+ */
+static inline struct blkg_rwstat blkg_rwstat_read(struct blkg_rwstat *rwstat)
+{
+       unsigned int start;
+       struct blkg_rwstat tmp;
+
+       do {
+               start = u64_stats_fetch_begin(&rwstat->syncp);
+               tmp = *rwstat;
+       } while (u64_stats_fetch_retry(&rwstat->syncp, start));
+
+       return tmp;
+}
+
+/**
+ * blkg_rwstat_sum - read the total count of a blkg_rwstat
+ * @rwstat: blkg_rwstat to read
+ *
+ * Return the total count of @rwstat regardless of the IO direction.  This
+ * function can be called without synchronization and takes care of u64
+ * atomicity.
+ */
+static inline uint64_t blkg_rwstat_sum(struct blkg_rwstat *rwstat)
+{
+       struct blkg_rwstat tmp = blkg_rwstat_read(rwstat);
+
+       return tmp.cnt[BLKG_RWSTAT_READ] + tmp.cnt[BLKG_RWSTAT_WRITE];
+}
+
+/**
+ * blkg_rwstat_reset - reset a blkg_rwstat
+ * @rwstat: blkg_rwstat to reset
+ */
+static inline void blkg_rwstat_reset(struct blkg_rwstat *rwstat)
+{
+       memset(rwstat->cnt, 0, sizeof(rwstat->cnt));
+}
+
+#else  /* CONFIG_BLK_CGROUP */
+
+struct cgroup;
+
+struct blkg_policy_data {
 };
 
-struct blkio_policy_type {
+struct blkcg_gq {
 };
 
-static inline void blkio_policy_register(struct blkio_policy_type *blkiop) { }
-static inline void blkio_policy_unregister(struct blkio_policy_type *blkiop) { }
-
-static inline char *blkg_path(struct blkio_group *blkg) { return NULL; }
-
-#endif
-
-#define BLKIO_WEIGHT_MIN       10
-#define BLKIO_WEIGHT_MAX       1000
-#define BLKIO_WEIGHT_DEFAULT   500
-
-#ifdef CONFIG_DEBUG_BLK_CGROUP
-void blkiocg_update_avg_queue_size_stats(struct blkio_group *blkg);
-void blkiocg_update_dequeue_stats(struct blkio_group *blkg,
-                               unsigned long dequeue);
-void blkiocg_update_set_idle_time_stats(struct blkio_group *blkg);
-void blkiocg_update_idle_time_stats(struct blkio_group *blkg);
-void blkiocg_set_start_empty_time(struct blkio_group *blkg);
-
-#define BLKG_FLAG_FNS(name)                                            \
-static inline void blkio_mark_blkg_##name(                             \
-               struct blkio_group_stats *stats)                        \
-{                                                                      \
-       stats->flags |= (1 << BLKG_##name);                             \
-}                                                                      \
-static inline void blkio_clear_blkg_##name(                            \
-               struct blkio_group_stats *stats)                        \
-{                                                                      \
-       stats->flags &= ~(1 << BLKG_##name);                            \
-}                                                                      \
-static inline int blkio_blkg_##name(struct blkio_group_stats *stats)   \
-{                                                                      \
-       return (stats->flags & (1 << BLKG_##name)) != 0;                \
-}                                                                      \
-
-BLKG_FLAG_FNS(waiting)
-BLKG_FLAG_FNS(idling)
-BLKG_FLAG_FNS(empty)
-#undef BLKG_FLAG_FNS
-#else
-static inline void blkiocg_update_avg_queue_size_stats(
-                                               struct blkio_group *blkg) {}
-static inline void blkiocg_update_dequeue_stats(struct blkio_group *blkg,
-                                               unsigned long dequeue) {}
-static inline void blkiocg_update_set_idle_time_stats(struct blkio_group *blkg)
-{}
-static inline void blkiocg_update_idle_time_stats(struct blkio_group *blkg) {}
-static inline void blkiocg_set_start_empty_time(struct blkio_group *blkg) {}
-#endif
-
-#if defined(CONFIG_BLK_CGROUP) || defined(CONFIG_BLK_CGROUP_MODULE)
-extern struct blkio_cgroup blkio_root_cgroup;
-extern struct blkio_cgroup *cgroup_to_blkio_cgroup(struct cgroup *cgroup);
-extern struct blkio_cgroup *task_blkio_cgroup(struct task_struct *tsk);
-extern void blkiocg_add_blkio_group(struct blkio_cgroup *blkcg,
-       struct blkio_group *blkg, void *key, dev_t dev,
-       enum blkio_policy_id plid);
-extern int blkio_alloc_blkg_stats(struct blkio_group *blkg);
-extern int blkiocg_del_blkio_group(struct blkio_group *blkg);
-extern struct blkio_group *blkiocg_lookup_group(struct blkio_cgroup *blkcg,
-                                               void *key);
-void blkiocg_update_timeslice_used(struct blkio_group *blkg,
-                                       unsigned long time,
-                                       unsigned long unaccounted_time);
-void blkiocg_update_dispatch_stats(struct blkio_group *blkg, uint64_t bytes,
-                                               bool direction, bool sync);
-void blkiocg_update_completion_stats(struct blkio_group *blkg,
-       uint64_t start_time, uint64_t io_start_time, bool direction, bool sync);
-void blkiocg_update_io_merged_stats(struct blkio_group *blkg, bool direction,
-                                       bool sync);
-void blkiocg_update_io_add_stats(struct blkio_group *blkg,
-               struct blkio_group *curr_blkg, bool direction, bool sync);
-void blkiocg_update_io_remove_stats(struct blkio_group *blkg,
-                                       bool direction, bool sync);
-#else
-struct cgroup;
-static inline struct blkio_cgroup *
-cgroup_to_blkio_cgroup(struct cgroup *cgroup) { return NULL; }
-static inline struct blkio_cgroup *
-task_blkio_cgroup(struct task_struct *tsk) { return NULL; }
-
-static inline void blkiocg_add_blkio_group(struct blkio_cgroup *blkcg,
-               struct blkio_group *blkg, void *key, dev_t dev,
-               enum blkio_policy_id plid) {}
-
-static inline int blkio_alloc_blkg_stats(struct blkio_group *blkg) { return 0; }
-
-static inline int
-blkiocg_del_blkio_group(struct blkio_group *blkg) { return 0; }
-
-static inline struct blkio_group *
-blkiocg_lookup_group(struct blkio_cgroup *blkcg, void *key) { return NULL; }
-static inline void blkiocg_update_timeslice_used(struct blkio_group *blkg,
-                                               unsigned long time,
-                                               unsigned long unaccounted_time)
-{}
-static inline void blkiocg_update_dispatch_stats(struct blkio_group *blkg,
-                               uint64_t bytes, bool direction, bool sync) {}
-static inline void blkiocg_update_completion_stats(struct blkio_group *blkg,
-               uint64_t start_time, uint64_t io_start_time, bool direction,
-               bool sync) {}
-static inline void blkiocg_update_io_merged_stats(struct blkio_group *blkg,
-                                               bool direction, bool sync) {}
-static inline void blkiocg_update_io_add_stats(struct blkio_group *blkg,
-               struct blkio_group *curr_blkg, bool direction, bool sync) {}
-static inline void blkiocg_update_io_remove_stats(struct blkio_group *blkg,
-                                               bool direction, bool sync) {}
-#endif
-#endif /* _BLK_CGROUP_H */
+struct blkcg_policy {
+};
+
+static inline struct blkcg *cgroup_to_blkcg(struct cgroup *cgroup) { return NULL; }
+static inline struct blkcg *bio_blkcg(struct bio *bio) { return NULL; }
+static inline struct blkcg_gq *blkg_lookup(struct blkcg *blkcg, void *key) { return NULL; }
+static inline int blkcg_init_queue(struct request_queue *q) { return 0; }
+static inline void blkcg_drain_queue(struct request_queue *q) { }
+static inline void blkcg_exit_queue(struct request_queue *q) { }
+static inline int blkcg_policy_register(struct blkcg_policy *pol) { return 0; }
+static inline void blkcg_policy_unregister(struct blkcg_policy *pol) { }
+static inline int blkcg_activate_policy(struct request_queue *q,
+                                       const struct blkcg_policy *pol) { return 0; }
+static inline void blkcg_deactivate_policy(struct request_queue *q,
+                                          const struct blkcg_policy *pol) { }
+
+static inline struct blkg_policy_data *blkg_to_pd(struct blkcg_gq *blkg,
+                                                 struct blkcg_policy *pol) { return NULL; }
+static inline struct blkcg_gq *pd_to_blkg(struct blkg_policy_data *pd) { return NULL; }
+static inline char *blkg_path(struct blkcg_gq *blkg) { return NULL; }
+static inline void blkg_get(struct blkcg_gq *blkg) { }
+static inline void blkg_put(struct blkcg_gq *blkg) { }
+
+#endif /* CONFIG_BLK_CGROUP */
+#endif /* _BLK_CGROUP_H */
index 1f61b74867e41d3f74f61aeec539e8b00157dacf..3c923a7aeb56f1658142b091868c3c29ebffd3c5 100644 (file)
 #include <linux/fault-inject.h>
 #include <linux/list_sort.h>
 #include <linux/delay.h>
+#include <linux/ratelimit.h>
 
 #define CREATE_TRACE_POINTS
 #include <trace/events/block.h>
 
 #include "blk.h"
+#include "blk-cgroup.h"
 
 EXPORT_TRACEPOINT_SYMBOL_GPL(block_bio_remap);
 EXPORT_TRACEPOINT_SYMBOL_GPL(block_rq_remap);
@@ -280,7 +282,7 @@ EXPORT_SYMBOL(blk_stop_queue);
  *
  *     This function does not cancel any asynchronous activity arising
  *     out of elevator or throttling code. That would require elevaotor_exit()
- *     and blk_throtl_exit() to be called with queue lock initialized.
+ *     and blkcg_exit_queue() to be called with queue lock initialized.
  *
  */
 void blk_sync_queue(struct request_queue *q)
@@ -365,17 +367,23 @@ void blk_drain_queue(struct request_queue *q, bool drain_all)
 
                spin_lock_irq(q->queue_lock);
 
-               elv_drain_elevator(q);
-               if (drain_all)
-                       blk_throtl_drain(q);
+               /*
+                * The caller might be trying to drain @q before its
+                * elevator is initialized.
+                */
+               if (q->elevator)
+                       elv_drain_elevator(q);
+
+               blkcg_drain_queue(q);
 
                /*
                 * This function might be called on a queue which failed
-                * driver init after queue creation.  Some drivers
-                * (e.g. fd) get unhappy in such cases.  Kick queue iff
-                * dispatch queue has something on it.
+                * driver init after queue creation or is not yet fully
+                * active yet.  Some drivers (e.g. fd and loop) get unhappy
+                * in such cases.  Kick queue iff dispatch queue has
+                * something on it and @q has request_fn set.
                 */
-               if (!list_empty(&q->queue_head))
+               if (!list_empty(&q->queue_head) && q->request_fn)
                        __blk_run_queue(q);
 
                drain |= q->rq.elvpriv;
@@ -402,6 +410,49 @@ void blk_drain_queue(struct request_queue *q, bool drain_all)
        }
 }
 
+/**
+ * blk_queue_bypass_start - enter queue bypass mode
+ * @q: queue of interest
+ *
+ * In bypass mode, only the dispatch FIFO queue of @q is used.  This
+ * function makes @q enter bypass mode and drains all requests which were
+ * throttled or issued before.  On return, it's guaranteed that no request
+ * is being throttled or has ELVPRIV set and blk_queue_bypass() %true
+ * inside queue or RCU read lock.
+ */
+void blk_queue_bypass_start(struct request_queue *q)
+{
+       bool drain;
+
+       spin_lock_irq(q->queue_lock);
+       drain = !q->bypass_depth++;
+       queue_flag_set(QUEUE_FLAG_BYPASS, q);
+       spin_unlock_irq(q->queue_lock);
+
+       if (drain) {
+               blk_drain_queue(q, false);
+               /* ensure blk_queue_bypass() is %true inside RCU read lock */
+               synchronize_rcu();
+       }
+}
+EXPORT_SYMBOL_GPL(blk_queue_bypass_start);
+
+/**
+ * blk_queue_bypass_end - leave queue bypass mode
+ * @q: queue of interest
+ *
+ * Leave bypass mode and restore the normal queueing behavior.
+ */
+void blk_queue_bypass_end(struct request_queue *q)
+{
+       spin_lock_irq(q->queue_lock);
+       if (!--q->bypass_depth)
+               queue_flag_clear(QUEUE_FLAG_BYPASS, q);
+       WARN_ON_ONCE(q->bypass_depth < 0);
+       spin_unlock_irq(q->queue_lock);
+}
+EXPORT_SYMBOL_GPL(blk_queue_bypass_end);
+
 /**
  * blk_cleanup_queue - shutdown a request queue
  * @q: request queue to shutdown
@@ -418,6 +469,19 @@ void blk_cleanup_queue(struct request_queue *q)
        queue_flag_set_unlocked(QUEUE_FLAG_DEAD, q);
 
        spin_lock_irq(lock);
+
+       /*
+        * Dead queue is permanently in bypass mode till released.  Note
+        * that, unlike blk_queue_bypass_start(), we aren't performing
+        * synchronize_rcu() after entering bypass mode to avoid the delay
+        * as some drivers create and destroy a lot of queues while
+        * probing.  This is still safe because blk_release_queue() will be
+        * called only after the queue refcnt drops to zero and nothing,
+        * RCU or not, would be traversing the queue by then.
+        */
+       q->bypass_depth++;
+       queue_flag_set(QUEUE_FLAG_BYPASS, q);
+
        queue_flag_set(QUEUE_FLAG_NOMERGES, q);
        queue_flag_set(QUEUE_FLAG_NOXMERGES, q);
        queue_flag_set(QUEUE_FLAG_DEAD, q);
@@ -428,13 +492,8 @@ void blk_cleanup_queue(struct request_queue *q)
        spin_unlock_irq(lock);
        mutex_unlock(&q->sysfs_lock);
 
-       /*
-        * Drain all requests queued before DEAD marking.  The caller might
-        * be trying to tear down @q before its elevator is initialized, in
-        * which case we don't want to call into draining.
-        */
-       if (q->elevator)
-               blk_drain_queue(q, true);
+       /* drain all requests queued before DEAD marking */
+       blk_drain_queue(q, true);
 
        /* @q won't process any more request, flush async actions */
        del_timer_sync(&q->backing_dev_info.laptop_mode_wb_timer);
@@ -498,14 +557,15 @@ struct request_queue *blk_alloc_queue_node(gfp_t gfp_mask, int node_id)
        if (err)
                goto fail_id;
 
-       if (blk_throtl_init(q))
-               goto fail_id;
-
        setup_timer(&q->backing_dev_info.laptop_mode_wb_timer,
                    laptop_mode_timer_fn, (unsigned long) q);
        setup_timer(&q->timeout, blk_rq_timed_out_timer, (unsigned long) q);
+       INIT_LIST_HEAD(&q->queue_head);
        INIT_LIST_HEAD(&q->timeout_list);
        INIT_LIST_HEAD(&q->icq_list);
+#ifdef CONFIG_BLK_CGROUP
+       INIT_LIST_HEAD(&q->blkg_list);
+#endif
        INIT_LIST_HEAD(&q->flush_queue[0]);
        INIT_LIST_HEAD(&q->flush_queue[1]);
        INIT_LIST_HEAD(&q->flush_data_in_flight);
@@ -522,6 +582,18 @@ struct request_queue *blk_alloc_queue_node(gfp_t gfp_mask, int node_id)
         */
        q->queue_lock = &q->__queue_lock;
 
+       /*
+        * A queue starts its life with bypass turned on to avoid
+        * unnecessary bypass on/off overhead and nasty surprises during
+        * init.  The initial bypass will be finished at the end of
+        * blk_init_allocated_queue().
+        */
+       q->bypass_depth = 1;
+       __set_bit(QUEUE_FLAG_BYPASS, &q->queue_flags);
+
+       if (blkcg_init_queue(q))
+               goto fail_id;
+
        return q;
 
 fail_id:
@@ -614,15 +686,15 @@ blk_init_allocated_queue(struct request_queue *q, request_fn_proc *rfn,
 
        q->sg_reserved_size = INT_MAX;
 
-       /*
-        * all done
-        */
-       if (!elevator_init(q, NULL)) {
-               blk_queue_congestion_threshold(q);
-               return q;
-       }
+       /* init elevator */
+       if (elevator_init(q, NULL))
+               return NULL;
 
-       return NULL;
+       blk_queue_congestion_threshold(q);
+
+       /* all done, end the initial bypass */
+       blk_queue_bypass_end(q);
+       return q;
 }
 EXPORT_SYMBOL(blk_init_allocated_queue);
 
@@ -648,33 +720,6 @@ static inline void blk_free_request(struct request_queue *q, struct request *rq)
        mempool_free(rq, q->rq.rq_pool);
 }
 
-static struct request *
-blk_alloc_request(struct request_queue *q, struct io_cq *icq,
-                 unsigned int flags, gfp_t gfp_mask)
-{
-       struct request *rq = mempool_alloc(q->rq.rq_pool, gfp_mask);
-
-       if (!rq)
-               return NULL;
-
-       blk_rq_init(q, rq);
-
-       rq->cmd_flags = flags | REQ_ALLOCED;
-
-       if (flags & REQ_ELVPRIV) {
-               rq->elv.icq = icq;
-               if (unlikely(elv_set_request(q, rq, gfp_mask))) {
-                       mempool_free(rq, q->rq.rq_pool);
-                       return NULL;
-               }
-               /* @rq->elv.icq holds on to io_context until @rq is freed */
-               if (icq)
-                       get_io_context(icq->ioc);
-       }
-
-       return rq;
-}
-
 /*
  * ioc_batching returns true if the ioc is a valid batching request and
  * should be given priority access to a request.
@@ -762,6 +807,22 @@ static bool blk_rq_should_init_elevator(struct bio *bio)
        return true;
 }
 
+/**
+ * rq_ioc - determine io_context for request allocation
+ * @bio: request being allocated is for this bio (can be %NULL)
+ *
+ * Determine io_context to use for request allocation for @bio.  May return
+ * %NULL if %current->io_context doesn't exist.
+ */
+static struct io_context *rq_ioc(struct bio *bio)
+{
+#ifdef CONFIG_BLK_CGROUP
+       if (bio && bio->bi_ioc)
+               return bio->bi_ioc;
+#endif
+       return current->io_context;
+}
+
 /**
  * get_request - get a free request
  * @q: request_queue to allocate request from
@@ -779,7 +840,7 @@ static bool blk_rq_should_init_elevator(struct bio *bio)
 static struct request *get_request(struct request_queue *q, int rw_flags,
                                   struct bio *bio, gfp_t gfp_mask)
 {
-       struct request *rq = NULL;
+       struct request *rq;
        struct request_list *rl = &q->rq;
        struct elevator_type *et;
        struct io_context *ioc;
@@ -789,7 +850,7 @@ static struct request *get_request(struct request_queue *q, int rw_flags,
        int may_queue;
 retry:
        et = q->elevator->type;
-       ioc = current->io_context;
+       ioc = rq_ioc(bio);
 
        if (unlikely(blk_queue_dead(q)))
                return NULL;
@@ -808,7 +869,7 @@ retry:
                         */
                        if (!ioc && !retried) {
                                spin_unlock_irq(q->queue_lock);
-                               create_io_context(current, gfp_mask, q->node);
+                               create_io_context(gfp_mask, q->node);
                                spin_lock_irq(q->queue_lock);
                                retried = true;
                                goto retry;
@@ -831,7 +892,7 @@ retry:
                                         * process is not a "batcher", and not
                                         * exempted by the IO scheduler
                                         */
-                                       goto out;
+                                       return NULL;
                                }
                        }
                }
@@ -844,7 +905,7 @@ retry:
         * allocated with any setting of ->nr_requests
         */
        if (rl->count[is_sync] >= (3 * q->nr_requests / 2))
-               goto out;
+               return NULL;
 
        rl->count[is_sync]++;
        rl->starved[is_sync] = 0;
@@ -859,8 +920,7 @@ retry:
         * Also, lookup icq while holding queue_lock.  If it doesn't exist,
         * it will be created after releasing queue_lock.
         */
-       if (blk_rq_should_init_elevator(bio) &&
-           !test_bit(QUEUE_FLAG_ELVSWITCH, &q->queue_flags)) {
+       if (blk_rq_should_init_elevator(bio) && !blk_queue_bypass(q)) {
                rw_flags |= REQ_ELVPRIV;
                rl->elvpriv++;
                if (et->icq_cache && ioc)
@@ -871,41 +931,36 @@ retry:
                rw_flags |= REQ_IO_STAT;
        spin_unlock_irq(q->queue_lock);
 
-       /* create icq if missing */
-       if ((rw_flags & REQ_ELVPRIV) && unlikely(et->icq_cache && !icq)) {
-               icq = ioc_create_icq(q, gfp_mask);
-               if (!icq)
-                       goto fail_icq;
-       }
-
-       rq = blk_alloc_request(q, icq, rw_flags, gfp_mask);
+       /* allocate and init request */
+       rq = mempool_alloc(q->rq.rq_pool, gfp_mask);
+       if (!rq)
+               goto fail_alloc;
 
-fail_icq:
-       if (unlikely(!rq)) {
-               /*
-                * Allocation failed presumably due to memory. Undo anything
-                * we might have messed up.
-                *
-                * Allocating task should really be put onto the front of the
-                * wait queue, but this is pretty rare.
-                */
-               spin_lock_irq(q->queue_lock);
-               freed_request(q, rw_flags);
+       blk_rq_init(q, rq);
+       rq->cmd_flags = rw_flags | REQ_ALLOCED;
+
+       /* init elvpriv */
+       if (rw_flags & REQ_ELVPRIV) {
+               if (unlikely(et->icq_cache && !icq)) {
+                       create_io_context(gfp_mask, q->node);
+                       ioc = rq_ioc(bio);
+                       if (!ioc)
+                               goto fail_elvpriv;
+
+                       icq = ioc_create_icq(ioc, q, gfp_mask);
+                       if (!icq)
+                               goto fail_elvpriv;
+               }
 
-               /*
-                * in the very unlikely event that allocation failed and no
-                * requests for this direction was pending, mark us starved
-                * so that freeing of a request in the other direction will
-                * notice us. another possible fix would be to split the
-                * rq mempool into READ and WRITE
-                */
-rq_starved:
-               if (unlikely(rl->count[is_sync] == 0))
-                       rl->starved[is_sync] = 1;
+               rq->elv.icq = icq;
+               if (unlikely(elv_set_request(q, rq, bio, gfp_mask)))
+                       goto fail_elvpriv;
 
-               goto out;
+               /* @rq->elv.icq holds io_context until @rq is freed */
+               if (icq)
+                       get_io_context(icq->ioc);
        }
-
+out:
        /*
         * ioc may be NULL here, and ioc_batching will be false. That's
         * OK, if the queue is under the request limit then requests need
@@ -916,8 +971,48 @@ rq_starved:
                ioc->nr_batch_requests--;
 
        trace_block_getrq(q, bio, rw_flags & 1);
-out:
        return rq;
+
+fail_elvpriv:
+       /*
+        * elvpriv init failed.  ioc, icq and elvpriv aren't mempool backed
+        * and may fail indefinitely under memory pressure and thus
+        * shouldn't stall IO.  Treat this request as !elvpriv.  This will
+        * disturb iosched and blkcg but weird is bettern than dead.
+        */
+       printk_ratelimited(KERN_WARNING "%s: request aux data allocation failed, iosched may be disturbed\n",
+                          dev_name(q->backing_dev_info.dev));
+
+       rq->cmd_flags &= ~REQ_ELVPRIV;
+       rq->elv.icq = NULL;
+
+       spin_lock_irq(q->queue_lock);
+       rl->elvpriv--;
+       spin_unlock_irq(q->queue_lock);
+       goto out;
+
+fail_alloc:
+       /*
+        * Allocation failed presumably due to memory. Undo anything we
+        * might have messed up.
+        *
+        * Allocating task should really be put onto the front of the wait
+        * queue, but this is pretty rare.
+        */
+       spin_lock_irq(q->queue_lock);
+       freed_request(q, rw_flags);
+
+       /*
+        * in the very unlikely event that allocation failed and no
+        * requests for this direction was pending, mark us starved so that
+        * freeing of a request in the other direction will notice
+        * us. another possible fix would be to split the rq mempool into
+        * READ and WRITE
+        */
+rq_starved:
+       if (unlikely(rl->count[is_sync] == 0))
+               rl->starved[is_sync] = 1;
+       return NULL;
 }
 
 /**
@@ -961,7 +1056,7 @@ static struct request *get_request_wait(struct request_queue *q, int rw_flags,
                 * up to a big batch of them for a small period time.
                 * See ioc_batching, ioc_set_batching
                 */
-               create_io_context(current, GFP_NOIO, q->node);
+               create_io_context(GFP_NOIO, q->node);
                ioc_set_batching(q, current->io_context);
 
                spin_lock_irq(q->queue_lock);
index fb95dd2f889a6071d85e334d7d5d9342ca56ab75..893b8007c657e8bd0ca93d5ba61e9e7d02aa892b 100644 (file)
@@ -155,20 +155,20 @@ void put_io_context(struct io_context *ioc)
 }
 EXPORT_SYMBOL(put_io_context);
 
-/* Called by the exiting task */
-void exit_io_context(struct task_struct *task)
+/**
+ * put_io_context_active - put active reference on ioc
+ * @ioc: ioc of interest
+ *
+ * Undo get_io_context_active().  If active reference reaches zero after
+ * put, @ioc can never issue further IOs and ioscheds are notified.
+ */
+void put_io_context_active(struct io_context *ioc)
 {
-       struct io_context *ioc;
-       struct io_cq *icq;
        struct hlist_node *n;
        unsigned long flags;
+       struct io_cq *icq;
 
-       task_lock(task);
-       ioc = task->io_context;
-       task->io_context = NULL;
-       task_unlock(task);
-
-       if (!atomic_dec_and_test(&ioc->nr_tasks)) {
+       if (!atomic_dec_and_test(&ioc->active_ref)) {
                put_io_context(ioc);
                return;
        }
@@ -197,6 +197,20 @@ retry:
        put_io_context(ioc);
 }
 
+/* Called by the exiting task */
+void exit_io_context(struct task_struct *task)
+{
+       struct io_context *ioc;
+
+       task_lock(task);
+       ioc = task->io_context;
+       task->io_context = NULL;
+       task_unlock(task);
+
+       atomic_dec(&ioc->nr_tasks);
+       put_io_context_active(ioc);
+}
+
 /**
  * ioc_clear_queue - break any ioc association with the specified queue
  * @q: request_queue being cleared
@@ -218,19 +232,19 @@ void ioc_clear_queue(struct request_queue *q)
        }
 }
 
-void create_io_context_slowpath(struct task_struct *task, gfp_t gfp_flags,
-                               int node)
+int create_task_io_context(struct task_struct *task, gfp_t gfp_flags, int node)
 {
        struct io_context *ioc;
+       int ret;
 
        ioc = kmem_cache_alloc_node(iocontext_cachep, gfp_flags | __GFP_ZERO,
                                    node);
        if (unlikely(!ioc))
-               return;
+               return -ENOMEM;
 
        /* initialize */
        atomic_long_set(&ioc->refcount, 1);
-       atomic_set(&ioc->nr_tasks, 1);
+       atomic_set(&ioc->active_ref, 1);
        spin_lock_init(&ioc->lock);
        INIT_RADIX_TREE(&ioc->icq_tree, GFP_ATOMIC | __GFP_HIGH);
        INIT_HLIST_HEAD(&ioc->icq_list);
@@ -249,7 +263,12 @@ void create_io_context_slowpath(struct task_struct *task, gfp_t gfp_flags,
                task->io_context = ioc;
        else
                kmem_cache_free(iocontext_cachep, ioc);
+
+       ret = task->io_context ? 0 : -EBUSY;
+
        task_unlock(task);
+
+       return ret;
 }
 
 /**
@@ -281,7 +300,7 @@ struct io_context *get_task_io_context(struct task_struct *task,
                        return ioc;
                }
                task_unlock(task);
-       } while (create_io_context(task, gfp_flags, node));
+       } while (!create_task_io_context(task, gfp_flags, node));
 
        return NULL;
 }
@@ -325,26 +344,23 @@ EXPORT_SYMBOL(ioc_lookup_icq);
 
 /**
  * ioc_create_icq - create and link io_cq
+ * @ioc: io_context of interest
  * @q: request_queue of interest
  * @gfp_mask: allocation mask
  *
- * Make sure io_cq linking %current->io_context and @q exists.  If either
- * io_context and/or icq don't exist, they will be created using @gfp_mask.
+ * Make sure io_cq linking @ioc and @q exists.  If icq doesn't exist, they
+ * will be created using @gfp_mask.
  *
  * The caller is responsible for ensuring @ioc won't go away and @q is
  * alive and will stay alive until this function returns.
  */
-struct io_cq *ioc_create_icq(struct request_queue *q, gfp_t gfp_mask)
+struct io_cq *ioc_create_icq(struct io_context *ioc, struct request_queue *q,
+                            gfp_t gfp_mask)
 {
        struct elevator_type *et = q->elevator->type;
-       struct io_context *ioc;
        struct io_cq *icq;
 
        /* allocate stuff */
-       ioc = create_io_context(current, gfp_mask, q->node);
-       if (!ioc)
-               return NULL;
-
        icq = kmem_cache_alloc_node(et->icq_cache, gfp_mask | __GFP_ZERO,
                                    q->node);
        if (!icq)
@@ -382,74 +398,6 @@ struct io_cq *ioc_create_icq(struct request_queue *q, gfp_t gfp_mask)
        return icq;
 }
 
-void ioc_set_icq_flags(struct io_context *ioc, unsigned int flags)
-{
-       struct io_cq *icq;
-       struct hlist_node *n;
-
-       hlist_for_each_entry(icq, n, &ioc->icq_list, ioc_node)
-               icq->flags |= flags;
-}
-
-/**
- * ioc_ioprio_changed - notify ioprio change
- * @ioc: io_context of interest
- * @ioprio: new ioprio
- *
- * @ioc's ioprio has changed to @ioprio.  Set %ICQ_IOPRIO_CHANGED for all
- * icq's.  iosched is responsible for checking the bit and applying it on
- * request issue path.
- */
-void ioc_ioprio_changed(struct io_context *ioc, int ioprio)
-{
-       unsigned long flags;
-
-       spin_lock_irqsave(&ioc->lock, flags);
-       ioc->ioprio = ioprio;
-       ioc_set_icq_flags(ioc, ICQ_IOPRIO_CHANGED);
-       spin_unlock_irqrestore(&ioc->lock, flags);
-}
-
-/**
- * ioc_cgroup_changed - notify cgroup change
- * @ioc: io_context of interest
- *
- * @ioc's cgroup has changed.  Set %ICQ_CGROUP_CHANGED for all icq's.
- * iosched is responsible for checking the bit and applying it on request
- * issue path.
- */
-void ioc_cgroup_changed(struct io_context *ioc)
-{
-       unsigned long flags;
-
-       spin_lock_irqsave(&ioc->lock, flags);
-       ioc_set_icq_flags(ioc, ICQ_CGROUP_CHANGED);
-       spin_unlock_irqrestore(&ioc->lock, flags);
-}
-EXPORT_SYMBOL(ioc_cgroup_changed);
-
-/**
- * icq_get_changed - fetch and clear icq changed mask
- * @icq: icq of interest
- *
- * Fetch and clear ICQ_*_CHANGED bits from @icq.  Grabs and releases
- * @icq->ioc->lock.
- */
-unsigned icq_get_changed(struct io_cq *icq)
-{
-       unsigned int changed = 0;
-       unsigned long flags;
-
-       if (unlikely(icq->flags & ICQ_CHANGED_MASK)) {
-               spin_lock_irqsave(&icq->ioc->lock, flags);
-               changed = icq->flags & ICQ_CHANGED_MASK;
-               icq->flags &= ~ICQ_CHANGED_MASK;
-               spin_unlock_irqrestore(&icq->ioc->lock, flags);
-       }
-       return changed;
-}
-EXPORT_SYMBOL(icq_get_changed);
-
 static int __init blk_ioc_init(void)
 {
        iocontext_cachep = kmem_cache_create("blkdev_ioc",
index cf150011d808bc71fee8ff894387de0581235f56..aa41b47c22d2e89525a5bd3cfb9501e67634bff7 100644 (file)
@@ -9,6 +9,7 @@
 #include <linux/blktrace_api.h>
 
 #include "blk.h"
+#include "blk-cgroup.h"
 
 struct queue_sysfs_entry {
        struct attribute attr;
@@ -479,6 +480,8 @@ static void blk_release_queue(struct kobject *kobj)
 
        blk_sync_queue(q);
 
+       blkcg_exit_queue(q);
+
        if (q->elevator) {
                spin_lock_irq(q->queue_lock);
                ioc_clear_queue(q);
@@ -486,15 +489,12 @@ static void blk_release_queue(struct kobject *kobj)
                elevator_exit(q->elevator);
        }
 
-       blk_throtl_exit(q);
-
        if (rl->rq_pool)
                mempool_destroy(rl->rq_pool);
 
        if (q->queue_tags)
                __blk_queue_free_tags(q);
 
-       blk_throtl_release(q);
        blk_trace_shutdown(q);
 
        bdi_destroy(&q->backing_dev_info);
index f2ddb94626bd49df5e942591998d19ff8665a0eb..5b0659512047208efdcc3db7d714bc72dfd456f5 100644 (file)
@@ -21,6 +21,8 @@ static int throtl_quantum = 32;
 /* Throttling is performed over 100ms slice and after that slice is renewed */
 static unsigned long throtl_slice = HZ/10;     /* 100 ms */
 
+static struct blkcg_policy blkcg_policy_throtl;
+
 /* A workqueue to queue throttle related work */
 static struct workqueue_struct *kthrotld_workqueue;
 static void throtl_schedule_delayed_work(struct throtl_data *td,
@@ -38,9 +40,17 @@ struct throtl_rb_root {
 
 #define rb_entry_tg(node)      rb_entry((node), struct throtl_grp, rb_node)
 
+/* Per-cpu group stats */
+struct tg_stats_cpu {
+       /* total bytes transferred */
+       struct blkg_rwstat              service_bytes;
+       /* total IOs serviced, post merge */
+       struct blkg_rwstat              serviced;
+};
+
 struct throtl_grp {
-       /* List of throtl groups on the request queue*/
-       struct hlist_node tg_node;
+       /* must be the first member */
+       struct blkg_policy_data pd;
 
        /* active throtl group service_tree member */
        struct rb_node rb_node;
@@ -52,8 +62,6 @@ struct throtl_grp {
         */
        unsigned long disptime;
 
-       struct blkio_group blkg;
-       atomic_t ref;
        unsigned int flags;
 
        /* Two lists for READ and WRITE */
@@ -80,18 +88,18 @@ struct throtl_grp {
        /* Some throttle limits got updated for the group */
        int limits_changed;
 
-       struct rcu_head rcu_head;
+       /* Per cpu stats pointer */
+       struct tg_stats_cpu __percpu *stats_cpu;
+
+       /* List of tgs waiting for per cpu stats memory to be allocated */
+       struct list_head stats_alloc_node;
 };
 
 struct throtl_data
 {
-       /* List of throtl groups */
-       struct hlist_head tg_list;
-
        /* service tree for active throtl groups */
        struct throtl_rb_root tg_service_tree;
 
-       struct throtl_grp *root_tg;
        struct request_queue *queue;
 
        /* Total Number of queued bios on READ and WRITE lists */
@@ -108,6 +116,33 @@ struct throtl_data
        int limits_changed;
 };
 
+/* list and work item to allocate percpu group stats */
+static DEFINE_SPINLOCK(tg_stats_alloc_lock);
+static LIST_HEAD(tg_stats_alloc_list);
+
+static void tg_stats_alloc_fn(struct work_struct *);
+static DECLARE_DELAYED_WORK(tg_stats_alloc_work, tg_stats_alloc_fn);
+
+static inline struct throtl_grp *pd_to_tg(struct blkg_policy_data *pd)
+{
+       return pd ? container_of(pd, struct throtl_grp, pd) : NULL;
+}
+
+static inline struct throtl_grp *blkg_to_tg(struct blkcg_gq *blkg)
+{
+       return pd_to_tg(blkg_to_pd(blkg, &blkcg_policy_throtl));
+}
+
+static inline struct blkcg_gq *tg_to_blkg(struct throtl_grp *tg)
+{
+       return pd_to_blkg(&tg->pd);
+}
+
+static inline struct throtl_grp *td_root_tg(struct throtl_data *td)
+{
+       return blkg_to_tg(td->queue->root_blkg);
+}
+
 enum tg_state_flags {
        THROTL_TG_FLAG_on_rr = 0,       /* on round-robin busy list */
 };
@@ -128,244 +163,150 @@ static inline int throtl_tg_##name(const struct throtl_grp *tg)         \
 
 THROTL_TG_FNS(on_rr);
 
-#define throtl_log_tg(td, tg, fmt, args...)                            \
-       blk_add_trace_msg((td)->queue, "throtl %s " fmt,                \
-                               blkg_path(&(tg)->blkg), ##args);        \
+#define throtl_log_tg(td, tg, fmt, args...)    do {                    \
+       char __pbuf[128];                                               \
+                                                                       \
+       blkg_path(tg_to_blkg(tg), __pbuf, sizeof(__pbuf));              \
+       blk_add_trace_msg((td)->queue, "throtl %s " fmt, __pbuf, ##args); \
+} while (0)
 
 #define throtl_log(td, fmt, args...)   \
        blk_add_trace_msg((td)->queue, "throtl " fmt, ##args)
 
-static inline struct throtl_grp *tg_of_blkg(struct blkio_group *blkg)
-{
-       if (blkg)
-               return container_of(blkg, struct throtl_grp, blkg);
-
-       return NULL;
-}
-
 static inline unsigned int total_nr_queued(struct throtl_data *td)
 {
        return td->nr_queued[0] + td->nr_queued[1];
 }
 
-static inline struct throtl_grp *throtl_ref_get_tg(struct throtl_grp *tg)
-{
-       atomic_inc(&tg->ref);
-       return tg;
-}
-
-static void throtl_free_tg(struct rcu_head *head)
+/*
+ * Worker for allocating per cpu stat for tgs. This is scheduled on the
+ * system_nrt_wq once there are some groups on the alloc_list waiting for
+ * allocation.
+ */
+static void tg_stats_alloc_fn(struct work_struct *work)
 {
-       struct throtl_grp *tg;
+       static struct tg_stats_cpu *stats_cpu;  /* this fn is non-reentrant */
+       struct delayed_work *dwork = to_delayed_work(work);
+       bool empty = false;
+
+alloc_stats:
+       if (!stats_cpu) {
+               stats_cpu = alloc_percpu(struct tg_stats_cpu);
+               if (!stats_cpu) {
+                       /* allocation failed, try again after some time */
+                       queue_delayed_work(system_nrt_wq, dwork,
+                                          msecs_to_jiffies(10));
+                       return;
+               }
+       }
 
-       tg = container_of(head, struct throtl_grp, rcu_head);
-       free_percpu(tg->blkg.stats_cpu);
-       kfree(tg);
-}
+       spin_lock_irq(&tg_stats_alloc_lock);
 
-static void throtl_put_tg(struct throtl_grp *tg)
-{
-       BUG_ON(atomic_read(&tg->ref) <= 0);
-       if (!atomic_dec_and_test(&tg->ref))
-               return;
+       if (!list_empty(&tg_stats_alloc_list)) {
+               struct throtl_grp *tg = list_first_entry(&tg_stats_alloc_list,
+                                                        struct throtl_grp,
+                                                        stats_alloc_node);
+               swap(tg->stats_cpu, stats_cpu);
+               list_del_init(&tg->stats_alloc_node);
+       }
 
-       /*
-        * A group is freed in rcu manner. But having an rcu lock does not
-        * mean that one can access all the fields of blkg and assume these
-        * are valid. For example, don't try to follow throtl_data and
-        * request queue links.
-        *
-        * Having a reference to blkg under an rcu allows acess to only
-        * values local to groups like group stats and group rate limits
-        */
-       call_rcu(&tg->rcu_head, throtl_free_tg);
+       empty = list_empty(&tg_stats_alloc_list);
+       spin_unlock_irq(&tg_stats_alloc_lock);
+       if (!empty)
+               goto alloc_stats;
 }
 
-static void throtl_init_group(struct throtl_grp *tg)
+static void throtl_pd_init(struct blkcg_gq *blkg)
 {
-       INIT_HLIST_NODE(&tg->tg_node);
+       struct throtl_grp *tg = blkg_to_tg(blkg);
+       unsigned long flags;
+
        RB_CLEAR_NODE(&tg->rb_node);
        bio_list_init(&tg->bio_lists[0]);
        bio_list_init(&tg->bio_lists[1]);
        tg->limits_changed = false;
 
-       /* Practically unlimited BW */
-       tg->bps[0] = tg->bps[1] = -1;
-       tg->iops[0] = tg->iops[1] = -1;
+       tg->bps[READ] = -1;
+       tg->bps[WRITE] = -1;
+       tg->iops[READ] = -1;
+       tg->iops[WRITE] = -1;
 
        /*
-        * Take the initial reference that will be released on destroy
-        * This can be thought of a joint reference by cgroup and
-        * request queue which will be dropped by either request queue
-        * exit or cgroup deletion path depending on who is exiting first.
+        * Ugh... We need to perform per-cpu allocation for tg->stats_cpu
+        * but percpu allocator can't be called from IO path.  Queue tg on
+        * tg_stats_alloc_list and allocate from work item.
         */
-       atomic_set(&tg->ref, 1);
+       spin_lock_irqsave(&tg_stats_alloc_lock, flags);
+       list_add(&tg->stats_alloc_node, &tg_stats_alloc_list);
+       queue_delayed_work(system_nrt_wq, &tg_stats_alloc_work, 0);
+       spin_unlock_irqrestore(&tg_stats_alloc_lock, flags);
 }
 
-/* Should be called with rcu read lock held (needed for blkcg) */
-static void
-throtl_add_group_to_td_list(struct throtl_data *td, struct throtl_grp *tg)
+static void throtl_pd_exit(struct blkcg_gq *blkg)
 {
-       hlist_add_head(&tg->tg_node, &td->tg_list);
-       td->nr_undestroyed_grps++;
-}
-
-static void
-__throtl_tg_fill_dev_details(struct throtl_data *td, struct throtl_grp *tg)
-{
-       struct backing_dev_info *bdi = &td->queue->backing_dev_info;
-       unsigned int major, minor;
-
-       if (!tg || tg->blkg.dev)
-               return;
-
-       /*
-        * Fill in device details for a group which might not have been
-        * filled at group creation time as queue was being instantiated
-        * and driver had not attached a device yet
-        */
-       if (bdi->dev && dev_name(bdi->dev)) {
-               sscanf(dev_name(bdi->dev), "%u:%u", &major, &minor);
-               tg->blkg.dev = MKDEV(major, minor);
-       }
-}
-
-/*
- * Should be called with without queue lock held. Here queue lock will be
- * taken rarely. It will be taken only once during life time of a group
- * if need be
- */
-static void
-throtl_tg_fill_dev_details(struct throtl_data *td, struct throtl_grp *tg)
-{
-       if (!tg || tg->blkg.dev)
-               return;
-
-       spin_lock_irq(td->queue->queue_lock);
-       __throtl_tg_fill_dev_details(td, tg);
-       spin_unlock_irq(td->queue->queue_lock);
-}
-
-static void throtl_init_add_tg_lists(struct throtl_data *td,
-                       struct throtl_grp *tg, struct blkio_cgroup *blkcg)
-{
-       __throtl_tg_fill_dev_details(td, tg);
-
-       /* Add group onto cgroup list */
-       blkiocg_add_blkio_group(blkcg, &tg->blkg, (void *)td,
-                               tg->blkg.dev, BLKIO_POLICY_THROTL);
+       struct throtl_grp *tg = blkg_to_tg(blkg);
+       unsigned long flags;
 
-       tg->bps[READ] = blkcg_get_read_bps(blkcg, tg->blkg.dev);
-       tg->bps[WRITE] = blkcg_get_write_bps(blkcg, tg->blkg.dev);
-       tg->iops[READ] = blkcg_get_read_iops(blkcg, tg->blkg.dev);
-       tg->iops[WRITE] = blkcg_get_write_iops(blkcg, tg->blkg.dev);
+       spin_lock_irqsave(&tg_stats_alloc_lock, flags);
+       list_del_init(&tg->stats_alloc_node);
+       spin_unlock_irqrestore(&tg_stats_alloc_lock, flags);
 
-       throtl_add_group_to_td_list(td, tg);
+       free_percpu(tg->stats_cpu);
 }
 
-/* Should be called without queue lock and outside of rcu period */
-static struct throtl_grp *throtl_alloc_tg(struct throtl_data *td)
+static void throtl_pd_reset_stats(struct blkcg_gq *blkg)
 {
-       struct throtl_grp *tg = NULL;
-       int ret;
+       struct throtl_grp *tg = blkg_to_tg(blkg);
+       int cpu;
 
-       tg = kzalloc_node(sizeof(*tg), GFP_ATOMIC, td->queue->node);
-       if (!tg)
-               return NULL;
+       if (tg->stats_cpu == NULL)
+               return;
 
-       ret = blkio_alloc_blkg_stats(&tg->blkg);
+       for_each_possible_cpu(cpu) {
+               struct tg_stats_cpu *sc = per_cpu_ptr(tg->stats_cpu, cpu);
 
-       if (ret) {
-               kfree(tg);
-               return NULL;
+               blkg_rwstat_reset(&sc->service_bytes);
+               blkg_rwstat_reset(&sc->serviced);
        }
-
-       throtl_init_group(tg);
-       return tg;
 }
 
-static struct
-throtl_grp *throtl_find_tg(struct throtl_data *td, struct blkio_cgroup *blkcg)
+static struct throtl_grp *throtl_lookup_tg(struct throtl_data *td,
+                                          struct blkcg *blkcg)
 {
-       struct throtl_grp *tg = NULL;
-       void *key = td;
-
        /*
-        * This is the common case when there are no blkio cgroups.
-        * Avoid lookup in this case
-        */
-       if (blkcg == &blkio_root_cgroup)
-               tg = td->root_tg;
-       else
-               tg = tg_of_blkg(blkiocg_lookup_group(blkcg, key));
+        * This is the common case when there are no blkcgs.  Avoid lookup
+        * in this case
+        */
+       if (blkcg == &blkcg_root)
+               return td_root_tg(td);
 
-       __throtl_tg_fill_dev_details(td, tg);
-       return tg;
+       return blkg_to_tg(blkg_lookup(blkcg, td->queue));
 }
 
-static struct throtl_grp * throtl_get_tg(struct throtl_data *td)
+static struct throtl_grp *throtl_lookup_create_tg(struct throtl_data *td,
+                                                 struct blkcg *blkcg)
 {
-       struct throtl_grp *tg = NULL, *__tg = NULL;
-       struct blkio_cgroup *blkcg;
        struct request_queue *q = td->queue;
-
-       /* no throttling for dead queue */
-       if (unlikely(blk_queue_dead(q)))
-               return NULL;
-
-       rcu_read_lock();
-       blkcg = task_blkio_cgroup(current);
-       tg = throtl_find_tg(td, blkcg);
-       if (tg) {
-               rcu_read_unlock();
-               return tg;
-       }
-
-       /*
-        * Need to allocate a group. Allocation of group also needs allocation
-        * of per cpu stats which in-turn takes a mutex() and can block. Hence
-        * we need to drop rcu lock and queue_lock before we call alloc.
-        */
-       rcu_read_unlock();
-       spin_unlock_irq(q->queue_lock);
-
-       tg = throtl_alloc_tg(td);
-
-       /* Group allocated and queue is still alive. take the lock */
-       spin_lock_irq(q->queue_lock);
-
-       /* Make sure @q is still alive */
-       if (unlikely(blk_queue_dead(q))) {
-               kfree(tg);
-               return NULL;
-       }
-
-       /*
-        * Initialize the new group. After sleeping, read the blkcg again.
-        */
-       rcu_read_lock();
-       blkcg = task_blkio_cgroup(current);
+       struct throtl_grp *tg = NULL;
 
        /*
-        * If some other thread already allocated the group while we were
-        * not holding queue lock, free up the group
+        * This is the common case when there are no blkcgs.  Avoid lookup
+        * in this case
         */
-       __tg = throtl_find_tg(td, blkcg);
-
-       if (__tg) {
-               kfree(tg);
-               rcu_read_unlock();
-               return __tg;
-       }
-
-       /* Group allocation failed. Account the IO to root group */
-       if (!tg) {
-               tg = td->root_tg;
-               return tg;
+       if (blkcg == &blkcg_root) {
+               tg = td_root_tg(td);
+       } else {
+               struct blkcg_gq *blkg;
+
+               blkg = blkg_lookup_create(blkcg, q);
+
+               /* if %NULL and @q is alive, fall back to root_tg */
+               if (!IS_ERR(blkg))
+                       tg = blkg_to_tg(blkg);
+               else if (!blk_queue_dead(q))
+                       tg = td_root_tg(td);
        }
 
-       throtl_init_add_tg_lists(td, tg, blkcg);
-       rcu_read_unlock();
        return tg;
 }
 
@@ -734,16 +675,41 @@ static bool tg_may_dispatch(struct throtl_data *td, struct throtl_grp *tg,
        return 0;
 }
 
+static void throtl_update_dispatch_stats(struct blkcg_gq *blkg, u64 bytes,
+                                        int rw)
+{
+       struct throtl_grp *tg = blkg_to_tg(blkg);
+       struct tg_stats_cpu *stats_cpu;
+       unsigned long flags;
+
+       /* If per cpu stats are not allocated yet, don't do any accounting. */
+       if (tg->stats_cpu == NULL)
+               return;
+
+       /*
+        * Disabling interrupts to provide mutual exclusion between two
+        * writes on same cpu. It probably is not needed for 64bit. Not
+        * optimizing that case yet.
+        */
+       local_irq_save(flags);
+
+       stats_cpu = this_cpu_ptr(tg->stats_cpu);
+
+       blkg_rwstat_add(&stats_cpu->serviced, rw, 1);
+       blkg_rwstat_add(&stats_cpu->service_bytes, rw, bytes);
+
+       local_irq_restore(flags);
+}
+
 static void throtl_charge_bio(struct throtl_grp *tg, struct bio *bio)
 {
        bool rw = bio_data_dir(bio);
-       bool sync = rw_is_sync(bio->bi_rw);
 
        /* Charge the bio to the group */
        tg->bytes_disp[rw] += bio->bi_size;
        tg->io_disp[rw]++;
 
-       blkiocg_update_dispatch_stats(&tg->blkg, bio->bi_size, rw, sync);
+       throtl_update_dispatch_stats(tg_to_blkg(tg), bio->bi_size, bio->bi_rw);
 }
 
 static void throtl_add_bio_tg(struct throtl_data *td, struct throtl_grp *tg,
@@ -753,7 +719,7 @@ static void throtl_add_bio_tg(struct throtl_data *td, struct throtl_grp *tg,
 
        bio_list_add(&tg->bio_lists[rw], bio);
        /* Take a bio reference on tg */
-       throtl_ref_get_tg(tg);
+       blkg_get(tg_to_blkg(tg));
        tg->nr_queued[rw]++;
        td->nr_queued[rw]++;
        throtl_enqueue_tg(td, tg);
@@ -786,8 +752,8 @@ static void tg_dispatch_one_bio(struct throtl_data *td, struct throtl_grp *tg,
 
        bio = bio_list_pop(&tg->bio_lists[rw]);
        tg->nr_queued[rw]--;
-       /* Drop bio reference on tg */
-       throtl_put_tg(tg);
+       /* Drop bio reference on blkg */
+       blkg_put(tg_to_blkg(tg));
 
        BUG_ON(td->nr_queued[rw] <= 0);
        td->nr_queued[rw]--;
@@ -865,8 +831,8 @@ static int throtl_select_dispatch(struct throtl_data *td, struct bio_list *bl)
 
 static void throtl_process_limit_change(struct throtl_data *td)
 {
-       struct throtl_grp *tg;
-       struct hlist_node *pos, *n;
+       struct request_queue *q = td->queue;
+       struct blkcg_gq *blkg, *n;
 
        if (!td->limits_changed)
                return;
@@ -875,7 +841,9 @@ static void throtl_process_limit_change(struct throtl_data *td)
 
        throtl_log(td, "limits changed");
 
-       hlist_for_each_entry_safe(tg, pos, n, &td->tg_list, tg_node) {
+       list_for_each_entry_safe(blkg, n, &q->blkg_list, q_node) {
+               struct throtl_grp *tg = blkg_to_tg(blkg);
+
                if (!tg->limits_changed)
                        continue;
 
@@ -973,120 +941,159 @@ throtl_schedule_delayed_work(struct throtl_data *td, unsigned long delay)
        }
 }
 
-static void
-throtl_destroy_tg(struct throtl_data *td, struct throtl_grp *tg)
+static u64 tg_prfill_cpu_rwstat(struct seq_file *sf,
+                               struct blkg_policy_data *pd, int off)
 {
-       /* Something wrong if we are trying to remove same group twice */
-       BUG_ON(hlist_unhashed(&tg->tg_node));
+       struct throtl_grp *tg = pd_to_tg(pd);
+       struct blkg_rwstat rwstat = { }, tmp;
+       int i, cpu;
 
-       hlist_del_init(&tg->tg_node);
+       for_each_possible_cpu(cpu) {
+               struct tg_stats_cpu *sc = per_cpu_ptr(tg->stats_cpu, cpu);
 
-       /*
-        * Put the reference taken at the time of creation so that when all
-        * queues are gone, group can be destroyed.
-        */
-       throtl_put_tg(tg);
-       td->nr_undestroyed_grps--;
+               tmp = blkg_rwstat_read((void *)sc + off);
+               for (i = 0; i < BLKG_RWSTAT_NR; i++)
+                       rwstat.cnt[i] += tmp.cnt[i];
+       }
+
+       return __blkg_prfill_rwstat(sf, pd, &rwstat);
 }
 
-static void throtl_release_tgs(struct throtl_data *td)
+static int tg_print_cpu_rwstat(struct cgroup *cgrp, struct cftype *cft,
+                              struct seq_file *sf)
 {
-       struct hlist_node *pos, *n;
-       struct throtl_grp *tg;
+       struct blkcg *blkcg = cgroup_to_blkcg(cgrp);
 
-       hlist_for_each_entry_safe(tg, pos, n, &td->tg_list, tg_node) {
-               /*
-                * If cgroup removal path got to blk_group first and removed
-                * it from cgroup list, then it will take care of destroying
-                * cfqg also.
-                */
-               if (!blkiocg_del_blkio_group(&tg->blkg))
-                       throtl_destroy_tg(td, tg);
-       }
+       blkcg_print_blkgs(sf, blkcg, tg_prfill_cpu_rwstat, &blkcg_policy_throtl,
+                         cft->private, true);
+       return 0;
 }
 
-/*
- * Blk cgroup controller notification saying that blkio_group object is being
- * delinked as associated cgroup object is going away. That also means that
- * no new IO will come in this group. So get rid of this group as soon as
- * any pending IO in the group is finished.
- *
- * This function is called under rcu_read_lock(). key is the rcu protected
- * pointer. That means "key" is a valid throtl_data pointer as long as we are
- * rcu read lock.
- *
- * "key" was fetched from blkio_group under blkio_cgroup->lock. That means
- * it should not be NULL as even if queue was going away, cgroup deltion
- * path got to it first.
- */
-void throtl_unlink_blkio_group(void *key, struct blkio_group *blkg)
+static u64 tg_prfill_conf_u64(struct seq_file *sf, struct blkg_policy_data *pd,
+                             int off)
 {
-       unsigned long flags;
-       struct throtl_data *td = key;
+       struct throtl_grp *tg = pd_to_tg(pd);
+       u64 v = *(u64 *)((void *)tg + off);
 
-       spin_lock_irqsave(td->queue->queue_lock, flags);
-       throtl_destroy_tg(td, tg_of_blkg(blkg));
-       spin_unlock_irqrestore(td->queue->queue_lock, flags);
+       if (v == -1)
+               return 0;
+       return __blkg_prfill_u64(sf, pd, v);
 }
 
-static void throtl_update_blkio_group_common(struct throtl_data *td,
-                               struct throtl_grp *tg)
+static u64 tg_prfill_conf_uint(struct seq_file *sf, struct blkg_policy_data *pd,
+                              int off)
 {
-       xchg(&tg->limits_changed, true);
-       xchg(&td->limits_changed, true);
-       /* Schedule a work now to process the limit change */
-       throtl_schedule_delayed_work(td, 0);
+       struct throtl_grp *tg = pd_to_tg(pd);
+       unsigned int v = *(unsigned int *)((void *)tg + off);
+
+       if (v == -1)
+               return 0;
+       return __blkg_prfill_u64(sf, pd, v);
 }
 
-/*
- * For all update functions, key should be a valid pointer because these
- * update functions are called under blkcg_lock, that means, blkg is
- * valid and in turn key is valid. queue exit path can not race because
- * of blkcg_lock
- *
- * Can not take queue lock in update functions as queue lock under blkcg_lock
- * is not allowed. Under other paths we take blkcg_lock under queue_lock.
- */
-static void throtl_update_blkio_group_read_bps(void *key,
-                               struct blkio_group *blkg, u64 read_bps)
+static int tg_print_conf_u64(struct cgroup *cgrp, struct cftype *cft,
+                            struct seq_file *sf)
 {
-       struct throtl_data *td = key;
-       struct throtl_grp *tg = tg_of_blkg(blkg);
-
-       tg->bps[READ] = read_bps;
-       throtl_update_blkio_group_common(td, tg);
+       blkcg_print_blkgs(sf, cgroup_to_blkcg(cgrp), tg_prfill_conf_u64,
+                         &blkcg_policy_throtl, cft->private, false);
+       return 0;
 }
 
-static void throtl_update_blkio_group_write_bps(void *key,
-                               struct blkio_group *blkg, u64 write_bps)
+static int tg_print_conf_uint(struct cgroup *cgrp, struct cftype *cft,
+                             struct seq_file *sf)
 {
-       struct throtl_data *td = key;
-       struct throtl_grp *tg = tg_of_blkg(blkg);
-
-       tg->bps[WRITE] = write_bps;
-       throtl_update_blkio_group_common(td, tg);
+       blkcg_print_blkgs(sf, cgroup_to_blkcg(cgrp), tg_prfill_conf_uint,
+                         &blkcg_policy_throtl, cft->private, false);
+       return 0;
 }
 
-static void throtl_update_blkio_group_read_iops(void *key,
-                       struct blkio_group *blkg, unsigned int read_iops)
+static int tg_set_conf(struct cgroup *cgrp, struct cftype *cft, const char *buf,
+                      bool is_u64)
 {
-       struct throtl_data *td = key;
-       struct throtl_grp *tg = tg_of_blkg(blkg);
+       struct blkcg *blkcg = cgroup_to_blkcg(cgrp);
+       struct blkg_conf_ctx ctx;
+       struct throtl_grp *tg;
+       struct throtl_data *td;
+       int ret;
+
+       ret = blkg_conf_prep(blkcg, &blkcg_policy_throtl, buf, &ctx);
+       if (ret)
+               return ret;
+
+       tg = blkg_to_tg(ctx.blkg);
+       td = ctx.blkg->q->td;
+
+       if (!ctx.v)
+               ctx.v = -1;
+
+       if (is_u64)
+               *(u64 *)((void *)tg + cft->private) = ctx.v;
+       else
+               *(unsigned int *)((void *)tg + cft->private) = ctx.v;
+
+       /* XXX: we don't need the following deferred processing */
+       xchg(&tg->limits_changed, true);
+       xchg(&td->limits_changed, true);
+       throtl_schedule_delayed_work(td, 0);
 
-       tg->iops[READ] = read_iops;
-       throtl_update_blkio_group_common(td, tg);
+       blkg_conf_finish(&ctx);
+       return 0;
 }
 
-static void throtl_update_blkio_group_write_iops(void *key,
-                       struct blkio_group *blkg, unsigned int write_iops)
+static int tg_set_conf_u64(struct cgroup *cgrp, struct cftype *cft,
+                          const char *buf)
 {
-       struct throtl_data *td = key;
-       struct throtl_grp *tg = tg_of_blkg(blkg);
+       return tg_set_conf(cgrp, cft, buf, true);
+}
 
-       tg->iops[WRITE] = write_iops;
-       throtl_update_blkio_group_common(td, tg);
+static int tg_set_conf_uint(struct cgroup *cgrp, struct cftype *cft,
+                           const char *buf)
+{
+       return tg_set_conf(cgrp, cft, buf, false);
 }
 
+static struct cftype throtl_files[] = {
+       {
+               .name = "throttle.read_bps_device",
+               .private = offsetof(struct throtl_grp, bps[READ]),
+               .read_seq_string = tg_print_conf_u64,
+               .write_string = tg_set_conf_u64,
+               .max_write_len = 256,
+       },
+       {
+               .name = "throttle.write_bps_device",
+               .private = offsetof(struct throtl_grp, bps[WRITE]),
+               .read_seq_string = tg_print_conf_u64,
+               .write_string = tg_set_conf_u64,
+               .max_write_len = 256,
+       },
+       {
+               .name = "throttle.read_iops_device",
+               .private = offsetof(struct throtl_grp, iops[READ]),
+               .read_seq_string = tg_print_conf_uint,
+               .write_string = tg_set_conf_uint,
+               .max_write_len = 256,
+       },
+       {
+               .name = "throttle.write_iops_device",
+               .private = offsetof(struct throtl_grp, iops[WRITE]),
+               .read_seq_string = tg_print_conf_uint,
+               .write_string = tg_set_conf_uint,
+               .max_write_len = 256,
+       },
+       {
+               .name = "throttle.io_service_bytes",
+               .private = offsetof(struct tg_stats_cpu, service_bytes),
+               .read_seq_string = tg_print_cpu_rwstat,
+       },
+       {
+               .name = "throttle.io_serviced",
+               .private = offsetof(struct tg_stats_cpu, serviced),
+               .read_seq_string = tg_print_cpu_rwstat,
+       },
+       { }     /* terminate */
+};
+
 static void throtl_shutdown_wq(struct request_queue *q)
 {
        struct throtl_data *td = q->td;
@@ -1094,19 +1101,13 @@ static void throtl_shutdown_wq(struct request_queue *q)
        cancel_delayed_work_sync(&td->throtl_work);
 }
 
-static struct blkio_policy_type blkio_policy_throtl = {
-       .ops = {
-               .blkio_unlink_group_fn = throtl_unlink_blkio_group,
-               .blkio_update_group_read_bps_fn =
-                                       throtl_update_blkio_group_read_bps,
-               .blkio_update_group_write_bps_fn =
-                                       throtl_update_blkio_group_write_bps,
-               .blkio_update_group_read_iops_fn =
-                                       throtl_update_blkio_group_read_iops,
-               .blkio_update_group_write_iops_fn =
-                                       throtl_update_blkio_group_write_iops,
-       },
-       .plid = BLKIO_POLICY_THROTL,
+static struct blkcg_policy blkcg_policy_throtl = {
+       .pd_size                = sizeof(struct throtl_grp),
+       .cftypes                = throtl_files,
+
+       .pd_init_fn             = throtl_pd_init,
+       .pd_exit_fn             = throtl_pd_exit,
+       .pd_reset_stats_fn      = throtl_pd_reset_stats,
 };
 
 bool blk_throtl_bio(struct request_queue *q, struct bio *bio)
@@ -1114,7 +1115,7 @@ bool blk_throtl_bio(struct request_queue *q, struct bio *bio)
        struct throtl_data *td = q->td;
        struct throtl_grp *tg;
        bool rw = bio_data_dir(bio), update_disptime = true;
-       struct blkio_cgroup *blkcg;
+       struct blkcg *blkcg;
        bool throttled = false;
 
        if (bio->bi_rw & REQ_THROTTLED) {
@@ -1122,33 +1123,31 @@ bool blk_throtl_bio(struct request_queue *q, struct bio *bio)
                goto out;
        }
 
+       /* bio_associate_current() needs ioc, try creating */
+       create_io_context(GFP_ATOMIC, q->node);
+
        /*
         * A throtl_grp pointer retrieved under rcu can be used to access
         * basic fields like stats and io rates. If a group has no rules,
         * just update the dispatch stats in lockless manner and return.
         */
-
        rcu_read_lock();
-       blkcg = task_blkio_cgroup(current);
-       tg = throtl_find_tg(td, blkcg);
+       blkcg = bio_blkcg(bio);
+       tg = throtl_lookup_tg(td, blkcg);
        if (tg) {
-               throtl_tg_fill_dev_details(td, tg);
-
                if (tg_no_rule_group(tg, rw)) {
-                       blkiocg_update_dispatch_stats(&tg->blkg, bio->bi_size,
-                                       rw, rw_is_sync(bio->bi_rw));
-                       rcu_read_unlock();
-                       goto out;
+                       throtl_update_dispatch_stats(tg_to_blkg(tg),
+                                                    bio->bi_size, bio->bi_rw);
+                       goto out_unlock_rcu;
                }
        }
-       rcu_read_unlock();
 
        /*
         * Either group has not been allocated yet or it is not an unlimited
         * IO group
         */
        spin_lock_irq(q->queue_lock);
-       tg = throtl_get_tg(td);
+       tg = throtl_lookup_create_tg(td, blkcg);
        if (unlikely(!tg))
                goto out_unlock;
 
@@ -1189,6 +1188,7 @@ queue_bio:
                        tg->io_disp[rw], tg->iops[rw],
                        tg->nr_queued[READ], tg->nr_queued[WRITE]);
 
+       bio_associate_current(bio);
        throtl_add_bio_tg(q->td, tg, bio);
        throttled = true;
 
@@ -1199,6 +1199,8 @@ queue_bio:
 
 out_unlock:
        spin_unlock_irq(q->queue_lock);
+out_unlock_rcu:
+       rcu_read_unlock();
 out:
        return throttled;
 }
@@ -1241,79 +1243,31 @@ void blk_throtl_drain(struct request_queue *q)
 int blk_throtl_init(struct request_queue *q)
 {
        struct throtl_data *td;
-       struct throtl_grp *tg;
+       int ret;
 
        td = kzalloc_node(sizeof(*td), GFP_KERNEL, q->node);
        if (!td)
                return -ENOMEM;
 
-       INIT_HLIST_HEAD(&td->tg_list);
        td->tg_service_tree = THROTL_RB_ROOT;
        td->limits_changed = false;
        INIT_DELAYED_WORK(&td->throtl_work, blk_throtl_work);
 
-       /* alloc and Init root group. */
+       q->td = td;
        td->queue = q;
-       tg = throtl_alloc_tg(td);
 
-       if (!tg) {
+       /* activate policy */
+       ret = blkcg_activate_policy(q, &blkcg_policy_throtl);
+       if (ret)
                kfree(td);
-               return -ENOMEM;
-       }
-
-       td->root_tg = tg;
-
-       rcu_read_lock();
-       throtl_init_add_tg_lists(td, tg, &blkio_root_cgroup);
-       rcu_read_unlock();
-
-       /* Attach throtl data to request queue */
-       q->td = td;
-       return 0;
+       return ret;
 }
 
 void blk_throtl_exit(struct request_queue *q)
 {
-       struct throtl_data *td = q->td;
-       bool wait = false;
-
-       BUG_ON(!td);
-
-       throtl_shutdown_wq(q);
-
-       spin_lock_irq(q->queue_lock);
-       throtl_release_tgs(td);
-
-       /* If there are other groups */
-       if (td->nr_undestroyed_grps > 0)
-               wait = true;
-
-       spin_unlock_irq(q->queue_lock);
-
-       /*
-        * Wait for tg->blkg->key accessors to exit their grace periods.
-        * Do this wait only if there are other undestroyed groups out
-        * there (other than root group). This can happen if cgroup deletion
-        * path claimed the responsibility of cleaning up a group before
-        * queue cleanup code get to the group.
-        *
-        * Do not call synchronize_rcu() unconditionally as there are drivers
-        * which create/delete request queue hundreds of times during scan/boot
-        * and synchronize_rcu() can take significant time and slow down boot.
-        */
-       if (wait)
-               synchronize_rcu();
-
-       /*
-        * Just being safe to make sure after previous flush if some body did
-        * update limits through cgroup and another work got queued, cancel
-        * it.
-        */
+       BUG_ON(!q->td);
        throtl_shutdown_wq(q);
-}
-
-void blk_throtl_release(struct request_queue *q)
-{
+       blkcg_deactivate_policy(q, &blkcg_policy_throtl);
        kfree(q->td);
 }
 
@@ -1323,8 +1277,7 @@ static int __init throtl_init(void)
        if (!kthrotld_workqueue)
                panic("Failed to create kthrotld\n");
 
-       blkio_policy_register(&blkio_policy_throtl);
-       return 0;
+       return blkcg_policy_register(&blkcg_policy_throtl);
 }
 
 module_init(throtl_init);
index d45be871329ec67121d2fd41a53289a593f72eb7..85f6ae42f7d3f698e9e82c75064f428065953e70 100644 (file)
@@ -23,7 +23,8 @@ void blk_rq_bio_prep(struct request_queue *q, struct request *rq,
                        struct bio *bio);
 int blk_rq_append_bio(struct request_queue *q, struct request *rq,
                      struct bio *bio);
-void blk_drain_queue(struct request_queue *q, bool drain_all);
+void blk_queue_bypass_start(struct request_queue *q);
+void blk_queue_bypass_end(struct request_queue *q);
 void blk_dequeue_request(struct request *rq);
 void __blk_queue_free_tags(struct request_queue *q);
 bool __blk_end_bidi_request(struct request *rq, int error,
@@ -144,9 +145,6 @@ void blk_queue_congestion_threshold(struct request_queue *q);
 
 int blk_dev_init(void);
 
-void elv_quiesce_start(struct request_queue *q);
-void elv_quiesce_end(struct request_queue *q);
-
 
 /*
  * Return the threshold (number of used requests) at which the queue is
@@ -186,32 +184,30 @@ static inline int blk_do_io_stat(struct request *rq)
  */
 void get_io_context(struct io_context *ioc);
 struct io_cq *ioc_lookup_icq(struct io_context *ioc, struct request_queue *q);
-struct io_cq *ioc_create_icq(struct request_queue *q, gfp_t gfp_mask);
+struct io_cq *ioc_create_icq(struct io_context *ioc, struct request_queue *q,
+                            gfp_t gfp_mask);
 void ioc_clear_queue(struct request_queue *q);
 
-void create_io_context_slowpath(struct task_struct *task, gfp_t gfp_mask,
-                               int node);
+int create_task_io_context(struct task_struct *task, gfp_t gfp_mask, int node);
 
 /**
  * create_io_context - try to create task->io_context
- * @task: target task
  * @gfp_mask: allocation mask
  * @node: allocation node
  *
- * If @task->io_context is %NULL, allocate a new io_context and install it.
- * Returns the current @task->io_context which may be %NULL if allocation
- * failed.
+ * If %current->io_context is %NULL, allocate a new io_context and install
+ * it.  Returns the current %current->io_context which may be %NULL if
+ * allocation failed.
  *
  * Note that this function can't be called with IRQ disabled because
- * task_lock which protects @task->io_context is IRQ-unsafe.
+ * task_lock which protects %current->io_context is IRQ-unsafe.
  */
-static inline struct io_context *create_io_context(struct task_struct *task,
-                                                  gfp_t gfp_mask, int node)
+static inline struct io_context *create_io_context(gfp_t gfp_mask, int node)
 {
        WARN_ON_ONCE(irqs_disabled());
-       if (unlikely(!task->io_context))
-               create_io_context_slowpath(task, gfp_mask, node);
-       return task->io_context;
+       if (unlikely(!current->io_context))
+               create_task_io_context(current, gfp_mask, node);
+       return current->io_context;
 }
 
 /*
@@ -222,7 +218,6 @@ extern bool blk_throtl_bio(struct request_queue *q, struct bio *bio);
 extern void blk_throtl_drain(struct request_queue *q);
 extern int blk_throtl_init(struct request_queue *q);
 extern void blk_throtl_exit(struct request_queue *q);
-extern void blk_throtl_release(struct request_queue *q);
 #else /* CONFIG_BLK_DEV_THROTTLING */
 static inline bool blk_throtl_bio(struct request_queue *q, struct bio *bio)
 {
@@ -231,7 +226,6 @@ static inline bool blk_throtl_bio(struct request_queue *q, struct bio *bio)
 static inline void blk_throtl_drain(struct request_queue *q) { }
 static inline int blk_throtl_init(struct request_queue *q) { return 0; }
 static inline void blk_throtl_exit(struct request_queue *q) { }
-static inline void blk_throtl_release(struct request_queue *q) { }
 #endif /* CONFIG_BLK_DEV_THROTTLING */
 
 #endif /* BLK_INTERNAL_H */
index 3c38536bd52c3e3a25f1b8152e40a6ebe5192d36..673c977cc2bfa238e0fe0efa6dddac5193fbccd8 100644 (file)
@@ -15,7 +15,9 @@
 #include <linux/ioprio.h>
 #include <linux/blktrace_api.h>
 #include "blk.h"
-#include "cfq.h"
+#include "blk-cgroup.h"
+
+static struct blkcg_policy blkcg_policy_cfq __maybe_unused;
 
 /*
  * tunables
@@ -171,8 +173,53 @@ enum wl_type_t {
        SYNC_WORKLOAD = 2
 };
 
+struct cfqg_stats {
+#ifdef CONFIG_CFQ_GROUP_IOSCHED
+       /* total bytes transferred */
+       struct blkg_rwstat              service_bytes;
+       /* total IOs serviced, post merge */
+       struct blkg_rwstat              serviced;
+       /* number of ios merged */
+       struct blkg_rwstat              merged;
+       /* total time spent on device in ns, may not be accurate w/ queueing */
+       struct blkg_rwstat              service_time;
+       /* total time spent waiting in scheduler queue in ns */
+       struct blkg_rwstat              wait_time;
+       /* number of IOs queued up */
+       struct blkg_rwstat              queued;
+       /* total sectors transferred */
+       struct blkg_stat                sectors;
+       /* total disk time and nr sectors dispatched by this group */
+       struct blkg_stat                time;
+#ifdef CONFIG_DEBUG_BLK_CGROUP
+       /* time not charged to this cgroup */
+       struct blkg_stat                unaccounted_time;
+       /* sum of number of ios queued across all samples */
+       struct blkg_stat                avg_queue_size_sum;
+       /* count of samples taken for average */
+       struct blkg_stat                avg_queue_size_samples;
+       /* how many times this group has been removed from service tree */
+       struct blkg_stat                dequeue;
+       /* total time spent waiting for it to be assigned a timeslice. */
+       struct blkg_stat                group_wait_time;
+       /* time spent idling for this blkcg_gq */
+       struct blkg_stat                idle_time;
+       /* total time with empty current active q with other requests queued */
+       struct blkg_stat                empty_time;
+       /* fields after this shouldn't be cleared on stat reset */
+       uint64_t                        start_group_wait_time;
+       uint64_t                        start_idle_time;
+       uint64_t                        start_empty_time;
+       uint16_t                        flags;
+#endif /* CONFIG_DEBUG_BLK_CGROUP */
+#endif /* CONFIG_CFQ_GROUP_IOSCHED */
+};
+
 /* This is per cgroup per device grouping structure */
 struct cfq_group {
+       /* must be the first member */
+       struct blkg_policy_data pd;
+
        /* group service_tree member */
        struct rb_node rb_node;
 
@@ -180,7 +227,7 @@ struct cfq_group {
        u64 vdisktime;
        unsigned int weight;
        unsigned int new_weight;
-       bool needs_update;
+       unsigned int dev_weight;
 
        /* number of cfqq currently on this group */
        int nr_cfqq;
@@ -206,20 +253,21 @@ struct cfq_group {
        unsigned long saved_workload_slice;
        enum wl_type_t saved_workload;
        enum wl_prio_t saved_serving_prio;
-       struct blkio_group blkg;
-#ifdef CONFIG_CFQ_GROUP_IOSCHED
-       struct hlist_node cfqd_node;
-       int ref;
-#endif
+
        /* number of requests that are on the dispatch list or inside driver */
        int dispatched;
        struct cfq_ttime ttime;
+       struct cfqg_stats stats;
 };
 
 struct cfq_io_cq {
        struct io_cq            icq;            /* must be the first member */
        struct cfq_queue        *cfqq[2];
        struct cfq_ttime        ttime;
+       int                     ioprio;         /* the current ioprio */
+#ifdef CONFIG_CFQ_GROUP_IOSCHED
+       uint64_t                blkcg_id;       /* the current blkcg ID */
+#endif
 };
 
 /*
@@ -229,7 +277,7 @@ struct cfq_data {
        struct request_queue *queue;
        /* Root service tree for cfq_groups */
        struct cfq_rb_root grp_service_tree;
-       struct cfq_group root_group;
+       struct cfq_group *root_group;
 
        /*
         * The priority currently being served
@@ -303,12 +351,6 @@ struct cfq_data {
        struct cfq_queue oom_cfqq;
 
        unsigned long last_delayed_sync;
-
-       /* List of cfq groups being managed on this device*/
-       struct hlist_head cfqg_list;
-
-       /* Number of groups which are on blkcg->blkg_list */
-       unsigned int nr_blkcg_linked_grps;
 };
 
 static struct cfq_group *cfq_get_next_cfqg(struct cfq_data *cfqd);
@@ -371,21 +413,284 @@ CFQ_CFQQ_FNS(deep);
 CFQ_CFQQ_FNS(wait_busy);
 #undef CFQ_CFQQ_FNS
 
+static inline struct cfq_group *pd_to_cfqg(struct blkg_policy_data *pd)
+{
+       return pd ? container_of(pd, struct cfq_group, pd) : NULL;
+}
+
+static inline struct cfq_group *blkg_to_cfqg(struct blkcg_gq *blkg)
+{
+       return pd_to_cfqg(blkg_to_pd(blkg, &blkcg_policy_cfq));
+}
+
+static inline struct blkcg_gq *cfqg_to_blkg(struct cfq_group *cfqg)
+{
+       return pd_to_blkg(&cfqg->pd);
+}
+
+#if defined(CONFIG_CFQ_GROUP_IOSCHED) && defined(CONFIG_DEBUG_BLK_CGROUP)
+
+/* cfqg stats flags */
+enum cfqg_stats_flags {
+       CFQG_stats_waiting = 0,
+       CFQG_stats_idling,
+       CFQG_stats_empty,
+};
+
+#define CFQG_FLAG_FNS(name)                                            \
+static inline void cfqg_stats_mark_##name(struct cfqg_stats *stats)    \
+{                                                                      \
+       stats->flags |= (1 << CFQG_stats_##name);                       \
+}                                                                      \
+static inline void cfqg_stats_clear_##name(struct cfqg_stats *stats)   \
+{                                                                      \
+       stats->flags &= ~(1 << CFQG_stats_##name);                      \
+}                                                                      \
+static inline int cfqg_stats_##name(struct cfqg_stats *stats)          \
+{                                                                      \
+       return (stats->flags & (1 << CFQG_stats_##name)) != 0;          \
+}                                                                      \
+
+CFQG_FLAG_FNS(waiting)
+CFQG_FLAG_FNS(idling)
+CFQG_FLAG_FNS(empty)
+#undef CFQG_FLAG_FNS
+
+/* This should be called with the queue_lock held. */
+static void cfqg_stats_update_group_wait_time(struct cfqg_stats *stats)
+{
+       unsigned long long now;
+
+       if (!cfqg_stats_waiting(stats))
+               return;
+
+       now = sched_clock();
+       if (time_after64(now, stats->start_group_wait_time))
+               blkg_stat_add(&stats->group_wait_time,
+                             now - stats->start_group_wait_time);
+       cfqg_stats_clear_waiting(stats);
+}
+
+/* This should be called with the queue_lock held. */
+static void cfqg_stats_set_start_group_wait_time(struct cfq_group *cfqg,
+                                                struct cfq_group *curr_cfqg)
+{
+       struct cfqg_stats *stats = &cfqg->stats;
+
+       if (cfqg_stats_waiting(stats))
+               return;
+       if (cfqg == curr_cfqg)
+               return;
+       stats->start_group_wait_time = sched_clock();
+       cfqg_stats_mark_waiting(stats);
+}
+
+/* This should be called with the queue_lock held. */
+static void cfqg_stats_end_empty_time(struct cfqg_stats *stats)
+{
+       unsigned long long now;
+
+       if (!cfqg_stats_empty(stats))
+               return;
+
+       now = sched_clock();
+       if (time_after64(now, stats->start_empty_time))
+               blkg_stat_add(&stats->empty_time,
+                             now - stats->start_empty_time);
+       cfqg_stats_clear_empty(stats);
+}
+
+static void cfqg_stats_update_dequeue(struct cfq_group *cfqg)
+{
+       blkg_stat_add(&cfqg->stats.dequeue, 1);
+}
+
+static void cfqg_stats_set_start_empty_time(struct cfq_group *cfqg)
+{
+       struct cfqg_stats *stats = &cfqg->stats;
+
+       if (blkg_rwstat_sum(&stats->queued))
+               return;
+
+       /*
+        * group is already marked empty. This can happen if cfqq got new
+        * request in parent group and moved to this group while being added
+        * to service tree. Just ignore the event and move on.
+        */
+       if (cfqg_stats_empty(stats))
+               return;
+
+       stats->start_empty_time = sched_clock();
+       cfqg_stats_mark_empty(stats);
+}
+
+static void cfqg_stats_update_idle_time(struct cfq_group *cfqg)
+{
+       struct cfqg_stats *stats = &cfqg->stats;
+
+       if (cfqg_stats_idling(stats)) {
+               unsigned long long now = sched_clock();
+
+               if (time_after64(now, stats->start_idle_time))
+                       blkg_stat_add(&stats->idle_time,
+                                     now - stats->start_idle_time);
+               cfqg_stats_clear_idling(stats);
+       }
+}
+
+static void cfqg_stats_set_start_idle_time(struct cfq_group *cfqg)
+{
+       struct cfqg_stats *stats = &cfqg->stats;
+
+       BUG_ON(cfqg_stats_idling(stats));
+
+       stats->start_idle_time = sched_clock();
+       cfqg_stats_mark_idling(stats);
+}
+
+static void cfqg_stats_update_avg_queue_size(struct cfq_group *cfqg)
+{
+       struct cfqg_stats *stats = &cfqg->stats;
+
+       blkg_stat_add(&stats->avg_queue_size_sum,
+                     blkg_rwstat_sum(&stats->queued));
+       blkg_stat_add(&stats->avg_queue_size_samples, 1);
+       cfqg_stats_update_group_wait_time(stats);
+}
+
+#else  /* CONFIG_CFQ_GROUP_IOSCHED && CONFIG_DEBUG_BLK_CGROUP */
+
+static inline void cfqg_stats_set_start_group_wait_time(struct cfq_group *cfqg, struct cfq_group *curr_cfqg) { }
+static inline void cfqg_stats_end_empty_time(struct cfqg_stats *stats) { }
+static inline void cfqg_stats_update_dequeue(struct cfq_group *cfqg) { }
+static inline void cfqg_stats_set_start_empty_time(struct cfq_group *cfqg) { }
+static inline void cfqg_stats_update_idle_time(struct cfq_group *cfqg) { }
+static inline void cfqg_stats_set_start_idle_time(struct cfq_group *cfqg) { }
+static inline void cfqg_stats_update_avg_queue_size(struct cfq_group *cfqg) { }
+
+#endif /* CONFIG_CFQ_GROUP_IOSCHED && CONFIG_DEBUG_BLK_CGROUP */
+
 #ifdef CONFIG_CFQ_GROUP_IOSCHED
-#define cfq_log_cfqq(cfqd, cfqq, fmt, args...) \
+
+static inline void cfqg_get(struct cfq_group *cfqg)
+{
+       return blkg_get(cfqg_to_blkg(cfqg));
+}
+
+static inline void cfqg_put(struct cfq_group *cfqg)
+{
+       return blkg_put(cfqg_to_blkg(cfqg));
+}
+
+#define cfq_log_cfqq(cfqd, cfqq, fmt, args...) do {                    \
+       char __pbuf[128];                                               \
+                                                                       \
+       blkg_path(cfqg_to_blkg((cfqq)->cfqg), __pbuf, sizeof(__pbuf));  \
        blk_add_trace_msg((cfqd)->queue, "cfq%d%c %s " fmt, (cfqq)->pid, \
-                       cfq_cfqq_sync((cfqq)) ? 'S' : 'A', \
-                       blkg_path(&(cfqq)->cfqg->blkg), ##args)
+                         cfq_cfqq_sync((cfqq)) ? 'S' : 'A',            \
+                         __pbuf, ##args);                              \
+} while (0)
 
-#define cfq_log_cfqg(cfqd, cfqg, fmt, args...)                         \
-       blk_add_trace_msg((cfqd)->queue, "%s " fmt,                     \
-                               blkg_path(&(cfqg)->blkg), ##args)       \
+#define cfq_log_cfqg(cfqd, cfqg, fmt, args...) do {                    \
+       char __pbuf[128];                                               \
+                                                                       \
+       blkg_path(cfqg_to_blkg(cfqg), __pbuf, sizeof(__pbuf));          \
+       blk_add_trace_msg((cfqd)->queue, "%s " fmt, __pbuf, ##args);    \
+} while (0)
+
+static inline void cfqg_stats_update_io_add(struct cfq_group *cfqg,
+                                           struct cfq_group *curr_cfqg, int rw)
+{
+       blkg_rwstat_add(&cfqg->stats.queued, rw, 1);
+       cfqg_stats_end_empty_time(&cfqg->stats);
+       cfqg_stats_set_start_group_wait_time(cfqg, curr_cfqg);
+}
+
+static inline void cfqg_stats_update_timeslice_used(struct cfq_group *cfqg,
+                       unsigned long time, unsigned long unaccounted_time)
+{
+       blkg_stat_add(&cfqg->stats.time, time);
+#ifdef CONFIG_DEBUG_BLK_CGROUP
+       blkg_stat_add(&cfqg->stats.unaccounted_time, unaccounted_time);
+#endif
+}
+
+static inline void cfqg_stats_update_io_remove(struct cfq_group *cfqg, int rw)
+{
+       blkg_rwstat_add(&cfqg->stats.queued, rw, -1);
+}
+
+static inline void cfqg_stats_update_io_merged(struct cfq_group *cfqg, int rw)
+{
+       blkg_rwstat_add(&cfqg->stats.merged, rw, 1);
+}
+
+static inline void cfqg_stats_update_dispatch(struct cfq_group *cfqg,
+                                             uint64_t bytes, int rw)
+{
+       blkg_stat_add(&cfqg->stats.sectors, bytes >> 9);
+       blkg_rwstat_add(&cfqg->stats.serviced, rw, 1);
+       blkg_rwstat_add(&cfqg->stats.service_bytes, rw, bytes);
+}
+
+static inline void cfqg_stats_update_completion(struct cfq_group *cfqg,
+                       uint64_t start_time, uint64_t io_start_time, int rw)
+{
+       struct cfqg_stats *stats = &cfqg->stats;
+       unsigned long long now = sched_clock();
+
+       if (time_after64(now, io_start_time))
+               blkg_rwstat_add(&stats->service_time, rw, now - io_start_time);
+       if (time_after64(io_start_time, start_time))
+               blkg_rwstat_add(&stats->wait_time, rw,
+                               io_start_time - start_time);
+}
+
+static void cfq_pd_reset_stats(struct blkcg_gq *blkg)
+{
+       struct cfq_group *cfqg = blkg_to_cfqg(blkg);
+       struct cfqg_stats *stats = &cfqg->stats;
+
+       /* queued stats shouldn't be cleared */
+       blkg_rwstat_reset(&stats->service_bytes);
+       blkg_rwstat_reset(&stats->serviced);
+       blkg_rwstat_reset(&stats->merged);
+       blkg_rwstat_reset(&stats->service_time);
+       blkg_rwstat_reset(&stats->wait_time);
+       blkg_stat_reset(&stats->time);
+#ifdef CONFIG_DEBUG_BLK_CGROUP
+       blkg_stat_reset(&stats->unaccounted_time);
+       blkg_stat_reset(&stats->avg_queue_size_sum);
+       blkg_stat_reset(&stats->avg_queue_size_samples);
+       blkg_stat_reset(&stats->dequeue);
+       blkg_stat_reset(&stats->group_wait_time);
+       blkg_stat_reset(&stats->idle_time);
+       blkg_stat_reset(&stats->empty_time);
+#endif
+}
+
+#else  /* CONFIG_CFQ_GROUP_IOSCHED */
+
+static inline void cfqg_get(struct cfq_group *cfqg) { }
+static inline void cfqg_put(struct cfq_group *cfqg) { }
 
-#else
 #define cfq_log_cfqq(cfqd, cfqq, fmt, args...) \
        blk_add_trace_msg((cfqd)->queue, "cfq%d " fmt, (cfqq)->pid, ##args)
 #define cfq_log_cfqg(cfqd, cfqg, fmt, args...)         do {} while (0)
-#endif
+
+static inline void cfqg_stats_update_io_add(struct cfq_group *cfqg,
+                       struct cfq_group *curr_cfqg, int rw) { }
+static inline void cfqg_stats_update_timeslice_used(struct cfq_group *cfqg,
+                       unsigned long time, unsigned long unaccounted_time) { }
+static inline void cfqg_stats_update_io_remove(struct cfq_group *cfqg, int rw) { }
+static inline void cfqg_stats_update_io_merged(struct cfq_group *cfqg, int rw) { }
+static inline void cfqg_stats_update_dispatch(struct cfq_group *cfqg,
+                                             uint64_t bytes, int rw) { }
+static inline void cfqg_stats_update_completion(struct cfq_group *cfqg,
+                       uint64_t start_time, uint64_t io_start_time, int rw) { }
+
+#endif /* CONFIG_CFQ_GROUP_IOSCHED */
+
 #define cfq_log(cfqd, fmt, args...)    \
        blk_add_trace_msg((cfqd)->queue, "cfq " fmt, ##args)
 
@@ -466,8 +771,9 @@ static inline int cfqg_busy_async_queues(struct cfq_data *cfqd,
 }
 
 static void cfq_dispatch_insert(struct request_queue *, struct request *);
-static struct cfq_queue *cfq_get_queue(struct cfq_data *, bool,
-                                      struct io_context *, gfp_t);
+static struct cfq_queue *cfq_get_queue(struct cfq_data *cfqd, bool is_sync,
+                                      struct cfq_io_cq *cic, struct bio *bio,
+                                      gfp_t gfp_mask);
 
 static inline struct cfq_io_cq *icq_to_cic(struct io_cq *icq)
 {
@@ -545,7 +851,7 @@ static inline u64 cfq_scale_slice(unsigned long delta, struct cfq_group *cfqg)
 {
        u64 d = delta << CFQ_SERVICE_SHIFT;
 
-       d = d * BLKIO_WEIGHT_DEFAULT;
+       d = d * CFQ_WEIGHT_DEFAULT;
        do_div(d, cfqg->weight);
        return d;
 }
@@ -872,9 +1178,9 @@ static void
 cfq_update_group_weight(struct cfq_group *cfqg)
 {
        BUG_ON(!RB_EMPTY_NODE(&cfqg->rb_node));
-       if (cfqg->needs_update) {
+       if (cfqg->new_weight) {
                cfqg->weight = cfqg->new_weight;
-               cfqg->needs_update = false;
+               cfqg->new_weight = 0;
        }
 }
 
@@ -936,7 +1242,7 @@ cfq_group_notify_queue_del(struct cfq_data *cfqd, struct cfq_group *cfqg)
        cfq_log_cfqg(cfqd, cfqg, "del_from_rr group");
        cfq_group_service_tree_del(st, cfqg);
        cfqg->saved_workload_slice = 0;
-       cfq_blkiocg_update_dequeue_stats(&cfqg->blkg, 1);
+       cfqg_stats_update_dequeue(cfqg);
 }
 
 static inline unsigned int cfq_cfqq_slice_usage(struct cfq_queue *cfqq,
@@ -1008,178 +1314,59 @@ static void cfq_group_served(struct cfq_data *cfqd, struct cfq_group *cfqg,
                     "sl_used=%u disp=%u charge=%u iops=%u sect=%lu",
                     used_sl, cfqq->slice_dispatch, charge,
                     iops_mode(cfqd), cfqq->nr_sectors);
-       cfq_blkiocg_update_timeslice_used(&cfqg->blkg, used_sl,
-                                         unaccounted_sl);
-       cfq_blkiocg_set_start_empty_time(&cfqg->blkg);
+       cfqg_stats_update_timeslice_used(cfqg, used_sl, unaccounted_sl);
+       cfqg_stats_set_start_empty_time(cfqg);
 }
 
-#ifdef CONFIG_CFQ_GROUP_IOSCHED
-static inline struct cfq_group *cfqg_of_blkg(struct blkio_group *blkg)
-{
-       if (blkg)
-               return container_of(blkg, struct cfq_group, blkg);
-       return NULL;
-}
-
-static void cfq_update_blkio_group_weight(void *key, struct blkio_group *blkg,
-                                         unsigned int weight)
-{
-       struct cfq_group *cfqg = cfqg_of_blkg(blkg);
-       cfqg->new_weight = weight;
-       cfqg->needs_update = true;
-}
-
-static void cfq_init_add_cfqg_lists(struct cfq_data *cfqd,
-                       struct cfq_group *cfqg, struct blkio_cgroup *blkcg)
-{
-       struct backing_dev_info *bdi = &cfqd->queue->backing_dev_info;
-       unsigned int major, minor;
-
-       /*
-        * Add group onto cgroup list. It might happen that bdi->dev is
-        * not initialized yet. Initialize this new group without major
-        * and minor info and this info will be filled in once a new thread
-        * comes for IO.
-        */
-       if (bdi->dev) {
-               sscanf(dev_name(bdi->dev), "%u:%u", &major, &minor);
-               cfq_blkiocg_add_blkio_group(blkcg, &cfqg->blkg,
-                                       (void *)cfqd, MKDEV(major, minor));
-       } else
-               cfq_blkiocg_add_blkio_group(blkcg, &cfqg->blkg,
-                                       (void *)cfqd, 0);
-
-       cfqd->nr_blkcg_linked_grps++;
-       cfqg->weight = blkcg_get_weight(blkcg, cfqg->blkg.dev);
-
-       /* Add group on cfqd list */
-       hlist_add_head(&cfqg->cfqd_node, &cfqd->cfqg_list);
-}
-
-/*
- * Should be called from sleepable context. No request queue lock as per
- * cpu stats are allocated dynamically and alloc_percpu needs to be called
- * from sleepable context.
+/**
+ * cfq_init_cfqg_base - initialize base part of a cfq_group
+ * @cfqg: cfq_group to initialize
+ *
+ * Initialize the base part which is used whether %CONFIG_CFQ_GROUP_IOSCHED
+ * is enabled or not.
  */
-static struct cfq_group * cfq_alloc_cfqg(struct cfq_data *cfqd)
+static void cfq_init_cfqg_base(struct cfq_group *cfqg)
 {
-       struct cfq_group *cfqg = NULL;
-       int i, j, ret;
        struct cfq_rb_root *st;
-
-       cfqg = kzalloc_node(sizeof(*cfqg), GFP_ATOMIC, cfqd->queue->node);
-       if (!cfqg)
-               return NULL;
+       int i, j;
 
        for_each_cfqg_st(cfqg, i, j, st)
                *st = CFQ_RB_ROOT;
        RB_CLEAR_NODE(&cfqg->rb_node);
 
        cfqg->ttime.last_end_request = jiffies;
-
-       /*
-        * Take the initial reference that will be released on destroy
-        * This can be thought of a joint reference by cgroup and
-        * elevator which will be dropped by either elevator exit
-        * or cgroup deletion path depending on who is exiting first.
-        */
-       cfqg->ref = 1;
-
-       ret = blkio_alloc_blkg_stats(&cfqg->blkg);
-       if (ret) {
-               kfree(cfqg);
-               return NULL;
-       }
-
-       return cfqg;
 }
 
-static struct cfq_group *
-cfq_find_cfqg(struct cfq_data *cfqd, struct blkio_cgroup *blkcg)
+#ifdef CONFIG_CFQ_GROUP_IOSCHED
+static void cfq_pd_init(struct blkcg_gq *blkg)
 {
-       struct cfq_group *cfqg = NULL;
-       void *key = cfqd;
-       struct backing_dev_info *bdi = &cfqd->queue->backing_dev_info;
-       unsigned int major, minor;
-
-       /*
-        * This is the common case when there are no blkio cgroups.
-        * Avoid lookup in this case
-        */
-       if (blkcg == &blkio_root_cgroup)
-               cfqg = &cfqd->root_group;
-       else
-               cfqg = cfqg_of_blkg(blkiocg_lookup_group(blkcg, key));
-
-       if (cfqg && !cfqg->blkg.dev && bdi->dev && dev_name(bdi->dev)) {
-               sscanf(dev_name(bdi->dev), "%u:%u", &major, &minor);
-               cfqg->blkg.dev = MKDEV(major, minor);
-       }
+       struct cfq_group *cfqg = blkg_to_cfqg(blkg);
 
-       return cfqg;
+       cfq_init_cfqg_base(cfqg);
+       cfqg->weight = blkg->blkcg->cfq_weight;
 }
 
 /*
  * Search for the cfq group current task belongs to. request_queue lock must
  * be held.
  */
-static struct cfq_group *cfq_get_cfqg(struct cfq_data *cfqd)
+static struct cfq_group *cfq_lookup_create_cfqg(struct cfq_data *cfqd,
+                                               struct blkcg *blkcg)
 {
-       struct blkio_cgroup *blkcg;
-       struct cfq_group *cfqg = NULL, *__cfqg = NULL;
        struct request_queue *q = cfqd->queue;
+       struct cfq_group *cfqg = NULL;
 
-       rcu_read_lock();
-       blkcg = task_blkio_cgroup(current);
-       cfqg = cfq_find_cfqg(cfqd, blkcg);
-       if (cfqg) {
-               rcu_read_unlock();
-               return cfqg;
-       }
-
-       /*
-        * Need to allocate a group. Allocation of group also needs allocation
-        * of per cpu stats which in-turn takes a mutex() and can block. Hence
-        * we need to drop rcu lock and queue_lock before we call alloc.
-        *
-        * Not taking any queue reference here and assuming that queue is
-        * around by the time we return. CFQ queue allocation code does
-        * the same. It might be racy though.
-        */
-
-       rcu_read_unlock();
-       spin_unlock_irq(q->queue_lock);
-
-       cfqg = cfq_alloc_cfqg(cfqd);
-
-       spin_lock_irq(q->queue_lock);
-
-       rcu_read_lock();
-       blkcg = task_blkio_cgroup(current);
-
-       /*
-        * If some other thread already allocated the group while we were
-        * not holding queue lock, free up the group
-        */
-       __cfqg = cfq_find_cfqg(cfqd, blkcg);
+       /* avoid lookup for the common case where there's no blkcg */
+       if (blkcg == &blkcg_root) {
+               cfqg = cfqd->root_group;
+       } else {
+               struct blkcg_gq *blkg;
 
-       if (__cfqg) {
-               kfree(cfqg);
-               rcu_read_unlock();
-               return __cfqg;
+               blkg = blkg_lookup_create(blkcg, q);
+               if (!IS_ERR(blkg))
+                       cfqg = blkg_to_cfqg(blkg);
        }
 
-       if (!cfqg)
-               cfqg = &cfqd->root_group;
-
-       cfq_init_add_cfqg_lists(cfqd, cfqg, blkcg);
-       rcu_read_unlock();
-       return cfqg;
-}
-
-static inline struct cfq_group *cfq_ref_get_cfqg(struct cfq_group *cfqg)
-{
-       cfqg->ref++;
        return cfqg;
 }
 
@@ -1187,94 +1374,224 @@ static void cfq_link_cfqq_cfqg(struct cfq_queue *cfqq, struct cfq_group *cfqg)
 {
        /* Currently, all async queues are mapped to root group */
        if (!cfq_cfqq_sync(cfqq))
-               cfqg = &cfqq->cfqd->root_group;
+               cfqg = cfqq->cfqd->root_group;
 
        cfqq->cfqg = cfqg;
        /* cfqq reference on cfqg */
-       cfqq->cfqg->ref++;
+       cfqg_get(cfqg);
 }
 
-static void cfq_put_cfqg(struct cfq_group *cfqg)
+static u64 cfqg_prfill_weight_device(struct seq_file *sf,
+                                    struct blkg_policy_data *pd, int off)
 {
-       struct cfq_rb_root *st;
-       int i, j;
+       struct cfq_group *cfqg = pd_to_cfqg(pd);
 
-       BUG_ON(cfqg->ref <= 0);
-       cfqg->ref--;
-       if (cfqg->ref)
-               return;
-       for_each_cfqg_st(cfqg, i, j, st)
-               BUG_ON(!RB_EMPTY_ROOT(&st->rb));
-       free_percpu(cfqg->blkg.stats_cpu);
-       kfree(cfqg);
+       if (!cfqg->dev_weight)
+               return 0;
+       return __blkg_prfill_u64(sf, pd, cfqg->dev_weight);
 }
 
-static void cfq_destroy_cfqg(struct cfq_data *cfqd, struct cfq_group *cfqg)
+static int cfqg_print_weight_device(struct cgroup *cgrp, struct cftype *cft,
+                                   struct seq_file *sf)
 {
-       /* Something wrong if we are trying to remove same group twice */
-       BUG_ON(hlist_unhashed(&cfqg->cfqd_node));
+       blkcg_print_blkgs(sf, cgroup_to_blkcg(cgrp),
+                         cfqg_prfill_weight_device, &blkcg_policy_cfq, 0,
+                         false);
+       return 0;
+}
 
-       hlist_del_init(&cfqg->cfqd_node);
+static int cfq_print_weight(struct cgroup *cgrp, struct cftype *cft,
+                           struct seq_file *sf)
+{
+       seq_printf(sf, "%u\n", cgroup_to_blkcg(cgrp)->cfq_weight);
+       return 0;
+}
 
-       BUG_ON(cfqd->nr_blkcg_linked_grps <= 0);
-       cfqd->nr_blkcg_linked_grps--;
+static int cfqg_set_weight_device(struct cgroup *cgrp, struct cftype *cft,
+                                 const char *buf)
+{
+       struct blkcg *blkcg = cgroup_to_blkcg(cgrp);
+       struct blkg_conf_ctx ctx;
+       struct cfq_group *cfqg;
+       int ret;
 
-       /*
-        * Put the reference taken at the time of creation so that when all
-        * queues are gone, group can be destroyed.
-        */
-       cfq_put_cfqg(cfqg);
+       ret = blkg_conf_prep(blkcg, &blkcg_policy_cfq, buf, &ctx);
+       if (ret)
+               return ret;
+
+       ret = -EINVAL;
+       cfqg = blkg_to_cfqg(ctx.blkg);
+       if (!ctx.v || (ctx.v >= CFQ_WEIGHT_MIN && ctx.v <= CFQ_WEIGHT_MAX)) {
+               cfqg->dev_weight = ctx.v;
+               cfqg->new_weight = cfqg->dev_weight ?: blkcg->cfq_weight;
+               ret = 0;
+       }
+
+       blkg_conf_finish(&ctx);
+       return ret;
 }
 
-static void cfq_release_cfq_groups(struct cfq_data *cfqd)
+static int cfq_set_weight(struct cgroup *cgrp, struct cftype *cft, u64 val)
 {
-       struct hlist_node *pos, *n;
-       struct cfq_group *cfqg;
+       struct blkcg *blkcg = cgroup_to_blkcg(cgrp);
+       struct blkcg_gq *blkg;
+       struct hlist_node *n;
 
-       hlist_for_each_entry_safe(cfqg, pos, n, &cfqd->cfqg_list, cfqd_node) {
-               /*
-                * If cgroup removal path got to blk_group first and removed
-                * it from cgroup list, then it will take care of destroying
-                * cfqg also.
-                */
-               if (!cfq_blkiocg_del_blkio_group(&cfqg->blkg))
-                       cfq_destroy_cfqg(cfqd, cfqg);
+       if (val < CFQ_WEIGHT_MIN || val > CFQ_WEIGHT_MAX)
+               return -EINVAL;
+
+       spin_lock_irq(&blkcg->lock);
+       blkcg->cfq_weight = (unsigned int)val;
+
+       hlist_for_each_entry(blkg, n, &blkcg->blkg_list, blkcg_node) {
+               struct cfq_group *cfqg = blkg_to_cfqg(blkg);
+
+               if (cfqg && !cfqg->dev_weight)
+                       cfqg->new_weight = blkcg->cfq_weight;
        }
+
+       spin_unlock_irq(&blkcg->lock);
+       return 0;
 }
 
-/*
- * Blk cgroup controller notification saying that blkio_group object is being
- * delinked as associated cgroup object is going away. That also means that
- * no new IO will come in this group. So get rid of this group as soon as
- * any pending IO in the group is finished.
- *
- * This function is called under rcu_read_lock(). key is the rcu protected
- * pointer. That means "key" is a valid cfq_data pointer as long as we are rcu
- * read lock.
- *
- * "key" was fetched from blkio_group under blkio_cgroup->lock. That means
- * it should not be NULL as even if elevator was exiting, cgroup deltion
- * path got to it first.
- */
-static void cfq_unlink_blkio_group(void *key, struct blkio_group *blkg)
+static int cfqg_print_stat(struct cgroup *cgrp, struct cftype *cft,
+                          struct seq_file *sf)
 {
-       unsigned long  flags;
-       struct cfq_data *cfqd = key;
+       struct blkcg *blkcg = cgroup_to_blkcg(cgrp);
 
-       spin_lock_irqsave(cfqd->queue->queue_lock, flags);
-       cfq_destroy_cfqg(cfqd, cfqg_of_blkg(blkg));
-       spin_unlock_irqrestore(cfqd->queue->queue_lock, flags);
+       blkcg_print_blkgs(sf, blkcg, blkg_prfill_stat, &blkcg_policy_cfq,
+                         cft->private, false);
+       return 0;
 }
 
-#else /* GROUP_IOSCHED */
-static struct cfq_group *cfq_get_cfqg(struct cfq_data *cfqd)
+static int cfqg_print_rwstat(struct cgroup *cgrp, struct cftype *cft,
+                            struct seq_file *sf)
 {
-       return &cfqd->root_group;
+       struct blkcg *blkcg = cgroup_to_blkcg(cgrp);
+
+       blkcg_print_blkgs(sf, blkcg, blkg_prfill_rwstat, &blkcg_policy_cfq,
+                         cft->private, true);
+       return 0;
 }
 
-static inline struct cfq_group *cfq_ref_get_cfqg(struct cfq_group *cfqg)
+#ifdef CONFIG_DEBUG_BLK_CGROUP
+static u64 cfqg_prfill_avg_queue_size(struct seq_file *sf,
+                                     struct blkg_policy_data *pd, int off)
 {
-       return cfqg;
+       struct cfq_group *cfqg = pd_to_cfqg(pd);
+       u64 samples = blkg_stat_read(&cfqg->stats.avg_queue_size_samples);
+       u64 v = 0;
+
+       if (samples) {
+               v = blkg_stat_read(&cfqg->stats.avg_queue_size_sum);
+               do_div(v, samples);
+       }
+       __blkg_prfill_u64(sf, pd, v);
+       return 0;
+}
+
+/* print avg_queue_size */
+static int cfqg_print_avg_queue_size(struct cgroup *cgrp, struct cftype *cft,
+                                    struct seq_file *sf)
+{
+       struct blkcg *blkcg = cgroup_to_blkcg(cgrp);
+
+       blkcg_print_blkgs(sf, blkcg, cfqg_prfill_avg_queue_size,
+                         &blkcg_policy_cfq, 0, false);
+       return 0;
+}
+#endif /* CONFIG_DEBUG_BLK_CGROUP */
+
+static struct cftype cfq_blkcg_files[] = {
+       {
+               .name = "weight_device",
+               .read_seq_string = cfqg_print_weight_device,
+               .write_string = cfqg_set_weight_device,
+               .max_write_len = 256,
+       },
+       {
+               .name = "weight",
+               .read_seq_string = cfq_print_weight,
+               .write_u64 = cfq_set_weight,
+       },
+       {
+               .name = "time",
+               .private = offsetof(struct cfq_group, stats.time),
+               .read_seq_string = cfqg_print_stat,
+       },
+       {
+               .name = "sectors",
+               .private = offsetof(struct cfq_group, stats.sectors),
+               .read_seq_string = cfqg_print_stat,
+       },
+       {
+               .name = "io_service_bytes",
+               .private = offsetof(struct cfq_group, stats.service_bytes),
+               .read_seq_string = cfqg_print_rwstat,
+       },
+       {
+               .name = "io_serviced",
+               .private = offsetof(struct cfq_group, stats.serviced),
+               .read_seq_string = cfqg_print_rwstat,
+       },
+       {
+               .name = "io_service_time",
+               .private = offsetof(struct cfq_group, stats.service_time),
+               .read_seq_string = cfqg_print_rwstat,
+       },
+       {
+               .name = "io_wait_time",
+               .private = offsetof(struct cfq_group, stats.wait_time),
+               .read_seq_string = cfqg_print_rwstat,
+       },
+       {
+               .name = "io_merged",
+               .private = offsetof(struct cfq_group, stats.merged),
+               .read_seq_string = cfqg_print_rwstat,
+       },
+       {
+               .name = "io_queued",
+               .private = offsetof(struct cfq_group, stats.queued),
+               .read_seq_string = cfqg_print_rwstat,
+       },
+#ifdef CONFIG_DEBUG_BLK_CGROUP
+       {
+               .name = "avg_queue_size",
+               .read_seq_string = cfqg_print_avg_queue_size,
+       },
+       {
+               .name = "group_wait_time",
+               .private = offsetof(struct cfq_group, stats.group_wait_time),
+               .read_seq_string = cfqg_print_stat,
+       },
+       {
+               .name = "idle_time",
+               .private = offsetof(struct cfq_group, stats.idle_time),
+               .read_seq_string = cfqg_print_stat,
+       },
+       {
+               .name = "empty_time",
+               .private = offsetof(struct cfq_group, stats.empty_time),
+               .read_seq_string = cfqg_print_stat,
+       },
+       {
+               .name = "dequeue",
+               .private = offsetof(struct cfq_group, stats.dequeue),
+               .read_seq_string = cfqg_print_stat,
+       },
+       {
+               .name = "unaccounted_time",
+               .private = offsetof(struct cfq_group, stats.unaccounted_time),
+               .read_seq_string = cfqg_print_stat,
+       },
+#endif /* CONFIG_DEBUG_BLK_CGROUP */
+       { }     /* terminate */
+};
+#else /* GROUP_IOSCHED */
+static struct cfq_group *cfq_lookup_create_cfqg(struct cfq_data *cfqd,
+                                               struct blkcg *blkcg)
+{
+       return cfqd->root_group;
 }
 
 static inline void
@@ -1282,9 +1599,6 @@ cfq_link_cfqq_cfqg(struct cfq_queue *cfqq, struct cfq_group *cfqg) {
        cfqq->cfqg = cfqg;
 }
 
-static void cfq_release_cfq_groups(struct cfq_data *cfqd) {}
-static inline void cfq_put_cfqg(struct cfq_group *cfqg) {}
-
 #endif /* GROUP_IOSCHED */
 
 /*
@@ -1551,12 +1865,10 @@ static void cfq_reposition_rq_rb(struct cfq_queue *cfqq, struct request *rq)
 {
        elv_rb_del(&cfqq->sort_list, rq);
        cfqq->queued[rq_is_sync(rq)]--;
-       cfq_blkiocg_update_io_remove_stats(&(RQ_CFQG(rq))->blkg,
-                                       rq_data_dir(rq), rq_is_sync(rq));
+       cfqg_stats_update_io_remove(RQ_CFQG(rq), rq->cmd_flags);
        cfq_add_rq_rb(rq);
-       cfq_blkiocg_update_io_add_stats(&(RQ_CFQG(rq))->blkg,
-                       &cfqq->cfqd->serving_group->blkg, rq_data_dir(rq),
-                       rq_is_sync(rq));
+       cfqg_stats_update_io_add(RQ_CFQG(rq), cfqq->cfqd->serving_group,
+                                rq->cmd_flags);
 }
 
 static struct request *
@@ -1612,8 +1924,7 @@ static void cfq_remove_request(struct request *rq)
        cfq_del_rq_rb(rq);
 
        cfqq->cfqd->rq_queued--;
-       cfq_blkiocg_update_io_remove_stats(&(RQ_CFQG(rq))->blkg,
-                                       rq_data_dir(rq), rq_is_sync(rq));
+       cfqg_stats_update_io_remove(RQ_CFQG(rq), rq->cmd_flags);
        if (rq->cmd_flags & REQ_PRIO) {
                WARN_ON(!cfqq->prio_pending);
                cfqq->prio_pending--;
@@ -1648,8 +1959,7 @@ static void cfq_merged_request(struct request_queue *q, struct request *req,
 static void cfq_bio_merged(struct request_queue *q, struct request *req,
                                struct bio *bio)
 {
-       cfq_blkiocg_update_io_merged_stats(&(RQ_CFQG(req))->blkg,
-                                       bio_data_dir(bio), cfq_bio_sync(bio));
+       cfqg_stats_update_io_merged(RQ_CFQG(req), bio->bi_rw);
 }
 
 static void
@@ -1671,8 +1981,7 @@ cfq_merged_requests(struct request_queue *q, struct request *rq,
        if (cfqq->next_rq == next)
                cfqq->next_rq = rq;
        cfq_remove_request(next);
-       cfq_blkiocg_update_io_merged_stats(&(RQ_CFQG(rq))->blkg,
-                                       rq_data_dir(next), rq_is_sync(next));
+       cfqg_stats_update_io_merged(RQ_CFQG(rq), next->cmd_flags);
 
        cfqq = RQ_CFQQ(next);
        /*
@@ -1713,7 +2022,7 @@ static int cfq_allow_merge(struct request_queue *q, struct request *rq,
 static inline void cfq_del_timer(struct cfq_data *cfqd, struct cfq_queue *cfqq)
 {
        del_timer(&cfqd->idle_slice_timer);
-       cfq_blkiocg_update_idle_time_stats(&cfqq->cfqg->blkg);
+       cfqg_stats_update_idle_time(cfqq->cfqg);
 }
 
 static void __cfq_set_active_queue(struct cfq_data *cfqd,
@@ -1722,7 +2031,7 @@ static void __cfq_set_active_queue(struct cfq_data *cfqd,
        if (cfqq) {
                cfq_log_cfqq(cfqd, cfqq, "set_active wl_prio:%d wl_type:%d",
                                cfqd->serving_prio, cfqd->serving_type);
-               cfq_blkiocg_update_avg_queue_size_stats(&cfqq->cfqg->blkg);
+               cfqg_stats_update_avg_queue_size(cfqq->cfqg);
                cfqq->slice_start = 0;
                cfqq->dispatch_start = jiffies;
                cfqq->allocated_slice = 0;
@@ -2043,7 +2352,7 @@ static void cfq_arm_slice_timer(struct cfq_data *cfqd)
         * task has exited, don't wait
         */
        cic = cfqd->active_cic;
-       if (!cic || !atomic_read(&cic->icq.ioc->nr_tasks))
+       if (!cic || !atomic_read(&cic->icq.ioc->active_ref))
                return;
 
        /*
@@ -2070,7 +2379,7 @@ static void cfq_arm_slice_timer(struct cfq_data *cfqd)
                sl = cfqd->cfq_slice_idle;
 
        mod_timer(&cfqd->idle_slice_timer, jiffies + sl);
-       cfq_blkiocg_update_set_idle_time_stats(&cfqq->cfqg->blkg);
+       cfqg_stats_set_start_idle_time(cfqq->cfqg);
        cfq_log_cfqq(cfqd, cfqq, "arm_idle: %lu group_idle: %d", sl,
                        group_idle ? 1 : 0);
 }
@@ -2093,8 +2402,7 @@ static void cfq_dispatch_insert(struct request_queue *q, struct request *rq)
 
        cfqd->rq_in_flight[cfq_cfqq_sync(cfqq)]++;
        cfqq->nr_sectors += blk_rq_sectors(rq);
-       cfq_blkiocg_update_dispatch_stats(&cfqq->cfqg->blkg, blk_rq_bytes(rq),
-                                       rq_data_dir(rq), rq_is_sync(rq));
+       cfqg_stats_update_dispatch(cfqq->cfqg, blk_rq_bytes(rq), rq->cmd_flags);
 }
 
 /*
@@ -2677,7 +2985,7 @@ static void cfq_put_queue(struct cfq_queue *cfqq)
 
        BUG_ON(cfq_cfqq_on_rr(cfqq));
        kmem_cache_free(cfq_pool, cfqq);
-       cfq_put_cfqg(cfqg);
+       cfqg_put(cfqg);
 }
 
 static void cfq_put_cooperator(struct cfq_queue *cfqq)
@@ -2736,7 +3044,7 @@ static void cfq_exit_icq(struct io_cq *icq)
        }
 }
 
-static void cfq_init_prio_data(struct cfq_queue *cfqq, struct io_context *ioc)
+static void cfq_init_prio_data(struct cfq_queue *cfqq, struct cfq_io_cq *cic)
 {
        struct task_struct *tsk = current;
        int ioprio_class;
@@ -2744,7 +3052,7 @@ static void cfq_init_prio_data(struct cfq_queue *cfqq, struct io_context *ioc)
        if (!cfq_cfqq_prio_changed(cfqq))
                return;
 
-       ioprio_class = IOPRIO_PRIO_CLASS(ioc->ioprio);
+       ioprio_class = IOPRIO_PRIO_CLASS(cic->ioprio);
        switch (ioprio_class) {
        default:
                printk(KERN_ERR "cfq: bad prio %x\n", ioprio_class);
@@ -2756,11 +3064,11 @@ static void cfq_init_prio_data(struct cfq_queue *cfqq, struct io_context *ioc)
                cfqq->ioprio_class = task_nice_ioclass(tsk);
                break;
        case IOPRIO_CLASS_RT:
-               cfqq->ioprio = task_ioprio(ioc);
+               cfqq->ioprio = IOPRIO_PRIO_DATA(cic->ioprio);
                cfqq->ioprio_class = IOPRIO_CLASS_RT;
                break;
        case IOPRIO_CLASS_BE:
-               cfqq->ioprio = task_ioprio(ioc);
+               cfqq->ioprio = IOPRIO_PRIO_DATA(cic->ioprio);
                cfqq->ioprio_class = IOPRIO_CLASS_BE;
                break;
        case IOPRIO_CLASS_IDLE:
@@ -2778,19 +3086,24 @@ static void cfq_init_prio_data(struct cfq_queue *cfqq, struct io_context *ioc)
        cfq_clear_cfqq_prio_changed(cfqq);
 }
 
-static void changed_ioprio(struct cfq_io_cq *cic)
+static void check_ioprio_changed(struct cfq_io_cq *cic, struct bio *bio)
 {
+       int ioprio = cic->icq.ioc->ioprio;
        struct cfq_data *cfqd = cic_to_cfqd(cic);
        struct cfq_queue *cfqq;
 
-       if (unlikely(!cfqd))
+       /*
+        * Check whether ioprio has changed.  The condition may trigger
+        * spuriously on a newly created cic but there's no harm.
+        */
+       if (unlikely(!cfqd) || likely(cic->ioprio == ioprio))
                return;
 
        cfqq = cic->cfqq[BLK_RW_ASYNC];
        if (cfqq) {
                struct cfq_queue *new_cfqq;
-               new_cfqq = cfq_get_queue(cfqd, BLK_RW_ASYNC, cic->icq.ioc,
-                                               GFP_ATOMIC);
+               new_cfqq = cfq_get_queue(cfqd, BLK_RW_ASYNC, cic, bio,
+                                        GFP_ATOMIC);
                if (new_cfqq) {
                        cic->cfqq[BLK_RW_ASYNC] = new_cfqq;
                        cfq_put_queue(cfqq);
@@ -2800,6 +3113,8 @@ static void changed_ioprio(struct cfq_io_cq *cic)
        cfqq = cic->cfqq[BLK_RW_SYNC];
        if (cfqq)
                cfq_mark_cfqq_prio_changed(cfqq);
+
+       cic->ioprio = ioprio;
 }
 
 static void cfq_init_cfqq(struct cfq_data *cfqd, struct cfq_queue *cfqq,
@@ -2823,17 +3138,24 @@ static void cfq_init_cfqq(struct cfq_data *cfqd, struct cfq_queue *cfqq,
 }
 
 #ifdef CONFIG_CFQ_GROUP_IOSCHED
-static void changed_cgroup(struct cfq_io_cq *cic)
+static void check_blkcg_changed(struct cfq_io_cq *cic, struct bio *bio)
 {
-       struct cfq_queue *sync_cfqq = cic_to_cfqq(cic, 1);
        struct cfq_data *cfqd = cic_to_cfqd(cic);
-       struct request_queue *q;
+       struct cfq_queue *sync_cfqq;
+       uint64_t id;
 
-       if (unlikely(!cfqd))
-               return;
+       rcu_read_lock();
+       id = bio_blkcg(bio)->id;
+       rcu_read_unlock();
 
-       q = cfqd->queue;
+       /*
+        * Check whether blkcg has changed.  The condition may trigger
+        * spuriously on a newly created cic but there's no harm.
+        */
+       if (unlikely(!cfqd) || likely(cic->blkcg_id == id))
+               return;
 
+       sync_cfqq = cic_to_cfqq(cic, 1);
        if (sync_cfqq) {
                /*
                 * Drop reference to sync queue. A new sync queue will be
@@ -2843,21 +3165,26 @@ static void changed_cgroup(struct cfq_io_cq *cic)
                cic_set_cfqq(cic, NULL, 1);
                cfq_put_queue(sync_cfqq);
        }
+
+       cic->blkcg_id = id;
 }
+#else
+static inline void check_blkcg_changed(struct cfq_io_cq *cic, struct bio *bio) { }
 #endif  /* CONFIG_CFQ_GROUP_IOSCHED */
 
 static struct cfq_queue *
-cfq_find_alloc_queue(struct cfq_data *cfqd, bool is_sync,
-                    struct io_context *ioc, gfp_t gfp_mask)
+cfq_find_alloc_queue(struct cfq_data *cfqd, bool is_sync, struct cfq_io_cq *cic,
+                    struct bio *bio, gfp_t gfp_mask)
 {
+       struct blkcg *blkcg;
        struct cfq_queue *cfqq, *new_cfqq = NULL;
-       struct cfq_io_cq *cic;
        struct cfq_group *cfqg;
 
 retry:
-       cfqg = cfq_get_cfqg(cfqd);
-       cic = cfq_cic_lookup(cfqd, ioc);
-       /* cic always exists here */
+       rcu_read_lock();
+
+       blkcg = bio_blkcg(bio);
+       cfqg = cfq_lookup_create_cfqg(cfqd, blkcg);
        cfqq = cic_to_cfqq(cic, is_sync);
 
        /*
@@ -2870,6 +3197,7 @@ retry:
                        cfqq = new_cfqq;
                        new_cfqq = NULL;
                } else if (gfp_mask & __GFP_WAIT) {
+                       rcu_read_unlock();
                        spin_unlock_irq(cfqd->queue->queue_lock);
                        new_cfqq = kmem_cache_alloc_node(cfq_pool,
                                        gfp_mask | __GFP_ZERO,
@@ -2885,7 +3213,7 @@ retry:
 
                if (cfqq) {
                        cfq_init_cfqq(cfqd, cfqq, current->pid, is_sync);
-                       cfq_init_prio_data(cfqq, ioc);
+                       cfq_init_prio_data(cfqq, cic);
                        cfq_link_cfqq_cfqg(cfqq, cfqg);
                        cfq_log_cfqq(cfqd, cfqq, "alloced");
                } else
@@ -2895,6 +3223,7 @@ retry:
        if (new_cfqq)
                kmem_cache_free(cfq_pool, new_cfqq);
 
+       rcu_read_unlock();
        return cfqq;
 }
 
@@ -2904,6 +3233,9 @@ cfq_async_queue_prio(struct cfq_data *cfqd, int ioprio_class, int ioprio)
        switch (ioprio_class) {
        case IOPRIO_CLASS_RT:
                return &cfqd->async_cfqq[0][ioprio];
+       case IOPRIO_CLASS_NONE:
+               ioprio = IOPRIO_NORM;
+               /* fall through */
        case IOPRIO_CLASS_BE:
                return &cfqd->async_cfqq[1][ioprio];
        case IOPRIO_CLASS_IDLE:
@@ -2914,11 +3246,11 @@ cfq_async_queue_prio(struct cfq_data *cfqd, int ioprio_class, int ioprio)
 }
 
 static struct cfq_queue *
-cfq_get_queue(struct cfq_data *cfqd, bool is_sync, struct io_context *ioc,
-             gfp_t gfp_mask)
+cfq_get_queue(struct cfq_data *cfqd, bool is_sync, struct cfq_io_cq *cic,
+             struct bio *bio, gfp_t gfp_mask)
 {
-       const int ioprio = task_ioprio(ioc);
-       const int ioprio_class = task_ioprio_class(ioc);
+       const int ioprio_class = IOPRIO_PRIO_CLASS(cic->ioprio);
+       const int ioprio = IOPRIO_PRIO_DATA(cic->ioprio);
        struct cfq_queue **async_cfqq = NULL;
        struct cfq_queue *cfqq = NULL;
 
@@ -2928,7 +3260,7 @@ cfq_get_queue(struct cfq_data *cfqd, bool is_sync, struct io_context *ioc,
        }
 
        if (!cfqq)
-               cfqq = cfq_find_alloc_queue(cfqd, is_sync, ioc, gfp_mask);
+               cfqq = cfq_find_alloc_queue(cfqd, is_sync, cic, bio, gfp_mask);
 
        /*
         * pin the queue now that it's allocated, scheduler exit will prune it
@@ -3010,7 +3342,7 @@ cfq_update_idle_window(struct cfq_data *cfqd, struct cfq_queue *cfqq,
 
        if (cfqq->next_rq && (cfqq->next_rq->cmd_flags & REQ_NOIDLE))
                enable_idle = 0;
-       else if (!atomic_read(&cic->icq.ioc->nr_tasks) ||
+       else if (!atomic_read(&cic->icq.ioc->active_ref) ||
                 !cfqd->cfq_slice_idle ||
                 (!cfq_cfqq_deep(cfqq) && CFQQ_SEEKY(cfqq)))
                enable_idle = 0;
@@ -3174,8 +3506,7 @@ cfq_rq_enqueued(struct cfq_data *cfqd, struct cfq_queue *cfqq,
                                cfq_clear_cfqq_wait_request(cfqq);
                                __blk_run_queue(cfqd->queue);
                        } else {
-                               cfq_blkiocg_update_idle_time_stats(
-                                               &cfqq->cfqg->blkg);
+                               cfqg_stats_update_idle_time(cfqq->cfqg);
                                cfq_mark_cfqq_must_dispatch(cfqq);
                        }
                }
@@ -3197,14 +3528,13 @@ static void cfq_insert_request(struct request_queue *q, struct request *rq)
        struct cfq_queue *cfqq = RQ_CFQQ(rq);
 
        cfq_log_cfqq(cfqd, cfqq, "insert_request");
-       cfq_init_prio_data(cfqq, RQ_CIC(rq)->icq.ioc);
+       cfq_init_prio_data(cfqq, RQ_CIC(rq));
 
        rq_set_fifo_time(rq, jiffies + cfqd->cfq_fifo_expire[rq_is_sync(rq)]);
        list_add_tail(&rq->queuelist, &cfqq->fifo);
        cfq_add_rq_rb(rq);
-       cfq_blkiocg_update_io_add_stats(&(RQ_CFQG(rq))->blkg,
-                       &cfqd->serving_group->blkg, rq_data_dir(rq),
-                       rq_is_sync(rq));
+       cfqg_stats_update_io_add(RQ_CFQG(rq), cfqd->serving_group,
+                                rq->cmd_flags);
        cfq_rq_enqueued(cfqd, cfqq, rq);
 }
 
@@ -3300,9 +3630,8 @@ static void cfq_completed_request(struct request_queue *q, struct request *rq)
        cfqd->rq_in_driver--;
        cfqq->dispatched--;
        (RQ_CFQG(rq))->dispatched--;
-       cfq_blkiocg_update_completion_stats(&cfqq->cfqg->blkg,
-                       rq_start_time_ns(rq), rq_io_start_time_ns(rq),
-                       rq_data_dir(rq), rq_is_sync(rq));
+       cfqg_stats_update_completion(cfqq->cfqg, rq_start_time_ns(rq),
+                                    rq_io_start_time_ns(rq), rq->cmd_flags);
 
        cfqd->rq_in_flight[cfq_cfqq_sync(cfqq)]--;
 
@@ -3399,7 +3728,7 @@ static int cfq_may_queue(struct request_queue *q, int rw)
 
        cfqq = cic_to_cfqq(cic, rw_is_sync(rw));
        if (cfqq) {
-               cfq_init_prio_data(cfqq, cic->icq.ioc);
+               cfq_init_prio_data(cfqq, cic);
 
                return __cfq_may_queue(cfqq);
        }
@@ -3421,7 +3750,7 @@ static void cfq_put_request(struct request *rq)
                cfqq->allocated[rw]--;
 
                /* Put down rq reference on cfqg */
-               cfq_put_cfqg(RQ_CFQG(rq));
+               cfqg_put(RQ_CFQG(rq));
                rq->elv.priv[0] = NULL;
                rq->elv.priv[1] = NULL;
 
@@ -3465,32 +3794,25 @@ split_cfqq(struct cfq_io_cq *cic, struct cfq_queue *cfqq)
  * Allocate cfq data structures associated with this request.
  */
 static int
-cfq_set_request(struct request_queue *q, struct request *rq, gfp_t gfp_mask)
+cfq_set_request(struct request_queue *q, struct request *rq, struct bio *bio,
+               gfp_t gfp_mask)
 {
        struct cfq_data *cfqd = q->elevator->elevator_data;
        struct cfq_io_cq *cic = icq_to_cic(rq->elv.icq);
        const int rw = rq_data_dir(rq);
        const bool is_sync = rq_is_sync(rq);
        struct cfq_queue *cfqq;
-       unsigned int changed;
 
        might_sleep_if(gfp_mask & __GFP_WAIT);
 
        spin_lock_irq(q->queue_lock);
 
-       /* handle changed notifications */
-       changed = icq_get_changed(&cic->icq);
-       if (unlikely(changed & ICQ_IOPRIO_CHANGED))
-               changed_ioprio(cic);
-#ifdef CONFIG_CFQ_GROUP_IOSCHED
-       if (unlikely(changed & ICQ_CGROUP_CHANGED))
-               changed_cgroup(cic);
-#endif
-
+       check_ioprio_changed(cic, bio);
+       check_blkcg_changed(cic, bio);
 new_queue:
        cfqq = cic_to_cfqq(cic, is_sync);
        if (!cfqq || cfqq == &cfqd->oom_cfqq) {
-               cfqq = cfq_get_queue(cfqd, is_sync, cic->icq.ioc, gfp_mask);
+               cfqq = cfq_get_queue(cfqd, is_sync, cic, bio, gfp_mask);
                cic_set_cfqq(cic, cfqq, is_sync);
        } else {
                /*
@@ -3516,8 +3838,9 @@ new_queue:
        cfqq->allocated[rw]++;
 
        cfqq->ref++;
+       cfqg_get(cfqq->cfqg);
        rq->elv.priv[0] = cfqq;
-       rq->elv.priv[1] = cfq_ref_get_cfqg(cfqq->cfqg);
+       rq->elv.priv[1] = cfqq->cfqg;
        spin_unlock_irq(q->queue_lock);
        return 0;
 }
@@ -3614,7 +3937,6 @@ static void cfq_exit_queue(struct elevator_queue *e)
 {
        struct cfq_data *cfqd = e->elevator_data;
        struct request_queue *q = cfqd->queue;
-       bool wait = false;
 
        cfq_shutdown_timer_wq(cfqd);
 
@@ -3624,89 +3946,52 @@ static void cfq_exit_queue(struct elevator_queue *e)
                __cfq_slice_expired(cfqd, cfqd->active_queue, 0);
 
        cfq_put_async_queues(cfqd);
-       cfq_release_cfq_groups(cfqd);
-
-       /*
-        * If there are groups which we could not unlink from blkcg list,
-        * wait for a rcu period for them to be freed.
-        */
-       if (cfqd->nr_blkcg_linked_grps)
-               wait = true;
 
        spin_unlock_irq(q->queue_lock);
 
        cfq_shutdown_timer_wq(cfqd);
 
-       /*
-        * Wait for cfqg->blkg->key accessors to exit their grace periods.
-        * Do this wait only if there are other unlinked groups out
-        * there. This can happen if cgroup deletion path claimed the
-        * responsibility of cleaning up a group before queue cleanup code
-        * get to the group.
-        *
-        * Do not call synchronize_rcu() unconditionally as there are drivers
-        * which create/delete request queue hundreds of times during scan/boot
-        * and synchronize_rcu() can take significant time and slow down boot.
-        */
-       if (wait)
-               synchronize_rcu();
-
-#ifdef CONFIG_CFQ_GROUP_IOSCHED
-       /* Free up per cpu stats for root group */
-       free_percpu(cfqd->root_group.blkg.stats_cpu);
+#ifndef CONFIG_CFQ_GROUP_IOSCHED
+       kfree(cfqd->root_group);
 #endif
+       blkcg_deactivate_policy(q, &blkcg_policy_cfq);
        kfree(cfqd);
 }
 
-static void *cfq_init_queue(struct request_queue *q)
+static int cfq_init_queue(struct request_queue *q)
 {
        struct cfq_data *cfqd;
-       int i, j;
-       struct cfq_group *cfqg;
-       struct cfq_rb_root *st;
+       struct blkcg_gq *blkg __maybe_unused;
+       int i, ret;
 
        cfqd = kmalloc_node(sizeof(*cfqd), GFP_KERNEL | __GFP_ZERO, q->node);
        if (!cfqd)
-               return NULL;
+               return -ENOMEM;
+
+       cfqd->queue = q;
+       q->elevator->elevator_data = cfqd;
 
        /* Init root service tree */
        cfqd->grp_service_tree = CFQ_RB_ROOT;
 
-       /* Init root group */
-       cfqg = &cfqd->root_group;
-       for_each_cfqg_st(cfqg, i, j, st)
-               *st = CFQ_RB_ROOT;
-       RB_CLEAR_NODE(&cfqg->rb_node);
-
-       /* Give preference to root group over other groups */
-       cfqg->weight = 2*BLKIO_WEIGHT_DEFAULT;
-
+       /* Init root group and prefer root group over other groups by default */
 #ifdef CONFIG_CFQ_GROUP_IOSCHED
-       /*
-        * Set root group reference to 2. One reference will be dropped when
-        * all groups on cfqd->cfqg_list are being deleted during queue exit.
-        * Other reference will remain there as we don't want to delete this
-        * group as it is statically allocated and gets destroyed when
-        * throtl_data goes away.
-        */
-       cfqg->ref = 2;
-
-       if (blkio_alloc_blkg_stats(&cfqg->blkg)) {
-               kfree(cfqg);
-               kfree(cfqd);
-               return NULL;
-       }
-
-       rcu_read_lock();
+       ret = blkcg_activate_policy(q, &blkcg_policy_cfq);
+       if (ret)
+               goto out_free;
 
-       cfq_blkiocg_add_blkio_group(&blkio_root_cgroup, &cfqg->blkg,
-                                       (void *)cfqd, 0);
-       rcu_read_unlock();
-       cfqd->nr_blkcg_linked_grps++;
+       cfqd->root_group = blkg_to_cfqg(q->root_blkg);
+#else
+       ret = -ENOMEM;
+       cfqd->root_group = kzalloc_node(sizeof(*cfqd->root_group),
+                                       GFP_KERNEL, cfqd->queue->node);
+       if (!cfqd->root_group)
+               goto out_free;
 
-       /* Add group on cfqd->cfqg_list */
-       hlist_add_head(&cfqg->cfqd_node, &cfqd->cfqg_list);
+       cfq_init_cfqg_base(cfqd->root_group);
 #endif
+       cfqd->root_group->weight = 2 * CFQ_WEIGHT_DEFAULT;
+
        /*
         * Not strictly needed (since RB_ROOT just clears the node and we
         * zeroed cfqd on alloc), but better be safe in case someone decides
@@ -3718,13 +4003,17 @@ static void *cfq_init_queue(struct request_queue *q)
        /*
         * Our fallback cfqq if cfq_find_alloc_queue() runs into OOM issues.
         * Grab a permanent reference to it, so that the normal code flow
-        * will not attempt to free it.
+        * will not attempt to free it.  oom_cfqq is linked to root_group
+        * but shouldn't hold a reference as it'll never be unlinked.  Lose
+        * the reference from linking right away.
         */
        cfq_init_cfqq(cfqd, &cfqd->oom_cfqq, 1, 0);
        cfqd->oom_cfqq.ref++;
-       cfq_link_cfqq_cfqg(&cfqd->oom_cfqq, &cfqd->root_group);
 
-       cfqd->queue = q;
+       spin_lock_irq(q->queue_lock);
+       cfq_link_cfqq_cfqg(&cfqd->oom_cfqq, cfqd->root_group);
+       cfqg_put(cfqd->root_group);
+       spin_unlock_irq(q->queue_lock);
 
        init_timer(&cfqd->idle_slice_timer);
        cfqd->idle_slice_timer.function = cfq_idle_slice_timer;
@@ -3750,7 +4039,11 @@ static void *cfq_init_queue(struct request_queue *q)
         * second, in order to have larger depth for async operations.
         */
        cfqd->last_delayed_sync = jiffies - HZ;
-       return cfqd;
+       return 0;
+
+out_free:
+       kfree(cfqd);
+       return ret;
 }
 
 /*
@@ -3877,15 +4170,13 @@ static struct elevator_type iosched_cfq = {
 };
 
 #ifdef CONFIG_CFQ_GROUP_IOSCHED
-static struct blkio_policy_type blkio_policy_cfq = {
-       .ops = {
-               .blkio_unlink_group_fn =        cfq_unlink_blkio_group,
-               .blkio_update_group_weight_fn = cfq_update_blkio_group_weight,
-       },
-       .plid = BLKIO_POLICY_PROP,
+static struct blkcg_policy blkcg_policy_cfq = {
+       .pd_size                = sizeof(struct cfq_group),
+       .cftypes                = cfq_blkcg_files,
+
+       .pd_init_fn             = cfq_pd_init,
+       .pd_reset_stats_fn      = cfq_pd_reset_stats,
 };
-#else
-static struct blkio_policy_type blkio_policy_cfq;
 #endif
 
 static int __init cfq_init(void)
@@ -3906,24 +4197,31 @@ static int __init cfq_init(void)
 #else
                cfq_group_idle = 0;
 #endif
+
+       ret = blkcg_policy_register(&blkcg_policy_cfq);
+       if (ret)
+               return ret;
+
        cfq_pool = KMEM_CACHE(cfq_queue, 0);
        if (!cfq_pool)
-               return -ENOMEM;
+               goto err_pol_unreg;
 
        ret = elv_register(&iosched_cfq);
-       if (ret) {
-               kmem_cache_destroy(cfq_pool);
-               return ret;
-       }
-
-       blkio_policy_register(&blkio_policy_cfq);
+       if (ret)
+               goto err_free_pool;
 
        return 0;
+
+err_free_pool:
+       kmem_cache_destroy(cfq_pool);
+err_pol_unreg:
+       blkcg_policy_unregister(&blkcg_policy_cfq);
+       return ret;
 }
 
 static void __exit cfq_exit(void)
 {
-       blkio_policy_unregister(&blkio_policy_cfq);
+       blkcg_policy_unregister(&blkcg_policy_cfq);
        elv_unregister(&iosched_cfq);
        kmem_cache_destroy(cfq_pool);
 }
diff --git a/block/cfq.h b/block/cfq.h
deleted file mode 100644 (file)
index 2a15592..0000000
+++ /dev/null
@@ -1,115 +0,0 @@
-#ifndef _CFQ_H
-#define _CFQ_H
-#include "blk-cgroup.h"
-
-#ifdef CONFIG_CFQ_GROUP_IOSCHED
-static inline void cfq_blkiocg_update_io_add_stats(struct blkio_group *blkg,
-       struct blkio_group *curr_blkg, bool direction, bool sync)
-{
-       blkiocg_update_io_add_stats(blkg, curr_blkg, direction, sync);
-}
-
-static inline void cfq_blkiocg_update_dequeue_stats(struct blkio_group *blkg,
-                       unsigned long dequeue)
-{
-       blkiocg_update_dequeue_stats(blkg, dequeue);
-}
-
-static inline void cfq_blkiocg_update_timeslice_used(struct blkio_group *blkg,
-                       unsigned long time, unsigned long unaccounted_time)
-{
-       blkiocg_update_timeslice_used(blkg, time, unaccounted_time);
-}
-
-static inline void cfq_blkiocg_set_start_empty_time(struct blkio_group *blkg)
-{
-       blkiocg_set_start_empty_time(blkg);
-}
-
-static inline void cfq_blkiocg_update_io_remove_stats(struct blkio_group *blkg,
-                               bool direction, bool sync)
-{
-       blkiocg_update_io_remove_stats(blkg, direction, sync);
-}
-
-static inline void cfq_blkiocg_update_io_merged_stats(struct blkio_group *blkg,
-               bool direction, bool sync)
-{
-       blkiocg_update_io_merged_stats(blkg, direction, sync);
-}
-
-static inline void cfq_blkiocg_update_idle_time_stats(struct blkio_group *blkg)
-{
-       blkiocg_update_idle_time_stats(blkg);
-}
-
-static inline void
-cfq_blkiocg_update_avg_queue_size_stats(struct blkio_group *blkg)
-{
-       blkiocg_update_avg_queue_size_stats(blkg);
-}
-
-static inline void
-cfq_blkiocg_update_set_idle_time_stats(struct blkio_group *blkg)
-{
-       blkiocg_update_set_idle_time_stats(blkg);
-}
-
-static inline void cfq_blkiocg_update_dispatch_stats(struct blkio_group *blkg,
-                               uint64_t bytes, bool direction, bool sync)
-{
-       blkiocg_update_dispatch_stats(blkg, bytes, direction, sync);
-}
-
-static inline void cfq_blkiocg_update_completion_stats(struct blkio_group *blkg, uint64_t start_time, uint64_t io_start_time, bool direction, bool sync)
-{
-       blkiocg_update_completion_stats(blkg, start_time, io_start_time,
-                               direction, sync);
-}
-
-static inline void cfq_blkiocg_add_blkio_group(struct blkio_cgroup *blkcg,
-                       struct blkio_group *blkg, void *key, dev_t dev) {
-       blkiocg_add_blkio_group(blkcg, blkg, key, dev, BLKIO_POLICY_PROP);
-}
-
-static inline int cfq_blkiocg_del_blkio_group(struct blkio_group *blkg)
-{
-       return blkiocg_del_blkio_group(blkg);
-}
-
-#else /* CFQ_GROUP_IOSCHED */
-static inline void cfq_blkiocg_update_io_add_stats(struct blkio_group *blkg,
-       struct blkio_group *curr_blkg, bool direction, bool sync) {}
-
-static inline void cfq_blkiocg_update_dequeue_stats(struct blkio_group *blkg,
-                       unsigned long dequeue) {}
-
-static inline void cfq_blkiocg_update_timeslice_used(struct blkio_group *blkg,
-                       unsigned long time, unsigned long unaccounted_time) {}
-static inline void cfq_blkiocg_set_start_empty_time(struct blkio_group *blkg) {}
-static inline void cfq_blkiocg_update_io_remove_stats(struct blkio_group *blkg,
-                               bool direction, bool sync) {}
-static inline void cfq_blkiocg_update_io_merged_stats(struct blkio_group *blkg,
-               bool direction, bool sync) {}
-static inline void cfq_blkiocg_update_idle_time_stats(struct blkio_group *blkg)
-{
-}
-static inline void
-cfq_blkiocg_update_avg_queue_size_stats(struct blkio_group *blkg) {}
-
-static inline void
-cfq_blkiocg_update_set_idle_time_stats(struct blkio_group *blkg) {}
-
-static inline void cfq_blkiocg_update_dispatch_stats(struct blkio_group *blkg,
-                               uint64_t bytes, bool direction, bool sync) {}
-static inline void cfq_blkiocg_update_completion_stats(struct blkio_group *blkg, uint64_t start_time, uint64_t io_start_time, bool direction, bool sync) {}
-
-static inline void cfq_blkiocg_add_blkio_group(struct blkio_cgroup *blkcg,
-                       struct blkio_group *blkg, void *key, dev_t dev) {}
-static inline int cfq_blkiocg_del_blkio_group(struct blkio_group *blkg)
-{
-       return 0;
-}
-
-#endif /* CFQ_GROUP_IOSCHED */
-#endif
index 7bf12d793fcdee25eb1ba178a59203b72d60ec2e..599b12e5380f50aa32e35ec3096b035437842805 100644 (file)
@@ -337,13 +337,13 @@ static void deadline_exit_queue(struct elevator_queue *e)
 /*
  * initialize elevator private data (deadline_data).
  */
-static void *deadline_init_queue(struct request_queue *q)
+static int deadline_init_queue(struct request_queue *q)
 {
        struct deadline_data *dd;
 
        dd = kmalloc_node(sizeof(*dd), GFP_KERNEL | __GFP_ZERO, q->node);
        if (!dd)
-               return NULL;
+               return -ENOMEM;
 
        INIT_LIST_HEAD(&dd->fifo_list[READ]);
        INIT_LIST_HEAD(&dd->fifo_list[WRITE]);
@@ -354,7 +354,9 @@ static void *deadline_init_queue(struct request_queue *q)
        dd->writes_starved = writes_starved;
        dd->front_merges = 1;
        dd->fifo_batch = fifo_batch;
-       return dd;
+
+       q->elevator->elevator_data = dd;
+       return 0;
 }
 
 /*
index f016855a46b094628190f68e698ae21a936912e6..6a55d418896f5ceee0042da69c0177c495219cbc 100644 (file)
@@ -38,6 +38,7 @@
 #include <trace/events/block.h>
 
 #include "blk.h"
+#include "blk-cgroup.h"
 
 static DEFINE_SPINLOCK(elv_list_lock);
 static LIST_HEAD(elv_list);
@@ -121,15 +122,6 @@ static struct elevator_type *elevator_get(const char *name)
        return e;
 }
 
-static int elevator_init_queue(struct request_queue *q,
-                              struct elevator_queue *eq)
-{
-       eq->elevator_data = eq->type->ops.elevator_init_fn(q);
-       if (eq->elevator_data)
-               return 0;
-       return -ENOMEM;
-}
-
 static char chosen_elevator[ELV_NAME_MAX];
 
 static int __init elevator_setup(char *str)
@@ -188,7 +180,6 @@ static void elevator_release(struct kobject *kobj)
 int elevator_init(struct request_queue *q, char *name)
 {
        struct elevator_type *e = NULL;
-       struct elevator_queue *eq;
        int err;
 
        if (unlikely(q->elevator))
@@ -222,17 +213,16 @@ int elevator_init(struct request_queue *q, char *name)
                }
        }
 
-       eq = elevator_alloc(q, e);
-       if (!eq)
+       q->elevator = elevator_alloc(q, e);
+       if (!q->elevator)
                return -ENOMEM;
 
-       err = elevator_init_queue(q, eq);
+       err = e->ops.elevator_init_fn(q);
        if (err) {
-               kobject_put(&eq->kobj);
+               kobject_put(&q->elevator->kobj);
                return err;
        }
 
-       q->elevator = eq;
        return 0;
 }
 EXPORT_SYMBOL(elevator_init);
@@ -564,25 +554,6 @@ void elv_drain_elevator(struct request_queue *q)
        }
 }
 
-void elv_quiesce_start(struct request_queue *q)
-{
-       if (!q->elevator)
-               return;
-
-       spin_lock_irq(q->queue_lock);
-       queue_flag_set(QUEUE_FLAG_ELVSWITCH, q);
-       spin_unlock_irq(q->queue_lock);
-
-       blk_drain_queue(q, false);
-}
-
-void elv_quiesce_end(struct request_queue *q)
-{
-       spin_lock_irq(q->queue_lock);
-       queue_flag_clear(QUEUE_FLAG_ELVSWITCH, q);
-       spin_unlock_irq(q->queue_lock);
-}
-
 void __elv_add_request(struct request_queue *q, struct request *rq, int where)
 {
        trace_block_rq_insert(q, rq);
@@ -692,12 +663,13 @@ struct request *elv_former_request(struct request_queue *q, struct request *rq)
        return NULL;
 }
 
-int elv_set_request(struct request_queue *q, struct request *rq, gfp_t gfp_mask)
+int elv_set_request(struct request_queue *q, struct request *rq,
+                   struct bio *bio, gfp_t gfp_mask)
 {
        struct elevator_queue *e = q->elevator;
 
        if (e->type->ops.elevator_set_req_fn)
-               return e->type->ops.elevator_set_req_fn(q, rq, gfp_mask);
+               return e->type->ops.elevator_set_req_fn(q, rq, bio, gfp_mask);
        return 0;
 }
 
@@ -801,8 +773,9 @@ static struct kobj_type elv_ktype = {
        .release        = elevator_release,
 };
 
-int __elv_register_queue(struct request_queue *q, struct elevator_queue *e)
+int elv_register_queue(struct request_queue *q)
 {
+       struct elevator_queue *e = q->elevator;
        int error;
 
        error = kobject_add(&e->kobj, &q->kobj, "%s", "iosched");
@@ -820,11 +793,6 @@ int __elv_register_queue(struct request_queue *q, struct elevator_queue *e)
        }
        return error;
 }
-
-int elv_register_queue(struct request_queue *q)
-{
-       return __elv_register_queue(q, q->elevator);
-}
 EXPORT_SYMBOL(elv_register_queue);
 
 void elv_unregister_queue(struct request_queue *q)
@@ -907,53 +875,60 @@ EXPORT_SYMBOL_GPL(elv_unregister);
  */
 static int elevator_switch(struct request_queue *q, struct elevator_type *new_e)
 {
-       struct elevator_queue *old_elevator, *e;
+       struct elevator_queue *old = q->elevator;
+       bool registered = old->registered;
        int err;
 
-       /* allocate new elevator */
-       e = elevator_alloc(q, new_e);
-       if (!e)
-               return -ENOMEM;
+       /*
+        * Turn on BYPASS and drain all requests w/ elevator private data.
+        * Block layer doesn't call into a quiesced elevator - all requests
+        * are directly put on the dispatch list without elevator data
+        * using INSERT_BACK.  All requests have SOFTBARRIER set and no
+        * merge happens either.
+        */
+       blk_queue_bypass_start(q);
+
+       /* unregister and clear all auxiliary data of the old elevator */
+       if (registered)
+               elv_unregister_queue(q);
+
+       spin_lock_irq(q->queue_lock);
+       ioc_clear_queue(q);
+       spin_unlock_irq(q->queue_lock);
 
-       err = elevator_init_queue(q, e);
+       /* allocate, init and register new elevator */
+       err = -ENOMEM;
+       q->elevator = elevator_alloc(q, new_e);
+       if (!q->elevator)
+               goto fail_init;
+
+       err = new_e->ops.elevator_init_fn(q);
        if (err) {
-               kobject_put(&e->kobj);
-               return err;
+               kobject_put(&q->elevator->kobj);
+               goto fail_init;
        }
 
-       /* turn on BYPASS and drain all requests w/ elevator private data */
-       elv_quiesce_start(q);
-
-       /* unregister old queue, register new one and kill old elevator */
-       if (q->elevator->registered) {
-               elv_unregister_queue(q);
-               err = __elv_register_queue(q, e);
+       if (registered) {
+               err = elv_register_queue(q);
                if (err)
                        goto fail_register;
        }
 
-       /* done, clear io_cq's, switch elevators and turn off BYPASS */
-       spin_lock_irq(q->queue_lock);
-       ioc_clear_queue(q);
-       old_elevator = q->elevator;
-       q->elevator = e;
-       spin_unlock_irq(q->queue_lock);
-
-       elevator_exit(old_elevator);
-       elv_quiesce_end(q);
+       /* done, kill the old one and finish */
+       elevator_exit(old);
+       blk_queue_bypass_end(q);
 
-       blk_add_trace_msg(q, "elv switch: %s", e->type->elevator_name);
+       blk_add_trace_msg(q, "elv switch: %s", new_e->elevator_name);
 
        return 0;
 
 fail_register:
-       /*
-        * switch failed, exit the new io scheduler and reattach the old
-        * one again (along with re-adding the sysfs dir)
-        */
-       elevator_exit(e);
+       elevator_exit(q->elevator);
+fail_init:
+       /* switch failed, restore and re-register old elevator */
+       q->elevator = old;
        elv_register_queue(q);
-       elv_quiesce_end(q);
+       blk_queue_bypass_end(q);
 
        return err;
 }
index 413a0b1d788c745df932745a65adb36ec119afaa..5d1bf70e33d5a04a5fc994c8a8cb0c9ecdbf0900 100644 (file)
@@ -59,15 +59,17 @@ noop_latter_request(struct request_queue *q, struct request *rq)
        return list_entry(rq->queuelist.next, struct request, queuelist);
 }
 
-static void *noop_init_queue(struct request_queue *q)
+static int noop_init_queue(struct request_queue *q)
 {
        struct noop_data *nd;
 
        nd = kmalloc_node(sizeof(*nd), GFP_KERNEL, q->node);
        if (!nd)
-               return NULL;
+               return -ENOMEM;
+
        INIT_LIST_HEAD(&nd->queue);
-       return nd;
+       q->elevator->elevator_data = nd;
+       return 0;
 }
 
 static void noop_exit_queue(struct elevator_queue *e)
index 8cf6c46e99fb538b320b7e63131269ef78accaae..6680df36b9634f414cc7438ca57590bef0c8cc71 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/init.h>
 #include <linux/device.h>
 #include <linux/sysfs.h>
+#include <linux/io.h>
 #include <acpi/acpi.h>
 #include <acpi/acpi_bus.h>
 
index 06527c526618ce2b9a22b2cd4ffc77449cfbc116..74ee4ab577b67b2dd22b1068240c4c651f1e2e3a 100644 (file)
@@ -93,11 +93,9 @@ static int acpi_sleep_prepare(u32 acpi_state)
 #ifdef CONFIG_ACPI_SLEEP
        /* do we have a wakeup address for S2 and S3? */
        if (acpi_state == ACPI_STATE_S3) {
-               if (!acpi_wakeup_address) {
+               if (!acpi_wakeup_address)
                        return -EFAULT;
-               }
-               acpi_set_firmware_waking_vector(
-                               (acpi_physical_address)acpi_wakeup_address);
+               acpi_set_firmware_waking_vector(acpi_wakeup_address);
 
        }
        ACPI_FLUSH_CPU_CACHE();
index e8cd652d20178c7c3ff8465aac997f1f65e20f63..98510931c8153110ee0617457fb63cafe652696a 100644 (file)
@@ -984,6 +984,7 @@ static uint32_t fpga_tx(struct solos_card *card)
                        } else if (skb && card->using_dma) {
                                SKB_CB(skb)->dma_addr = pci_map_single(card->dev, skb->data,
                                                                       skb->len, PCI_DMA_TODEVICE);
+                               card->tx_skb[port] = skb;
                                iowrite32(SKB_CB(skb)->dma_addr,
                                          card->config_regs + TX_DMA_ADDR(port));
                        }
@@ -1152,7 +1153,8 @@ static int fpga_probe(struct pci_dev *dev, const struct pci_device_id *id)
                db_fpga_upgrade = db_firmware_upgrade = 0;
        }
 
-       if (card->fpga_version >= DMA_SUPPORTED){
+       if (card->fpga_version >= DMA_SUPPORTED) {
+               pci_set_master(dev);
                card->using_dma = 1;
        } else {
                card->using_dma = 0;
index 90aa2a11a933b448a536a353c5d932668c3f8e11..af1a177216f12573c7e481fea0a568c09b97ef12 100644 (file)
@@ -592,11 +592,9 @@ static ssize_t print_nodes_state(enum node_states state, char *buf)
 {
        int n;
 
-       n = nodelist_scnprintf(buf, PAGE_SIZE, node_states[state]);
-       if (n > 0 && PAGE_SIZE > n + 1) {
-               *(buf + n++) = '\n';
-               *(buf + n++) = '\0';
-       }
+       n = nodelist_scnprintf(buf, PAGE_SIZE-2, node_states[state]);
+       buf[n++] = '\n';
+       buf[n] = '\0';
        return n;
 }
 
index 5f6b2478bf1759717e9c85f1958207364f3f7520..fa6bf5279d28465f095c0829381854a718313e5e 100644 (file)
@@ -42,7 +42,7 @@ static int regmap_i2c_gather_write(void *context,
        /* If the I2C controller can't do a gather tell the core, it
         * will substitute in a linear write for us.
         */
-       if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_PROTOCOL_MANGLING))
+       if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_NOSTART))
                return -ENOTSUPP;
 
        xfer[0].addr = i2c->addr;
index ba29b2e73d48936ab9a93a028abd0b0d9cd29691..72b5e7280d14792e6d83f3a59e4d4793d20fe28c 100644 (file)
@@ -42,7 +42,7 @@ struct device *soc_device_to_device(struct soc_device *soc_dev)
        return &soc_dev->dev;
 }
 
-static mode_t soc_attribute_mode(struct kobject *kobj,
+static umode_t soc_attribute_mode(struct kobject *kobj,
                                  struct attribute *attr,
                                  int index)
 {
index cf0e63dd97da9bf09a88bf364cba87956cea4f42..e54e31b02b88eb6e927072745f345871a8d96203 100644 (file)
@@ -65,39 +65,80 @@ struct drbd_atodb_wait {
 
 int w_al_write_transaction(struct drbd_conf *, struct drbd_work *, int);
 
+void *drbd_md_get_buffer(struct drbd_conf *mdev)
+{
+       int r;
+
+       wait_event(mdev->misc_wait,
+                  (r = atomic_cmpxchg(&mdev->md_io_in_use, 0, 1)) == 0 ||
+                  mdev->state.disk <= D_FAILED);
+
+       return r ? NULL : page_address(mdev->md_io_page);
+}
+
+void drbd_md_put_buffer(struct drbd_conf *mdev)
+{
+       if (atomic_dec_and_test(&mdev->md_io_in_use))
+               wake_up(&mdev->misc_wait);
+}
+
+static bool md_io_allowed(struct drbd_conf *mdev)
+{
+       enum drbd_disk_state ds = mdev->state.disk;
+       return ds >= D_NEGOTIATING || ds == D_ATTACHING;
+}
+
+void wait_until_done_or_disk_failure(struct drbd_conf *mdev, struct drbd_backing_dev *bdev,
+                                    unsigned int *done)
+{
+       long dt = bdev->dc.disk_timeout * HZ / 10;
+       if (dt == 0)
+               dt = MAX_SCHEDULE_TIMEOUT;
+
+       dt = wait_event_timeout(mdev->misc_wait, *done || !md_io_allowed(mdev), dt);
+       if (dt == 0)
+               dev_err(DEV, "meta-data IO operation timed out\n");
+}
+
 static int _drbd_md_sync_page_io(struct drbd_conf *mdev,
                                 struct drbd_backing_dev *bdev,
                                 struct page *page, sector_t sector,
                                 int rw, int size)
 {
        struct bio *bio;
-       struct drbd_md_io md_io;
        int ok;
 
-       md_io.mdev = mdev;
-       init_completion(&md_io.event);
-       md_io.error = 0;
+       mdev->md_io.done = 0;
+       mdev->md_io.error = -ENODEV;
 
        if ((rw & WRITE) && !test_bit(MD_NO_FUA, &mdev->flags))
                rw |= REQ_FUA | REQ_FLUSH;
        rw |= REQ_SYNC;
 
-       bio = bio_alloc(GFP_NOIO, 1);
+       bio = bio_alloc_drbd(GFP_NOIO);
        bio->bi_bdev = bdev->md_bdev;
        bio->bi_sector = sector;
        ok = (bio_add_page(bio, page, size, 0) == size);
        if (!ok)
                goto out;
-       bio->bi_private = &md_io;
+       bio->bi_private = &mdev->md_io;
        bio->bi_end_io = drbd_md_io_complete;
        bio->bi_rw = rw;
 
+       if (!get_ldev_if_state(mdev, D_ATTACHING)) {  /* Corresponding put_ldev in drbd_md_io_complete() */
+               dev_err(DEV, "ASSERT FAILED: get_ldev_if_state() == 1 in _drbd_md_sync_page_io()\n");
+               ok = 0;
+               goto out;
+       }
+
+       bio_get(bio); /* one bio_put() is in the completion handler */
+       atomic_inc(&mdev->md_io_in_use); /* drbd_md_put_buffer() is in the completion handler */
        if (drbd_insert_fault(mdev, (rw & WRITE) ? DRBD_FAULT_MD_WR : DRBD_FAULT_MD_RD))
                bio_endio(bio, -EIO);
        else
                submit_bio(rw, bio);
-       wait_for_completion(&md_io.event);
-       ok = bio_flagged(bio, BIO_UPTODATE) && md_io.error == 0;
+       wait_until_done_or_disk_failure(mdev, bdev, &mdev->md_io.done);
+       ok = bio_flagged(bio, BIO_UPTODATE) && mdev->md_io.error == 0;
 
  out:
        bio_put(bio);
@@ -111,7 +152,7 @@ int drbd_md_sync_page_io(struct drbd_conf *mdev, struct drbd_backing_dev *bdev,
        int offset = 0;
        struct page *iop = mdev->md_io_page;
 
-       D_ASSERT(mutex_is_locked(&mdev->md_io_mutex));
+       D_ASSERT(atomic_read(&mdev->md_io_in_use) == 1);
 
        BUG_ON(!bdev->md_bdev);
 
@@ -328,8 +369,13 @@ w_al_write_transaction(struct drbd_conf *mdev, struct drbd_work *w, int unused)
                return 1;
        }
 
-       mutex_lock(&mdev->md_io_mutex); /* protects md_io_buffer, al_tr_cycle, ... */
-       buffer = (struct al_transaction *)page_address(mdev->md_io_page);
+       buffer = drbd_md_get_buffer(mdev); /* protects md_io_buffer, al_tr_cycle, ... */
+       if (!buffer) {
+               dev_err(DEV, "disk failed while waiting for md_io buffer\n");
+               complete(&((struct update_al_work *)w)->event);
+               put_ldev(mdev);
+               return 1;
+       }
 
        buffer->magic = __constant_cpu_to_be32(DRBD_MAGIC);
        buffer->tr_number = cpu_to_be32(mdev->al_tr_number);
@@ -374,7 +420,7 @@ w_al_write_transaction(struct drbd_conf *mdev, struct drbd_work *w, int unused)
        D_ASSERT(mdev->al_tr_pos < MD_AL_MAX_SIZE);
        mdev->al_tr_number++;
 
-       mutex_unlock(&mdev->md_io_mutex);
+       drbd_md_put_buffer(mdev);
 
        complete(&((struct update_al_work *)w)->event);
        put_ldev(mdev);
@@ -443,8 +489,9 @@ int drbd_al_read_log(struct drbd_conf *mdev, struct drbd_backing_dev *bdev)
        /* lock out all other meta data io for now,
         * and make sure the page is mapped.
         */
-       mutex_lock(&mdev->md_io_mutex);
-       buffer = page_address(mdev->md_io_page);
+       buffer = drbd_md_get_buffer(mdev);
+       if (!buffer)
+               return 0;
 
        /* Find the valid transaction in the log */
        for (i = 0; i <= mx; i++) {
@@ -452,7 +499,7 @@ int drbd_al_read_log(struct drbd_conf *mdev, struct drbd_backing_dev *bdev)
                if (rv == 0)
                        continue;
                if (rv == -1) {
-                       mutex_unlock(&mdev->md_io_mutex);
+                       drbd_md_put_buffer(mdev);
                        return 0;
                }
                cnr = be32_to_cpu(buffer->tr_number);
@@ -478,7 +525,7 @@ int drbd_al_read_log(struct drbd_conf *mdev, struct drbd_backing_dev *bdev)
 
        if (!found_valid) {
                dev_warn(DEV, "No usable activity log found.\n");
-               mutex_unlock(&mdev->md_io_mutex);
+               drbd_md_put_buffer(mdev);
                return 1;
        }
 
@@ -493,7 +540,7 @@ int drbd_al_read_log(struct drbd_conf *mdev, struct drbd_backing_dev *bdev)
                rv = drbd_al_read_tr(mdev, bdev, buffer, i);
                ERR_IF(rv == 0) goto cancel;
                if (rv == -1) {
-                       mutex_unlock(&mdev->md_io_mutex);
+                       drbd_md_put_buffer(mdev);
                        return 0;
                }
 
@@ -534,7 +581,7 @@ cancel:
                mdev->al_tr_pos = 0;
 
        /* ok, we are done with it */
-       mutex_unlock(&mdev->md_io_mutex);
+       drbd_md_put_buffer(mdev);
 
        dev_info(DEV, "Found %d transactions (%d active extents) in activity log.\n",
             transactions, active_extents);
@@ -671,16 +718,20 @@ static void drbd_try_clear_on_disk_bm(struct drbd_conf *mdev, sector_t sector,
                        else
                                ext->rs_failed += count;
                        if (ext->rs_left < ext->rs_failed) {
-                               dev_err(DEV, "BAD! sector=%llus enr=%u rs_left=%d "
-                                   "rs_failed=%d count=%d\n",
+                               dev_warn(DEV, "BAD! sector=%llus enr=%u rs_left=%d "
+                                   "rs_failed=%d count=%d cstate=%s\n",
                                     (unsigned long long)sector,
                                     ext->lce.lc_number, ext->rs_left,
-                                    ext->rs_failed, count);
-                               dump_stack();
-
-                               lc_put(mdev->resync, &ext->lce);
-                               drbd_force_state(mdev, NS(conn, C_DISCONNECTING));
-                               return;
+                                    ext->rs_failed, count,
+                                    drbd_conn_str(mdev->state.conn));
+
+                               /* We don't expect to be able to clear more bits
+                                * than have been set when we originally counted
+                                * the set bits to cache that value in ext->rs_left.
+                                * Whatever the reason (disconnect during resync,
+                                * delayed local completion of an application write),
+                                * try to fix it up by recounting here. */
+                               ext->rs_left = drbd_bm_e_weight(mdev, enr);
                        }
                } else {
                        /* Normally this element should be in the cache,
@@ -1192,6 +1243,7 @@ int drbd_rs_del_all(struct drbd_conf *mdev)
                put_ldev(mdev);
        }
        spin_unlock_irq(&mdev->al_lock);
+       wake_up(&mdev->al_wait);
 
        return 0;
 }
index 3030201c69d89e7407230bc8fb2d48dd1b2fbdf3..b5c5ff53cb57f74e89cc59bad8ca6e29df5e1db5 100644 (file)
@@ -205,7 +205,7 @@ void drbd_bm_unlock(struct drbd_conf *mdev)
 static void bm_store_page_idx(struct page *page, unsigned long idx)
 {
        BUG_ON(0 != (idx & ~BM_PAGE_IDX_MASK));
-       page_private(page) |= idx;
+       set_page_private(page, idx);
 }
 
 static unsigned long bm_page_to_idx(struct page *page)
@@ -886,12 +886,21 @@ void drbd_bm_clear_all(struct drbd_conf *mdev)
 struct bm_aio_ctx {
        struct drbd_conf *mdev;
        atomic_t in_flight;
-       struct completion done;
+       unsigned int done;
        unsigned flags;
 #define BM_AIO_COPY_PAGES      1
        int error;
+       struct kref kref;
 };
 
+static void bm_aio_ctx_destroy(struct kref *kref)
+{
+       struct bm_aio_ctx *ctx = container_of(kref, struct bm_aio_ctx, kref);
+
+       put_ldev(ctx->mdev);
+       kfree(ctx);
+}
+
 /* bv_page may be a copy, or may be the original */
 static void bm_async_io_complete(struct bio *bio, int error)
 {
@@ -930,20 +939,21 @@ static void bm_async_io_complete(struct bio *bio, int error)
 
        bm_page_unlock_io(mdev, idx);
 
-       /* FIXME give back to page pool */
        if (ctx->flags & BM_AIO_COPY_PAGES)
-               put_page(bio->bi_io_vec[0].bv_page);
+               mempool_free(bio->bi_io_vec[0].bv_page, drbd_md_io_page_pool);
 
        bio_put(bio);
 
-       if (atomic_dec_and_test(&ctx->in_flight))
-               complete(&ctx->done);
+       if (atomic_dec_and_test(&ctx->in_flight)) {
+               ctx->done = 1;
+               wake_up(&mdev->misc_wait);
+               kref_put(&ctx->kref, &bm_aio_ctx_destroy);
+       }
 }
 
 static void bm_page_io_async(struct bm_aio_ctx *ctx, int page_nr, int rw) __must_hold(local)
 {
-       /* we are process context. we always get a bio */
-       struct bio *bio = bio_alloc(GFP_KERNEL, 1);
+       struct bio *bio = bio_alloc_drbd(GFP_NOIO);
        struct drbd_conf *mdev = ctx->mdev;
        struct drbd_bitmap *b = mdev->bitmap;
        struct page *page;
@@ -966,10 +976,8 @@ static void bm_page_io_async(struct bm_aio_ctx *ctx, int page_nr, int rw) __must
        bm_set_page_unchanged(b->bm_pages[page_nr]);
 
        if (ctx->flags & BM_AIO_COPY_PAGES) {
-               /* FIXME alloc_page is good enough for now, but actually needs
-                * to use pre-allocated page pool */
                void *src, *dest;
-               page = alloc_page(__GFP_HIGHMEM|__GFP_WAIT);
+               page = mempool_alloc(drbd_md_io_page_pool, __GFP_HIGHMEM|__GFP_WAIT);
                dest = kmap_atomic(page);
                src = kmap_atomic(b->bm_pages[page_nr]);
                memcpy(dest, src, PAGE_SIZE);
@@ -981,6 +989,8 @@ static void bm_page_io_async(struct bm_aio_ctx *ctx, int page_nr, int rw) __must
 
        bio->bi_bdev = mdev->ldev->md_bdev;
        bio->bi_sector = on_disk_sector;
+       /* bio_add_page of a single page to an empty bio will always succeed,
+        * according to api.  Do we want to assert that? */
        bio_add_page(bio, page, len, 0);
        bio->bi_private = ctx;
        bio->bi_end_io = bm_async_io_complete;
@@ -999,14 +1009,9 @@ static void bm_page_io_async(struct bm_aio_ctx *ctx, int page_nr, int rw) __must
 /*
  * bm_rw: read/write the whole bitmap from/to its on disk location.
  */
-static int bm_rw(struct drbd_conf *mdev, int rw, unsigned lazy_writeout_upper_idx) __must_hold(local)
+static int bm_rw(struct drbd_conf *mdev, int rw, unsigned flags, unsigned lazy_writeout_upper_idx) __must_hold(local)
 {
-       struct bm_aio_ctx ctx = {
-               .mdev = mdev,
-               .in_flight = ATOMIC_INIT(1),
-               .done = COMPLETION_INITIALIZER_ONSTACK(ctx.done),
-               .flags = lazy_writeout_upper_idx ? BM_AIO_COPY_PAGES : 0,
-       };
+       struct bm_aio_ctx *ctx;
        struct drbd_bitmap *b = mdev->bitmap;
        int num_pages, i, count = 0;
        unsigned long now;
@@ -1021,7 +1026,27 @@ static int bm_rw(struct drbd_conf *mdev, int rw, unsigned lazy_writeout_upper_id
         * For lazy writeout, we don't care for ongoing changes to the bitmap,
         * as we submit copies of pages anyways.
         */
-       if (!ctx.flags)
+
+       ctx = kmalloc(sizeof(struct bm_aio_ctx), GFP_NOIO);
+       if (!ctx)
+               return -ENOMEM;
+
+       *ctx = (struct bm_aio_ctx) {
+               .mdev = mdev,
+               .in_flight = ATOMIC_INIT(1),
+               .done = 0,
+               .flags = flags,
+               .error = 0,
+               .kref = { ATOMIC_INIT(2) },
+       };
+
+       if (!get_ldev_if_state(mdev, D_ATTACHING)) {  /* put is in bm_aio_ctx_destroy() */
+               dev_err(DEV, "ASSERT FAILED: get_ldev_if_state() == 1 in bm_rw()\n");
+               kfree(ctx);
+               return -ENODEV;
+       }
+
+       if (!ctx->flags)
                WARN_ON(!(BM_LOCKED_MASK & b->bm_flags));
 
        num_pages = b->bm_number_of_pages;
@@ -1046,29 +1071,38 @@ static int bm_rw(struct drbd_conf *mdev, int rw, unsigned lazy_writeout_upper_id
                                continue;
                        }
                }
-               atomic_inc(&ctx.in_flight);
-               bm_page_io_async(&ctx, i, rw);
+               atomic_inc(&ctx->in_flight);
+               bm_page_io_async(ctx, i, rw);
                ++count;
                cond_resched();
        }
 
        /*
-        * We initialize ctx.in_flight to one to make sure bm_async_io_complete
-        * will not complete() early, and decrement / test it here.  If there
+        * We initialize ctx->in_flight to one to make sure bm_async_io_complete
+        * will not set ctx->done early, and decrement / test it here.  If there
         * are still some bios in flight, we need to wait for them here.
+        * If all IO is done already (or nothing had been submitted), there is
+        * no need to wait.  Still, we need to put the kref associated with the
+        * "in_flight reached zero, all done" event.
         */
-       if (!atomic_dec_and_test(&ctx.in_flight))
-               wait_for_completion(&ctx.done);
+       if (!atomic_dec_and_test(&ctx->in_flight))
+               wait_until_done_or_disk_failure(mdev, mdev->ldev, &ctx->done);
+       else
+               kref_put(&ctx->kref, &bm_aio_ctx_destroy);
+
        dev_info(DEV, "bitmap %s of %u pages took %lu jiffies\n",
                        rw == WRITE ? "WRITE" : "READ",
                        count, jiffies - now);
 
-       if (ctx.error) {
+       if (ctx->error) {
                dev_alert(DEV, "we had at least one MD IO ERROR during bitmap IO\n");
                drbd_chk_io_error(mdev, 1, true);
-               err = -EIO; /* ctx.error ? */
+               err = -EIO; /* ctx->error ? */
        }
 
+       if (atomic_read(&ctx->in_flight))
+               err = -EIO; /* Disk failed during IO... */
+
        now = jiffies;
        if (rw == WRITE) {
                drbd_md_flush(mdev);
@@ -1082,6 +1116,7 @@ static int bm_rw(struct drbd_conf *mdev, int rw, unsigned lazy_writeout_upper_id
        dev_info(DEV, "%s (%lu bits) marked out-of-sync by on disk bit-map.\n",
             ppsize(ppb, now << (BM_BLOCK_SHIFT-10)), now);
 
+       kref_put(&ctx->kref, &bm_aio_ctx_destroy);
        return err;
 }
 
@@ -1091,7 +1126,7 @@ static int bm_rw(struct drbd_conf *mdev, int rw, unsigned lazy_writeout_upper_id
  */
 int drbd_bm_read(struct drbd_conf *mdev) __must_hold(local)
 {
-       return bm_rw(mdev, READ, 0);
+       return bm_rw(mdev, READ, 0, 0);
 }
 
 /**
@@ -1102,7 +1137,7 @@ int drbd_bm_read(struct drbd_conf *mdev) __must_hold(local)
  */
 int drbd_bm_write(struct drbd_conf *mdev) __must_hold(local)
 {
-       return bm_rw(mdev, WRITE, 0);
+       return bm_rw(mdev, WRITE, 0, 0);
 }
 
 /**
@@ -1112,7 +1147,23 @@ int drbd_bm_write(struct drbd_conf *mdev) __must_hold(local)
  */
 int drbd_bm_write_lazy(struct drbd_conf *mdev, unsigned upper_idx) __must_hold(local)
 {
-       return bm_rw(mdev, WRITE, upper_idx);
+       return bm_rw(mdev, WRITE, BM_AIO_COPY_PAGES, upper_idx);
+}
+
+/**
+ * drbd_bm_write_copy_pages() - Write the whole bitmap to its on disk location.
+ * @mdev:      DRBD device.
+ *
+ * Will only write pages that have changed since last IO.
+ * In contrast to drbd_bm_write(), this will copy the bitmap pages
+ * to temporary writeout pages. It is intended to trigger a full write-out
+ * while still allowing the bitmap to change, for example if a resync or online
+ * verify is aborted due to a failed peer disk, while local IO continues, or
+ * pending resync acks are still being processed.
+ */
+int drbd_bm_write_copy_pages(struct drbd_conf *mdev) __must_hold(local)
+{
+       return bm_rw(mdev, WRITE, BM_AIO_COPY_PAGES, 0);
 }
 
 
@@ -1130,28 +1181,45 @@ int drbd_bm_write_lazy(struct drbd_conf *mdev, unsigned upper_idx) __must_hold(l
  */
 int drbd_bm_write_page(struct drbd_conf *mdev, unsigned int idx) __must_hold(local)
 {
-       struct bm_aio_ctx ctx = {
+       struct bm_aio_ctx *ctx;
+       int err;
+
+       if (bm_test_page_unchanged(mdev->bitmap->bm_pages[idx])) {
+               dynamic_dev_dbg(DEV, "skipped bm page write for idx %u\n", idx);
+               return 0;
+       }
+
+       ctx = kmalloc(sizeof(struct bm_aio_ctx), GFP_NOIO);
+       if (!ctx)
+               return -ENOMEM;
+
+       *ctx = (struct bm_aio_ctx) {
                .mdev = mdev,
                .in_flight = ATOMIC_INIT(1),
-               .done = COMPLETION_INITIALIZER_ONSTACK(ctx.done),
+               .done = 0,
                .flags = BM_AIO_COPY_PAGES,
+               .error = 0,
+               .kref = { ATOMIC_INIT(2) },
        };
 
-       if (bm_test_page_unchanged(mdev->bitmap->bm_pages[idx])) {
-               dynamic_dev_dbg(DEV, "skipped bm page write for idx %u\n", idx);
-               return 0;
+       if (!get_ldev_if_state(mdev, D_ATTACHING)) {  /* put is in bm_aio_ctx_destroy() */
+               dev_err(DEV, "ASSERT FAILED: get_ldev_if_state() == 1 in drbd_bm_write_page()\n");
+               kfree(ctx);
+               return -ENODEV;
        }
 
-       bm_page_io_async(&ctx, idx, WRITE_SYNC);
-       wait_for_completion(&ctx.done);
+       bm_page_io_async(ctx, idx, WRITE_SYNC);
+       wait_until_done_or_disk_failure(mdev, mdev->ldev, &ctx->done);
 
-       if (ctx.error)
+       if (ctx->error)
                drbd_chk_io_error(mdev, 1, true);
                /* that should force detach, so the in memory bitmap will be
                 * gone in a moment as well. */
 
        mdev->bm_writ_cnt++;
-       return ctx.error;
+       err = atomic_read(&ctx->in_flight) ? -EIO : ctx->error;
+       kref_put(&ctx->kref, &bm_aio_ctx_destroy);
+       return err;
 }
 
 /* NOTE
index 8d680562ba73a1ca17120779c06aa834eea18f39..02f013a073a75b66fe73c8658f1cde96ea7102a7 100644 (file)
@@ -712,7 +712,6 @@ struct drbd_request {
        struct list_head tl_requests; /* ring list in the transfer log */
        struct bio *master_bio;       /* master bio pointer */
        unsigned long rq_state; /* see comments above _req_mod() */
-       int seq_num;
        unsigned long start_time;
 };
 
@@ -851,6 +850,7 @@ enum {
        NEW_CUR_UUID,           /* Create new current UUID when thawing IO */
        AL_SUSPENDED,           /* Activity logging is currently suspended. */
        AHEAD_TO_SYNC_SOURCE,   /* Ahead -> SyncSource queued */
+       STATE_SENT,             /* Do not change state/UUIDs while this is set */
 };
 
 struct drbd_bitmap; /* opaque for drbd_conf */
@@ -862,31 +862,30 @@ enum bm_flag {
        BM_P_VMALLOCED = 0x10000, /* internal use only, will be masked out */
 
        /* currently locked for bulk operation */
-       BM_LOCKED_MASK = 0x7,
+       BM_LOCKED_MASK = 0xf,
 
        /* in detail, that is: */
        BM_DONT_CLEAR = 0x1,
        BM_DONT_SET   = 0x2,
        BM_DONT_TEST  = 0x4,
 
+       /* so we can mark it locked for bulk operation,
+        * and still allow all non-bulk operations */
+       BM_IS_LOCKED  = 0x8,
+
        /* (test bit, count bit) allowed (common case) */
-       BM_LOCKED_TEST_ALLOWED = 0x3,
+       BM_LOCKED_TEST_ALLOWED = BM_DONT_CLEAR | BM_DONT_SET | BM_IS_LOCKED,
 
        /* testing bits, as well as setting new bits allowed, but clearing bits
         * would be unexpected.  Used during bitmap receive.  Setting new bits
         * requires sending of "out-of-sync" information, though. */
-       BM_LOCKED_SET_ALLOWED = 0x1,
+       BM_LOCKED_SET_ALLOWED = BM_DONT_CLEAR | BM_IS_LOCKED,
 
-       /* clear is not expected while bitmap is locked for bulk operation */
+       /* for drbd_bm_write_copy_pages, everything is allowed,
+        * only concurrent bulk operations are locked out. */
+       BM_LOCKED_CHANGE_ALLOWED = BM_IS_LOCKED,
 };
 
-
-/* TODO sort members for performance
- * MAYBE group them further */
-
-/* THINK maybe we actually want to use the default "event/%s" worker threads
- * or similar in linux 2.6, which uses per cpu data and threads.
- */
 struct drbd_work_queue {
        struct list_head q;
        struct semaphore s; /* producers up it, worker down()s it */
@@ -938,8 +937,7 @@ struct drbd_backing_dev {
 };
 
 struct drbd_md_io {
-       struct drbd_conf *mdev;
-       struct completion event;
+       unsigned int done;
        int error;
 };
 
@@ -1022,6 +1020,7 @@ struct drbd_conf {
        struct drbd_tl_epoch *newest_tle;
        struct drbd_tl_epoch *oldest_tle;
        struct list_head out_of_sequence_requests;
+       struct list_head barrier_acked_requests;
        struct hlist_head *tl_hash;
        unsigned int tl_hash_s;
 
@@ -1056,6 +1055,8 @@ struct drbd_conf {
        struct crypto_hash *csums_tfm;
        struct crypto_hash *verify_tfm;
 
+       unsigned long last_reattach_jif;
+       unsigned long last_reconnect_jif;
        struct drbd_thread receiver;
        struct drbd_thread worker;
        struct drbd_thread asender;
@@ -1094,7 +1095,8 @@ struct drbd_conf {
        wait_queue_head_t ee_wait;
        struct page *md_io_page;        /* one page buffer for md_io */
        struct page *md_io_tmpp;        /* for logical_block_size != 512 */
-       struct mutex md_io_mutex;       /* protects the md_io_buffer */
+       struct drbd_md_io md_io;
+       atomic_t md_io_in_use;          /* protects the md_io, md_io_page and md_io_tmpp */
        spinlock_t al_lock;
        wait_queue_head_t al_wait;
        struct lru_cache *act_log;      /* activity log */
@@ -1228,8 +1230,8 @@ extern int drbd_send_uuids(struct drbd_conf *mdev);
 extern int drbd_send_uuids_skip_initial_sync(struct drbd_conf *mdev);
 extern int drbd_gen_and_send_sync_uuid(struct drbd_conf *mdev);
 extern int drbd_send_sizes(struct drbd_conf *mdev, int trigger_reply, enum dds_flags flags);
-extern int _drbd_send_state(struct drbd_conf *mdev);
-extern int drbd_send_state(struct drbd_conf *mdev);
+extern int drbd_send_state(struct drbd_conf *mdev, union drbd_state s);
+extern int drbd_send_current_state(struct drbd_conf *mdev);
 extern int _drbd_send_cmd(struct drbd_conf *mdev, struct socket *sock,
                        enum drbd_packets cmd, struct p_header80 *h,
                        size_t size, unsigned msg_flags);
@@ -1461,6 +1463,7 @@ extern int  drbd_bm_e_weight(struct drbd_conf *mdev, unsigned long enr);
 extern int  drbd_bm_write_page(struct drbd_conf *mdev, unsigned int idx) __must_hold(local);
 extern int  drbd_bm_read(struct drbd_conf *mdev) __must_hold(local);
 extern int  drbd_bm_write(struct drbd_conf *mdev) __must_hold(local);
+extern int  drbd_bm_write_copy_pages(struct drbd_conf *mdev) __must_hold(local);
 extern unsigned long drbd_bm_ALe_set_all(struct drbd_conf *mdev,
                unsigned long al_enr);
 extern size_t       drbd_bm_words(struct drbd_conf *mdev);
@@ -1493,11 +1496,38 @@ extern struct kmem_cache *drbd_al_ext_cache;    /* activity log extents */
 extern mempool_t *drbd_request_mempool;
 extern mempool_t *drbd_ee_mempool;
 
-extern struct page *drbd_pp_pool; /* drbd's page pool */
+/* drbd's page pool, used to buffer data received from the peer,
+ * or data requested by the peer.
+ *
+ * This does not have an emergency reserve.
+ *
+ * When allocating from this pool, it first takes pages from the pool.
+ * Only if the pool is depleted will try to allocate from the system.
+ *
+ * The assumption is that pages taken from this pool will be processed,
+ * and given back, "quickly", and then can be recycled, so we can avoid
+ * frequent calls to alloc_page(), and still will be able to make progress even
+ * under memory pressure.
+ */
+extern struct page *drbd_pp_pool;
 extern spinlock_t   drbd_pp_lock;
 extern int         drbd_pp_vacant;
 extern wait_queue_head_t drbd_pp_wait;
 
+/* We also need a standard (emergency-reserve backed) page pool
+ * for meta data IO (activity log, bitmap).
+ * We can keep it global, as long as it is used as "N pages at a time".
+ * 128 should be plenty, currently we probably can get away with as few as 1.
+ */
+#define DRBD_MIN_POOL_PAGES    128
+extern mempool_t *drbd_md_io_page_pool;
+
+/* We also need to make sure we get a bio
+ * when we need it for housekeeping purposes */
+extern struct bio_set *drbd_md_io_bio_set;
+/* to allocate from that set */
+extern struct bio *bio_alloc_drbd(gfp_t gfp_mask);
+
 extern rwlock_t global_state_lock;
 
 extern struct drbd_conf *drbd_new_device(unsigned int minor);
@@ -1536,8 +1566,12 @@ extern void resume_next_sg(struct drbd_conf *mdev);
 extern void suspend_other_sg(struct drbd_conf *mdev);
 extern int drbd_resync_finished(struct drbd_conf *mdev);
 /* maybe rather drbd_main.c ? */
+extern void *drbd_md_get_buffer(struct drbd_conf *mdev);
+extern void drbd_md_put_buffer(struct drbd_conf *mdev);
 extern int drbd_md_sync_page_io(struct drbd_conf *mdev,
-               struct drbd_backing_dev *bdev, sector_t sector, int rw);
+                               struct drbd_backing_dev *bdev, sector_t sector, int rw);
+extern void wait_until_done_or_disk_failure(struct drbd_conf *mdev, struct drbd_backing_dev *bdev,
+                                           unsigned int *done);
 extern void drbd_ov_oos_found(struct drbd_conf*, sector_t, int);
 extern void drbd_rs_controller_reset(struct drbd_conf *mdev);
 
@@ -1754,19 +1788,6 @@ static inline struct page *page_chain_next(struct page *page)
 #define page_chain_for_each_safe(page, n) \
        for (; page && ({ n = page_chain_next(page); 1; }); page = n)
 
-static inline int drbd_bio_has_active_page(struct bio *bio)
-{
-       struct bio_vec *bvec;
-       int i;
-
-       __bio_for_each_segment(bvec, bio, i, 0) {
-               if (page_count(bvec->bv_page) > 1)
-                       return 1;
-       }
-
-       return 0;
-}
-
 static inline int drbd_ee_has_active_page(struct drbd_epoch_entry *e)
 {
        struct page *page = e->pages;
@@ -1777,7 +1798,6 @@ static inline int drbd_ee_has_active_page(struct drbd_epoch_entry *e)
        return 0;
 }
 
-
 static inline void drbd_state_lock(struct drbd_conf *mdev)
 {
        wait_event(mdev->misc_wait,
@@ -2230,7 +2250,7 @@ static inline void drbd_get_syncer_progress(struct drbd_conf *mdev,
                 * Note: currently we don't support such large bitmaps on 32bit
                 * arch anyways, but no harm done to be prepared for it here.
                 */
-               unsigned int shift = mdev->rs_total >= (1ULL << 32) ? 16 : 10;
+               unsigned int shift = mdev->rs_total > UINT_MAX ? 16 : 10;
                unsigned long left = *bits_left >> shift;
                unsigned long total = 1UL + (mdev->rs_total >> shift);
                unsigned long tmp = 1000UL - left * 1000UL/total;
@@ -2306,12 +2326,12 @@ static inline int drbd_state_is_stable(struct drbd_conf *mdev)
        case D_OUTDATED:
        case D_CONSISTENT:
        case D_UP_TO_DATE:
+       case D_FAILED:
                /* disk state is stable as well. */
                break;
 
        /* no new io accepted during tansitional states */
        case D_ATTACHING:
-       case D_FAILED:
        case D_NEGOTIATING:
        case D_UNKNOWN:
        case D_MASK:
index 211fc44f84be6ddda0112dbf2ad8a55ca1810c47..920ede2829d6c5e467e177ac43a3e97e9f550aac 100644 (file)
@@ -139,6 +139,8 @@ struct kmem_cache *drbd_bm_ext_cache;       /* bitmap extents */
 struct kmem_cache *drbd_al_ext_cache;  /* activity log extents */
 mempool_t *drbd_request_mempool;
 mempool_t *drbd_ee_mempool;
+mempool_t *drbd_md_io_page_pool;
+struct bio_set *drbd_md_io_bio_set;
 
 /* I do not use a standard mempool, because:
    1) I want to hand out the pre-allocated objects first.
@@ -159,7 +161,24 @@ static const struct block_device_operations drbd_ops = {
        .release = drbd_release,
 };
 
-#define ARRY_SIZE(A) (sizeof(A)/sizeof(A[0]))
+static void bio_destructor_drbd(struct bio *bio)
+{
+       bio_free(bio, drbd_md_io_bio_set);
+}
+
+struct bio *bio_alloc_drbd(gfp_t gfp_mask)
+{
+       struct bio *bio;
+
+       if (!drbd_md_io_bio_set)
+               return bio_alloc(gfp_mask, 1);
+
+       bio = bio_alloc_bioset(gfp_mask, 1, drbd_md_io_bio_set);
+       if (!bio)
+               return NULL;
+       bio->bi_destructor = bio_destructor_drbd;
+       return bio;
+}
 
 #ifdef __CHECKER__
 /* When checking with sparse, and this is an inline function, sparse will
@@ -208,6 +227,7 @@ static int tl_init(struct drbd_conf *mdev)
        mdev->oldest_tle = b;
        mdev->newest_tle = b;
        INIT_LIST_HEAD(&mdev->out_of_sequence_requests);
+       INIT_LIST_HEAD(&mdev->barrier_acked_requests);
 
        mdev->tl_hash = NULL;
        mdev->tl_hash_s = 0;
@@ -246,9 +266,7 @@ void _tl_add_barrier(struct drbd_conf *mdev, struct drbd_tl_epoch *new)
        new->n_writes = 0;
 
        newest_before = mdev->newest_tle;
-       /* never send a barrier number == 0, because that is special-cased
-        * when using TCQ for our write ordering code */
-       new->br_number = (newest_before->br_number+1) ?: 1;
+       new->br_number = newest_before->br_number+1;
        if (mdev->newest_tle != new) {
                mdev->newest_tle->next = new;
                mdev->newest_tle = new;
@@ -311,7 +329,7 @@ void tl_release(struct drbd_conf *mdev, unsigned int barrier_nr,
           These have been list_move'd to the out_of_sequence_requests list in
           _req_mod(, barrier_acked) above.
           */
-       list_del_init(&b->requests);
+       list_splice_init(&b->requests, &mdev->barrier_acked_requests);
 
        nob = b->next;
        if (test_and_clear_bit(CREATE_BARRIER, &mdev->flags)) {
@@ -411,6 +429,23 @@ static void _tl_restart(struct drbd_conf *mdev, enum drbd_req_event what)
                b = tmp;
                list_splice(&carry_reads, &b->requests);
        }
+
+       /* Actions operating on the disk state, also want to work on
+          requests that got barrier acked. */
+       switch (what) {
+       case fail_frozen_disk_io:
+       case restart_frozen_disk_io:
+               list_for_each_safe(le, tle, &mdev->barrier_acked_requests) {
+                       req = list_entry(le, struct drbd_request, tl_requests);
+                       _req_mod(req, what);
+               }
+
+       case connection_lost_while_pending:
+       case resend:
+               break;
+       default:
+               dev_err(DEV, "what = %d in _tl_restart()\n", what);
+       }
 }
 
 
@@ -457,6 +492,38 @@ void tl_restart(struct drbd_conf *mdev, enum drbd_req_event what)
        spin_unlock_irq(&mdev->req_lock);
 }
 
+/**
+ * tl_abort_disk_io() - Abort disk I/O for all requests for a certain mdev in the TL
+ * @mdev:      DRBD device.
+ */
+void tl_abort_disk_io(struct drbd_conf *mdev)
+{
+       struct drbd_tl_epoch *b;
+       struct list_head *le, *tle;
+       struct drbd_request *req;
+
+       spin_lock_irq(&mdev->req_lock);
+       b = mdev->oldest_tle;
+       while (b) {
+               list_for_each_safe(le, tle, &b->requests) {
+                       req = list_entry(le, struct drbd_request, tl_requests);
+                       if (!(req->rq_state & RQ_LOCAL_PENDING))
+                               continue;
+                       _req_mod(req, abort_disk_io);
+               }
+               b = b->next;
+       }
+
+       list_for_each_safe(le, tle, &mdev->barrier_acked_requests) {
+               req = list_entry(le, struct drbd_request, tl_requests);
+               if (!(req->rq_state & RQ_LOCAL_PENDING))
+                       continue;
+               _req_mod(req, abort_disk_io);
+       }
+
+       spin_unlock_irq(&mdev->req_lock);
+}
+
 /**
  * cl_wide_st_chg() - true if the state change is a cluster wide one
  * @mdev:      DRBD device.
@@ -470,7 +537,7 @@ static int cl_wide_st_chg(struct drbd_conf *mdev,
                 ((os.role != R_PRIMARY && ns.role == R_PRIMARY) ||
                  (os.conn != C_STARTING_SYNC_T && ns.conn == C_STARTING_SYNC_T) ||
                  (os.conn != C_STARTING_SYNC_S && ns.conn == C_STARTING_SYNC_S) ||
-                 (os.disk != D_DISKLESS && ns.disk == D_DISKLESS))) ||
+                 (os.disk != D_FAILED && ns.disk == D_FAILED))) ||
                (os.conn >= C_CONNECTED && ns.conn == C_DISCONNECTING) ||
                (os.conn == C_CONNECTED && ns.conn == C_VERIFY_S);
 }
@@ -509,8 +576,16 @@ static enum drbd_state_rv is_valid_state(struct drbd_conf *, union drbd_state);
 static enum drbd_state_rv is_valid_state_transition(struct drbd_conf *,
                                                    union drbd_state,
                                                    union drbd_state);
+enum sanitize_state_warnings {
+       NO_WARNING,
+       ABORTED_ONLINE_VERIFY,
+       ABORTED_RESYNC,
+       CONNECTION_LOST_NEGOTIATING,
+       IMPLICITLY_UPGRADED_DISK,
+       IMPLICITLY_UPGRADED_PDSK,
+};
 static union drbd_state sanitize_state(struct drbd_conf *mdev, union drbd_state os,
-                                      union drbd_state ns, const char **warn_sync_abort);
+                                      union drbd_state ns, enum sanitize_state_warnings *warn);
 int drbd_send_state_req(struct drbd_conf *,
                        union drbd_state, union drbd_state);
 
@@ -785,6 +860,13 @@ is_valid_state_transition(struct drbd_conf *mdev, union drbd_state ns,
        if (ns.conn == os.conn && ns.conn == C_WF_REPORT_PARAMS)
                rv = SS_IN_TRANSIENT_STATE;
 
+       /* While establishing a connection only allow cstate to change.
+          Delay/refuse role changes, detach attach etc... */
+       if (test_bit(STATE_SENT, &mdev->flags) &&
+           !(os.conn == C_WF_REPORT_PARAMS ||
+             (ns.conn == C_WF_REPORT_PARAMS && os.conn == C_WF_CONNECTION)))
+               rv = SS_IN_TRANSIENT_STATE;
+
        if ((ns.conn == C_VERIFY_S || ns.conn == C_VERIFY_T) && os.conn < C_CONNECTED)
                rv = SS_NEED_CONNECTION;
 
@@ -803,6 +885,21 @@ is_valid_state_transition(struct drbd_conf *mdev, union drbd_state ns,
        return rv;
 }
 
+static void print_sanitize_warnings(struct drbd_conf *mdev, enum sanitize_state_warnings warn)
+{
+       static const char *msg_table[] = {
+               [NO_WARNING] = "",
+               [ABORTED_ONLINE_VERIFY] = "Online-verify aborted.",
+               [ABORTED_RESYNC] = "Resync aborted.",
+               [CONNECTION_LOST_NEGOTIATING] = "Connection lost while negotiating, no data!",
+               [IMPLICITLY_UPGRADED_DISK] = "Implicitly upgraded disk",
+               [IMPLICITLY_UPGRADED_PDSK] = "Implicitly upgraded pdsk",
+       };
+
+       if (warn != NO_WARNING)
+               dev_warn(DEV, "%s\n", msg_table[warn]);
+}
+
 /**
  * sanitize_state() - Resolves implicitly necessary additional changes to a state transition
  * @mdev:      DRBD device.
@@ -814,11 +911,14 @@ is_valid_state_transition(struct drbd_conf *mdev, union drbd_state ns,
  * to D_UNKNOWN. This rule and many more along those lines are in this function.
  */
 static union drbd_state sanitize_state(struct drbd_conf *mdev, union drbd_state os,
-                                      union drbd_state ns, const char **warn_sync_abort)
+                                      union drbd_state ns, enum sanitize_state_warnings *warn)
 {
        enum drbd_fencing_p fp;
        enum drbd_disk_state disk_min, disk_max, pdsk_min, pdsk_max;
 
+       if (warn)
+               *warn = NO_WARNING;
+
        fp = FP_DONT_CARE;
        if (get_ldev(mdev)) {
                fp = mdev->ldev->dc.fencing;
@@ -833,18 +933,13 @@ static union drbd_state sanitize_state(struct drbd_conf *mdev, union drbd_state
        /* After a network error (+C_TEAR_DOWN) only C_UNCONNECTED or C_DISCONNECTING can follow.
         * If you try to go into some Sync* state, that shall fail (elsewhere). */
        if (os.conn >= C_TIMEOUT && os.conn <= C_TEAR_DOWN &&
-           ns.conn != C_UNCONNECTED && ns.conn != C_DISCONNECTING && ns.conn <= C_TEAR_DOWN)
+           ns.conn != C_UNCONNECTED && ns.conn != C_DISCONNECTING && ns.conn <= C_CONNECTED)
                ns.conn = os.conn;
 
        /* we cannot fail (again) if we already detached */
        if (ns.disk == D_FAILED && os.disk == D_DISKLESS)
                ns.disk = D_DISKLESS;
 
-       /* if we are only D_ATTACHING yet,
-        * we can (and should) go directly to D_DISKLESS. */
-       if (ns.disk == D_FAILED && os.disk == D_ATTACHING)
-               ns.disk = D_DISKLESS;
-
        /* After C_DISCONNECTING only C_STANDALONE may follow */
        if (os.conn == C_DISCONNECTING && ns.conn != C_STANDALONE)
                ns.conn = os.conn;
@@ -863,10 +958,9 @@ static union drbd_state sanitize_state(struct drbd_conf *mdev, union drbd_state
        /* Abort resync if a disk fails/detaches */
        if (os.conn > C_CONNECTED && ns.conn > C_CONNECTED &&
            (ns.disk <= D_FAILED || ns.pdsk <= D_FAILED)) {
-               if (warn_sync_abort)
-                       *warn_sync_abort =
-                               os.conn == C_VERIFY_S || os.conn == C_VERIFY_T ?
-                               "Online-verify" : "Resync";
+               if (warn)
+                       *warn = os.conn == C_VERIFY_S || os.conn == C_VERIFY_T ?
+                               ABORTED_ONLINE_VERIFY : ABORTED_RESYNC;
                ns.conn = C_CONNECTED;
        }
 
@@ -877,7 +971,8 @@ static union drbd_state sanitize_state(struct drbd_conf *mdev, union drbd_state
                        ns.disk = mdev->new_state_tmp.disk;
                        ns.pdsk = mdev->new_state_tmp.pdsk;
                } else {
-                       dev_alert(DEV, "Connection lost while negotiating, no data!\n");
+                       if (warn)
+                               *warn = CONNECTION_LOST_NEGOTIATING;
                        ns.disk = D_DISKLESS;
                        ns.pdsk = D_UNKNOWN;
                }
@@ -959,16 +1054,16 @@ static union drbd_state sanitize_state(struct drbd_conf *mdev, union drbd_state
                ns.disk = disk_max;
 
        if (ns.disk < disk_min) {
-               dev_warn(DEV, "Implicitly set disk from %s to %s\n",
-                        drbd_disk_str(ns.disk), drbd_disk_str(disk_min));
+               if (warn)
+                       *warn = IMPLICITLY_UPGRADED_DISK;
                ns.disk = disk_min;
        }
        if (ns.pdsk > pdsk_max)
                ns.pdsk = pdsk_max;
 
        if (ns.pdsk < pdsk_min) {
-               dev_warn(DEV, "Implicitly set pdsk from %s to %s\n",
-                        drbd_disk_str(ns.pdsk), drbd_disk_str(pdsk_min));
+               if (warn)
+                       *warn = IMPLICITLY_UPGRADED_PDSK;
                ns.pdsk = pdsk_min;
        }
 
@@ -1045,12 +1140,12 @@ __drbd_set_state(struct drbd_conf *mdev, union drbd_state ns,
 {
        union drbd_state os;
        enum drbd_state_rv rv = SS_SUCCESS;
-       const char *warn_sync_abort = NULL;
+       enum sanitize_state_warnings ssw;
        struct after_state_chg_work *ascw;
 
        os = mdev->state;
 
-       ns = sanitize_state(mdev, os, ns, &warn_sync_abort);
+       ns = sanitize_state(mdev, os, ns, &ssw);
 
        if (ns.i == os.i)
                return SS_NOTHING_TO_DO;
@@ -1076,8 +1171,7 @@ __drbd_set_state(struct drbd_conf *mdev, union drbd_state ns,
                return rv;
        }
 
-       if (warn_sync_abort)
-               dev_warn(DEV, "%s aborted.\n", warn_sync_abort);
+       print_sanitize_warnings(mdev, ssw);
 
        {
        char *pbp, pb[300];
@@ -1243,7 +1337,7 @@ __drbd_set_state(struct drbd_conf *mdev, union drbd_state ns,
                drbd_thread_stop_nowait(&mdev->receiver);
 
        /* Upon network failure, we need to restart the receiver. */
-       if (os.conn > C_TEAR_DOWN &&
+       if (os.conn > C_WF_CONNECTION &&
            ns.conn <= C_TEAR_DOWN && ns.conn >= C_TIMEOUT)
                drbd_thread_restart_nowait(&mdev->receiver);
 
@@ -1251,6 +1345,15 @@ __drbd_set_state(struct drbd_conf *mdev, union drbd_state ns,
        if (os.conn < C_CONNECTED && ns.conn >= C_CONNECTED)
                drbd_resume_al(mdev);
 
+       /* remember last connect and attach times so request_timer_fn() won't
+        * kill newly established sessions while we are still trying to thaw
+        * previously frozen IO */
+       if (os.conn != C_WF_REPORT_PARAMS && ns.conn == C_WF_REPORT_PARAMS)
+               mdev->last_reconnect_jif = jiffies;
+       if ((os.disk == D_ATTACHING || os.disk == D_NEGOTIATING) &&
+           ns.disk > D_NEGOTIATING)
+               mdev->last_reattach_jif = jiffies;
+
        ascw = kmalloc(sizeof(*ascw), GFP_ATOMIC);
        if (ascw) {
                ascw->os = os;
@@ -1354,12 +1457,16 @@ static void after_state_ch(struct drbd_conf *mdev, union drbd_state os,
        /* Here we have the actions that are performed after a
           state change. This function might sleep */
 
+       if (os.disk <= D_NEGOTIATING && ns.disk > D_NEGOTIATING)
+               mod_timer(&mdev->request_timer, jiffies + HZ);
+
        nsm.i = -1;
        if (ns.susp_nod) {
                if (os.conn < C_CONNECTED && ns.conn >= C_CONNECTED)
                        what = resend;
 
-               if (os.disk == D_ATTACHING && ns.disk > D_ATTACHING)
+               if ((os.disk == D_ATTACHING || os.disk == D_NEGOTIATING) &&
+                   ns.disk > D_NEGOTIATING)
                        what = restart_frozen_disk_io;
 
                if (what != nothing)
@@ -1408,7 +1515,7 @@ static void after_state_ch(struct drbd_conf *mdev, union drbd_state os,
        /* Do not change the order of the if above and the two below... */
        if (os.pdsk == D_DISKLESS && ns.pdsk > D_DISKLESS) {      /* attach on the peer */
                drbd_send_uuids(mdev);
-               drbd_send_state(mdev);
+               drbd_send_state(mdev, ns);
        }
        /* No point in queuing send_bitmap if we don't have a connection
         * anymore, so check also the _current_ state, not only the new state
@@ -1441,11 +1548,11 @@ static void after_state_ch(struct drbd_conf *mdev, union drbd_state os,
        }
 
        if (ns.pdsk < D_INCONSISTENT && get_ldev(mdev)) {
-               if (ns.peer == R_PRIMARY && mdev->ldev->md.uuid[UI_BITMAP] == 0) {
+               if (os.peer == R_SECONDARY && ns.peer == R_PRIMARY &&
+                   mdev->ldev->md.uuid[UI_BITMAP] == 0 && ns.disk >= D_UP_TO_DATE) {
                        drbd_uuid_new_current(mdev);
                        drbd_send_uuids(mdev);
                }
-
                /* D_DISKLESS Peer becomes secondary */
                if (os.peer == R_PRIMARY && ns.peer == R_SECONDARY)
                        /* We may still be Primary ourselves.
@@ -1473,14 +1580,14 @@ static void after_state_ch(struct drbd_conf *mdev, union drbd_state os,
            os.disk == D_ATTACHING && ns.disk == D_NEGOTIATING) {
                drbd_send_sizes(mdev, 0, 0);  /* to start sync... */
                drbd_send_uuids(mdev);
-               drbd_send_state(mdev);
+               drbd_send_state(mdev, ns);
        }
 
        /* We want to pause/continue resync, tell peer. */
        if (ns.conn >= C_CONNECTED &&
             ((os.aftr_isp != ns.aftr_isp) ||
              (os.user_isp != ns.user_isp)))
-               drbd_send_state(mdev);
+               drbd_send_state(mdev, ns);
 
        /* In case one of the isp bits got set, suspend other devices. */
        if ((!os.aftr_isp && !os.peer_isp && !os.user_isp) &&
@@ -1490,10 +1597,10 @@ static void after_state_ch(struct drbd_conf *mdev, union drbd_state os,
        /* Make sure the peer gets informed about eventual state
           changes (ISP bits) while we were in WFReportParams. */
        if (os.conn == C_WF_REPORT_PARAMS && ns.conn >= C_CONNECTED)
-               drbd_send_state(mdev);
+               drbd_send_state(mdev, ns);
 
        if (os.conn != C_AHEAD && ns.conn == C_AHEAD)
-               drbd_send_state(mdev);
+               drbd_send_state(mdev, ns);
 
        /* We are in the progress to start a full sync... */
        if ((os.conn != C_STARTING_SYNC_T && ns.conn == C_STARTING_SYNC_T) ||
@@ -1513,33 +1620,38 @@ static void after_state_ch(struct drbd_conf *mdev, union drbd_state os,
        /* first half of local IO error, failure to attach,
         * or administrative detach */
        if (os.disk != D_FAILED && ns.disk == D_FAILED) {
-               enum drbd_io_error_p eh;
-               int was_io_error;
+               enum drbd_io_error_p eh = EP_PASS_ON;
+               int was_io_error = 0;
                /* corresponding get_ldev was in __drbd_set_state, to serialize
-                * our cleanup here with the transition to D_DISKLESS,
-                * so it is safe to dreference ldev here. */
-               eh = mdev->ldev->dc.on_io_error;
-               was_io_error = test_and_clear_bit(WAS_IO_ERROR, &mdev->flags);
-
-               /* current state still has to be D_FAILED,
-                * there is only one way out: to D_DISKLESS,
-                * and that may only happen after our put_ldev below. */
-               if (mdev->state.disk != D_FAILED)
-                       dev_err(DEV,
-                               "ASSERT FAILED: disk is %s during detach\n",
-                               drbd_disk_str(mdev->state.disk));
-
-               if (drbd_send_state(mdev))
-                       dev_warn(DEV, "Notified peer that I am detaching my disk\n");
-               else
-                       dev_err(DEV, "Sending state for detaching disk failed\n");
-
-               drbd_rs_cancel_all(mdev);
-
-               /* In case we want to get something to stable storage still,
-                * this may be the last chance.
-                * Following put_ldev may transition to D_DISKLESS. */
-               drbd_md_sync(mdev);
+                * our cleanup here with the transition to D_DISKLESS.
+                * But is is still not save to dreference ldev here, since
+                * we might come from an failed Attach before ldev was set. */
+               if (mdev->ldev) {
+                       eh = mdev->ldev->dc.on_io_error;
+                       was_io_error = test_and_clear_bit(WAS_IO_ERROR, &mdev->flags);
+
+                       /* Immediately allow completion of all application IO, that waits
+                          for completion from the local disk. */
+                       tl_abort_disk_io(mdev);
+
+                       /* current state still has to be D_FAILED,
+                        * there is only one way out: to D_DISKLESS,
+                        * and that may only happen after our put_ldev below. */
+                       if (mdev->state.disk != D_FAILED)
+                               dev_err(DEV,
+                                       "ASSERT FAILED: disk is %s during detach\n",
+                                       drbd_disk_str(mdev->state.disk));
+
+                       if (ns.conn >= C_CONNECTED)
+                               drbd_send_state(mdev, ns);
+
+                       drbd_rs_cancel_all(mdev);
+
+                       /* In case we want to get something to stable storage still,
+                        * this may be the last chance.
+                        * Following put_ldev may transition to D_DISKLESS. */
+                       drbd_md_sync(mdev);
+               }
                put_ldev(mdev);
 
                if (was_io_error && eh == EP_CALL_HELPER)
@@ -1561,16 +1673,17 @@ static void after_state_ch(struct drbd_conf *mdev, union drbd_state os,
                 mdev->rs_failed = 0;
                 atomic_set(&mdev->rs_pending_cnt, 0);
 
-               if (drbd_send_state(mdev))
-                       dev_warn(DEV, "Notified peer that I'm now diskless.\n");
+               if (ns.conn >= C_CONNECTED)
+                       drbd_send_state(mdev, ns);
+
                /* corresponding get_ldev in __drbd_set_state
                 * this may finally trigger drbd_ldev_destroy. */
                put_ldev(mdev);
        }
 
        /* Notify peer that I had a local IO error, and did not detached.. */
-       if (os.disk == D_UP_TO_DATE && ns.disk == D_INCONSISTENT)
-               drbd_send_state(mdev);
+       if (os.disk == D_UP_TO_DATE && ns.disk == D_INCONSISTENT && ns.conn >= C_CONNECTED)
+               drbd_send_state(mdev, ns);
 
        /* Disks got bigger while they were detached */
        if (ns.disk > D_NEGOTIATING && ns.pdsk > D_NEGOTIATING &&
@@ -1588,7 +1701,13 @@ static void after_state_ch(struct drbd_conf *mdev, union drbd_state os,
        /* sync target done with resync.  Explicitly notify peer, even though
         * it should (at least for non-empty resyncs) already know itself. */
        if (os.disk < D_UP_TO_DATE && os.conn >= C_SYNC_SOURCE && ns.conn == C_CONNECTED)
-               drbd_send_state(mdev);
+               drbd_send_state(mdev, ns);
+
+       /* Wake up role changes, that were delayed because of connection establishing */
+       if (os.conn == C_WF_REPORT_PARAMS && ns.conn != C_WF_REPORT_PARAMS) {
+               clear_bit(STATE_SENT, &mdev->flags);
+               wake_up(&mdev->state_wait);
+       }
 
        /* This triggers bitmap writeout of potentially still unwritten pages
         * if the resync finished cleanly, or aborted because of peer disk
@@ -1598,8 +1717,8 @@ static void after_state_ch(struct drbd_conf *mdev, union drbd_state os,
         * No harm done if some bits change during this phase.
         */
        if (os.conn > C_CONNECTED && ns.conn <= C_CONNECTED && get_ldev(mdev)) {
-               drbd_queue_bitmap_io(mdev, &drbd_bm_write, NULL,
-                       "write from resync_finished", BM_LOCKED_SET_ALLOWED);
+               drbd_queue_bitmap_io(mdev, &drbd_bm_write_copy_pages, NULL,
+                       "write from resync_finished", BM_LOCKED_CHANGE_ALLOWED);
                put_ldev(mdev);
        }
 
@@ -2057,7 +2176,11 @@ int drbd_gen_and_send_sync_uuid(struct drbd_conf *mdev)
 
        D_ASSERT(mdev->state.disk == D_UP_TO_DATE);
 
-       uuid = mdev->ldev->md.uuid[UI_BITMAP] + UUID_NEW_BM_OFFSET;
+       uuid = mdev->ldev->md.uuid[UI_BITMAP];
+       if (uuid && uuid != UUID_JUST_CREATED)
+               uuid = uuid + UUID_NEW_BM_OFFSET;
+       else
+               get_random_bytes(&uuid, sizeof(u64));
        drbd_uuid_set(mdev, UI_BITMAP, uuid);
        drbd_print_uuids(mdev, "updated sync UUID");
        drbd_md_sync(mdev);
@@ -2089,6 +2212,10 @@ int drbd_send_sizes(struct drbd_conf *mdev, int trigger_reply, enum dds_flags fl
                max_bio_size = DRBD_MAX_BIO_SIZE; /* ... multiple BIOs per peer_request */
        }
 
+       /* Never allow old drbd (up to 8.3.7) to see more than 32KiB */
+       if (mdev->agreed_pro_version <= 94)
+               max_bio_size = min_t(int, max_bio_size, DRBD_MAX_SIZE_H80_PACKET);
+
        p.d_size = cpu_to_be64(d_size);
        p.u_size = cpu_to_be64(u_size);
        p.c_size = cpu_to_be64(trigger_reply ? 0 : drbd_get_capacity(mdev->this_bdev));
@@ -2102,10 +2229,10 @@ int drbd_send_sizes(struct drbd_conf *mdev, int trigger_reply, enum dds_flags fl
 }
 
 /**
- * drbd_send_state() - Sends the drbd state to the peer
+ * drbd_send_current_state() - Sends the drbd state to the peer
  * @mdev:      DRBD device.
  */
-int drbd_send_state(struct drbd_conf *mdev)
+int drbd_send_current_state(struct drbd_conf *mdev)
 {
        struct socket *sock;
        struct p_state p;
@@ -2131,6 +2258,37 @@ int drbd_send_state(struct drbd_conf *mdev)
        return ok;
 }
 
+/**
+ * drbd_send_state() - After a state change, sends the new state to the peer
+ * @mdev:      DRBD device.
+ * @state:     the state to send, not necessarily the current state.
+ *
+ * Each state change queues an "after_state_ch" work, which will eventually
+ * send the resulting new state to the peer. If more state changes happen
+ * between queuing and processing of the after_state_ch work, we still
+ * want to send each intermediary state in the order it occurred.
+ */
+int drbd_send_state(struct drbd_conf *mdev, union drbd_state state)
+{
+       struct socket *sock;
+       struct p_state p;
+       int ok = 0;
+
+       mutex_lock(&mdev->data.mutex);
+
+       p.state = cpu_to_be32(state.i);
+       sock = mdev->data.socket;
+
+       if (likely(sock != NULL)) {
+               ok = _drbd_send_cmd(mdev, sock, P_STATE,
+                                   (struct p_header80 *)&p, sizeof(p), 0);
+       }
+
+       mutex_unlock(&mdev->data.mutex);
+
+       return ok;
+}
+
 int drbd_send_state_req(struct drbd_conf *mdev,
        union drbd_state mask, union drbd_state val)
 {
@@ -2615,7 +2773,7 @@ static int _drbd_send_bio(struct drbd_conf *mdev, struct bio *bio)
        struct bio_vec *bvec;
        int i;
        /* hint all but last page with MSG_MORE */
-       __bio_for_each_segment(bvec, bio, i, 0) {
+       bio_for_each_segment(bvec, bio, i) {
                if (!_drbd_no_send_page(mdev, bvec->bv_page,
                                     bvec->bv_offset, bvec->bv_len,
                                     i == bio->bi_vcnt -1 ? 0 : MSG_MORE))
@@ -2629,7 +2787,7 @@ static int _drbd_send_zc_bio(struct drbd_conf *mdev, struct bio *bio)
        struct bio_vec *bvec;
        int i;
        /* hint all but last page with MSG_MORE */
-       __bio_for_each_segment(bvec, bio, i, 0) {
+       bio_for_each_segment(bvec, bio, i) {
                if (!_drbd_send_page(mdev, bvec->bv_page,
                                     bvec->bv_offset, bvec->bv_len,
                                     i == bio->bi_vcnt -1 ? 0 : MSG_MORE))
@@ -2695,8 +2853,7 @@ int drbd_send_dblock(struct drbd_conf *mdev, struct drbd_request *req)
 
        p.sector   = cpu_to_be64(req->sector);
        p.block_id = (unsigned long)req;
-       p.seq_num  = cpu_to_be32(req->seq_num =
-                                atomic_add_return(1, &mdev->packet_seq));
+       p.seq_num  = cpu_to_be32(atomic_add_return(1, &mdev->packet_seq));
 
        dp_flags = bio_flags_to_wire(mdev, req->master_bio->bi_rw);
 
@@ -2987,8 +3144,8 @@ void drbd_init_set_defaults(struct drbd_conf *mdev)
        atomic_set(&mdev->rs_sect_in, 0);
        atomic_set(&mdev->rs_sect_ev, 0);
        atomic_set(&mdev->ap_in_flight, 0);
+       atomic_set(&mdev->md_io_in_use, 0);
 
-       mutex_init(&mdev->md_io_mutex);
        mutex_init(&mdev->data.mutex);
        mutex_init(&mdev->meta.mutex);
        sema_init(&mdev->data.work.s, 0);
@@ -3126,6 +3283,10 @@ static void drbd_destroy_mempools(void)
 
        /* D_ASSERT(atomic_read(&drbd_pp_vacant)==0); */
 
+       if (drbd_md_io_bio_set)
+               bioset_free(drbd_md_io_bio_set);
+       if (drbd_md_io_page_pool)
+               mempool_destroy(drbd_md_io_page_pool);
        if (drbd_ee_mempool)
                mempool_destroy(drbd_ee_mempool);
        if (drbd_request_mempool)
@@ -3139,6 +3300,8 @@ static void drbd_destroy_mempools(void)
        if (drbd_al_ext_cache)
                kmem_cache_destroy(drbd_al_ext_cache);
 
+       drbd_md_io_bio_set   = NULL;
+       drbd_md_io_page_pool = NULL;
        drbd_ee_mempool      = NULL;
        drbd_request_mempool = NULL;
        drbd_ee_cache        = NULL;
@@ -3162,6 +3325,8 @@ static int drbd_create_mempools(void)
        drbd_bm_ext_cache    = NULL;
        drbd_al_ext_cache    = NULL;
        drbd_pp_pool         = NULL;
+       drbd_md_io_page_pool = NULL;
+       drbd_md_io_bio_set   = NULL;
 
        /* caches */
        drbd_request_cache = kmem_cache_create(
@@ -3185,6 +3350,16 @@ static int drbd_create_mempools(void)
                goto Enomem;
 
        /* mempools */
+#ifdef COMPAT_HAVE_BIOSET_CREATE
+       drbd_md_io_bio_set = bioset_create(DRBD_MIN_POOL_PAGES, 0);
+       if (drbd_md_io_bio_set == NULL)
+               goto Enomem;
+#endif
+
+       drbd_md_io_page_pool = mempool_create_page_pool(DRBD_MIN_POOL_PAGES, 0);
+       if (drbd_md_io_page_pool == NULL)
+               goto Enomem;
+
        drbd_request_mempool = mempool_create(number,
                mempool_alloc_slab, mempool_free_slab, drbd_request_cache);
        if (drbd_request_mempool == NULL)
@@ -3262,6 +3437,8 @@ static void drbd_delete_device(unsigned int minor)
        if (!mdev)
                return;
 
+       del_timer_sync(&mdev->request_timer);
+
        /* paranoia asserts */
        if (mdev->open_cnt != 0)
                dev_err(DEV, "open_cnt = %d in %s:%u", mdev->open_cnt,
@@ -3666,8 +3843,10 @@ void drbd_md_sync(struct drbd_conf *mdev)
        if (!get_ldev_if_state(mdev, D_FAILED))
                return;
 
-       mutex_lock(&mdev->md_io_mutex);
-       buffer = (struct meta_data_on_disk *)page_address(mdev->md_io_page);
+       buffer = drbd_md_get_buffer(mdev);
+       if (!buffer)
+               goto out;
+
        memset(buffer, 0, 512);
 
        buffer->la_size = cpu_to_be64(drbd_get_capacity(mdev->this_bdev));
@@ -3698,7 +3877,8 @@ void drbd_md_sync(struct drbd_conf *mdev)
         * since we updated it on metadata. */
        mdev->ldev->md.la_size_sect = drbd_get_capacity(mdev->this_bdev);
 
-       mutex_unlock(&mdev->md_io_mutex);
+       drbd_md_put_buffer(mdev);
+out:
        put_ldev(mdev);
 }
 
@@ -3718,8 +3898,9 @@ int drbd_md_read(struct drbd_conf *mdev, struct drbd_backing_dev *bdev)
        if (!get_ldev_if_state(mdev, D_ATTACHING))
                return ERR_IO_MD_DISK;
 
-       mutex_lock(&mdev->md_io_mutex);
-       buffer = (struct meta_data_on_disk *)page_address(mdev->md_io_page);
+       buffer = drbd_md_get_buffer(mdev);
+       if (!buffer)
+               goto out;
 
        if (!drbd_md_sync_page_io(mdev, bdev, bdev->md.md_offset, READ)) {
                /* NOTE: can't do normal error processing here as this is
@@ -3780,7 +3961,8 @@ int drbd_md_read(struct drbd_conf *mdev, struct drbd_backing_dev *bdev)
                mdev->sync_conf.al_extents = 127;
 
  err:
-       mutex_unlock(&mdev->md_io_mutex);
+       drbd_md_put_buffer(mdev);
+ out:
        put_ldev(mdev);
 
        return rv;
@@ -4183,12 +4365,11 @@ const char *drbd_buildtag(void)
        static char buildtag[38] = "\0uilt-in";
 
        if (buildtag[0] == 0) {
-#ifdef CONFIG_MODULES
-               if (THIS_MODULE != NULL)
-                       sprintf(buildtag, "srcversion: %-24s", THIS_MODULE->srcversion);
-               else
+#ifdef MODULE
+               sprintf(buildtag, "srcversion: %-24s", THIS_MODULE->srcversion);
+#else
+               buildtag[0] = 'b';
 #endif
-                       buildtag[0] = 'b';
        }
 
        return buildtag;
index 946166e13953cca1d6c64d3feb9757d60690922d..6d4de6a72e8069018b2b4d050b7ef2aa8b259259 100644 (file)
@@ -289,7 +289,7 @@ static int _try_outdate_peer_async(void *data)
        */
        spin_lock_irq(&mdev->req_lock);
        ns = mdev->state;
-       if (ns.conn < C_WF_REPORT_PARAMS) {
+       if (ns.conn < C_WF_REPORT_PARAMS && !test_bit(STATE_SENT, &mdev->flags)) {
                ns.pdsk = nps;
                _drbd_set_state(mdev, ns, CS_VERBOSE, NULL);
        }
@@ -432,7 +432,7 @@ drbd_set_role(struct drbd_conf *mdev, enum drbd_role new_role, int force)
                /* if this was forced, we should consider sync */
                if (forced)
                        drbd_send_uuids(mdev);
-               drbd_send_state(mdev);
+               drbd_send_current_state(mdev);
        }
 
        drbd_md_sync(mdev);
@@ -845,9 +845,10 @@ void drbd_reconsider_max_bio_size(struct drbd_conf *mdev)
           Because new from 8.3.8 onwards the peer can use multiple
           BIOs for a single peer_request */
        if (mdev->state.conn >= C_CONNECTED) {
-               if (mdev->agreed_pro_version < 94)
-                       peer = mdev->peer_max_bio_size;
-               else if (mdev->agreed_pro_version == 94)
+               if (mdev->agreed_pro_version < 94) {
+                       peer = min_t(int, mdev->peer_max_bio_size, DRBD_MAX_SIZE_H80_PACKET);
+                       /* Correct old drbd (up to 8.3.7) if it believes it can do more than 32KiB */
+               } else if (mdev->agreed_pro_version == 94)
                        peer = DRBD_MAX_SIZE_H80_PACKET;
                else /* drbd 8.3.8 onwards */
                        peer = DRBD_MAX_BIO_SIZE;
@@ -1032,7 +1033,7 @@ static int drbd_nl_disk_conf(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp
                dev_err(DEV, "max capacity %llu smaller than disk size %llu\n",
                        (unsigned long long) drbd_get_max_capacity(nbc),
                        (unsigned long long) nbc->dc.disk_size);
-               retcode = ERR_DISK_TO_SMALL;
+               retcode = ERR_DISK_TOO_SMALL;
                goto fail;
        }
 
@@ -1046,7 +1047,7 @@ static int drbd_nl_disk_conf(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp
        }
 
        if (drbd_get_capacity(nbc->md_bdev) < min_md_device_sectors) {
-               retcode = ERR_MD_DISK_TO_SMALL;
+               retcode = ERR_MD_DISK_TOO_SMALL;
                dev_warn(DEV, "refusing attach: md-device too small, "
                     "at least %llu sectors needed for this meta-disk type\n",
                     (unsigned long long) min_md_device_sectors);
@@ -1057,7 +1058,7 @@ static int drbd_nl_disk_conf(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp
         * (we may currently be R_PRIMARY with no local disk...) */
        if (drbd_get_max_capacity(nbc) <
            drbd_get_capacity(mdev->this_bdev)) {
-               retcode = ERR_DISK_TO_SMALL;
+               retcode = ERR_DISK_TOO_SMALL;
                goto fail;
        }
 
@@ -1138,7 +1139,7 @@ static int drbd_nl_disk_conf(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp
        if (drbd_md_test_flag(nbc, MDF_CONSISTENT) &&
            drbd_new_dev_size(mdev, nbc, 0) < nbc->md.la_size_sect) {
                dev_warn(DEV, "refusing to truncate a consistent device\n");
-               retcode = ERR_DISK_TO_SMALL;
+               retcode = ERR_DISK_TOO_SMALL;
                goto force_diskless_dec;
        }
 
@@ -1336,17 +1337,34 @@ static int drbd_nl_detach(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
 {
        enum drbd_ret_code retcode;
        int ret;
+       struct detach dt = {};
+
+       if (!detach_from_tags(mdev, nlp->tag_list, &dt)) {
+               reply->ret_code = ERR_MANDATORY_TAG;
+               goto out;
+       }
+
+       if (dt.detach_force) {
+               drbd_force_state(mdev, NS(disk, D_FAILED));
+               reply->ret_code = SS_SUCCESS;
+               goto out;
+       }
+
        drbd_suspend_io(mdev); /* so no-one is stuck in drbd_al_begin_io */
+       drbd_md_get_buffer(mdev); /* make sure there is no in-flight meta-data IO */
        retcode = drbd_request_state(mdev, NS(disk, D_FAILED));
+       drbd_md_put_buffer(mdev);
        /* D_FAILED will transition to DISKLESS. */
        ret = wait_event_interruptible(mdev->misc_wait,
                        mdev->state.disk != D_FAILED);
        drbd_resume_io(mdev);
+
        if ((int)retcode == (int)SS_IS_DISKLESS)
                retcode = SS_NOTHING_TO_DO;
        if (ret)
                retcode = ERR_INTR;
        reply->ret_code = retcode;
+out:
        return 0;
 }
 
@@ -1711,7 +1729,7 @@ static int drbd_nl_resize(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
 
        if (rs.no_resync && mdev->agreed_pro_version < 93) {
                retcode = ERR_NEED_APV_93;
-               goto fail;
+               goto fail_ldev;
        }
 
        if (mdev->ldev->known_size != drbd_get_capacity(mdev->ldev->backing_bdev))
@@ -1738,6 +1756,10 @@ static int drbd_nl_resize(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
  fail:
        reply->ret_code = retcode;
        return 0;
+
+ fail_ldev:
+       put_ldev(mdev);
+       goto fail;
 }
 
 static int drbd_nl_syncer_conf(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
@@ -1941,6 +1963,7 @@ static int drbd_nl_invalidate(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nl
 
        /* If there is still bitmap IO pending, probably because of a previous
         * resync just being finished, wait for it before requesting a new resync. */
+       drbd_suspend_io(mdev);
        wait_event(mdev->misc_wait, !test_bit(BITMAP_IO, &mdev->flags));
 
        retcode = _drbd_request_state(mdev, NS(conn, C_STARTING_SYNC_T), CS_ORDERED);
@@ -1959,6 +1982,7 @@ static int drbd_nl_invalidate(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nl
 
                retcode = drbd_request_state(mdev, NS(conn, C_STARTING_SYNC_T));
        }
+       drbd_resume_io(mdev);
 
        reply->ret_code = retcode;
        return 0;
@@ -1980,6 +2004,7 @@ static int drbd_nl_invalidate_peer(struct drbd_conf *mdev, struct drbd_nl_cfg_re
 
        /* If there is still bitmap IO pending, probably because of a previous
         * resync just being finished, wait for it before requesting a new resync. */
+       drbd_suspend_io(mdev);
        wait_event(mdev->misc_wait, !test_bit(BITMAP_IO, &mdev->flags));
 
        retcode = _drbd_request_state(mdev, NS(conn, C_STARTING_SYNC_S), CS_ORDERED);
@@ -1998,6 +2023,7 @@ static int drbd_nl_invalidate_peer(struct drbd_conf *mdev, struct drbd_nl_cfg_re
                } else
                        retcode = drbd_request_state(mdev, NS(conn, C_STARTING_SYNC_S));
        }
+       drbd_resume_io(mdev);
 
        reply->ret_code = retcode;
        return 0;
@@ -2170,11 +2196,13 @@ static int drbd_nl_start_ov(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
 
        /* If there is still bitmap IO pending, e.g. previous resync or verify
         * just being finished, wait for it before requesting a new resync. */
+       drbd_suspend_io(mdev);
        wait_event(mdev->misc_wait, !test_bit(BITMAP_IO, &mdev->flags));
 
        /* w_make_ov_request expects position to be aligned */
        mdev->ov_start_sector = args.start_sector & ~BM_SECT_PER_BIT;
        reply->ret_code = drbd_request_state(mdev,NS(conn,C_VERIFY_S));
+       drbd_resume_io(mdev);
        return 0;
 }
 
index 2959cdfb77f556e0bed2a8131eb69c69cbfed84f..869bada2ed06838a656d431584afde516a9cd115 100644 (file)
@@ -52,7 +52,7 @@ void seq_printf_with_thousands_grouping(struct seq_file *seq, long v)
        if (unlikely(v >= 1000000)) {
                /* cool: > GiByte/s */
                seq_printf(seq, "%ld,", v / 1000000);
-               v /= 1000000;
+               v %= 1000000;
                seq_printf(seq, "%03ld,%03ld", v/1000, v % 1000);
        } else if (likely(v >= 1000))
                seq_printf(seq, "%ld,%03ld", v/1000, v % 1000);
index 436f519bed1c4190168d3f847a9dc692153ca89a..ea4836e0ae9829e12206e482cc50b70678a3e4aa 100644 (file)
@@ -466,6 +466,7 @@ static int drbd_accept(struct drbd_conf *mdev, const char **what,
                goto out;
        }
        (*newsock)->ops  = sock->ops;
+       __module_get((*newsock)->ops->owner);
 
 out:
        return err;
@@ -750,6 +751,7 @@ static int drbd_connect(struct drbd_conf *mdev)
 {
        struct socket *s, *sock, *msock;
        int try, h, ok;
+       enum drbd_state_rv rv;
 
        D_ASSERT(!mdev->data.socket);
 
@@ -888,25 +890,32 @@ retry:
                }
        }
 
-       if (drbd_request_state(mdev, NS(conn, C_WF_REPORT_PARAMS)) < SS_SUCCESS)
-               return 0;
-
        sock->sk->sk_sndtimeo = mdev->net_conf->timeout*HZ/10;
        sock->sk->sk_rcvtimeo = MAX_SCHEDULE_TIMEOUT;
 
        atomic_set(&mdev->packet_seq, 0);
        mdev->peer_seq = 0;
 
-       drbd_thread_start(&mdev->asender);
-
        if (drbd_send_protocol(mdev) == -1)
                return -1;
+       set_bit(STATE_SENT, &mdev->flags);
        drbd_send_sync_param(mdev, &mdev->sync_conf);
        drbd_send_sizes(mdev, 0, 0);
        drbd_send_uuids(mdev);
-       drbd_send_state(mdev);
+       drbd_send_current_state(mdev);
        clear_bit(USE_DEGR_WFC_T, &mdev->flags);
        clear_bit(RESIZE_PENDING, &mdev->flags);
+
+       spin_lock_irq(&mdev->req_lock);
+       rv = _drbd_set_state(_NS(mdev, conn, C_WF_REPORT_PARAMS), CS_VERBOSE, NULL);
+       if (mdev->state.conn != C_WF_REPORT_PARAMS)
+               clear_bit(STATE_SENT, &mdev->flags);
+       spin_unlock_irq(&mdev->req_lock);
+
+       if (rv < SS_SUCCESS)
+               return 0;
+
+       drbd_thread_start(&mdev->asender);
        mod_timer(&mdev->request_timer, jiffies + HZ); /* just start it here. */
 
        return 1;
@@ -957,7 +966,7 @@ static void drbd_flush(struct drbd_conf *mdev)
                rv = blkdev_issue_flush(mdev->ldev->backing_bdev, GFP_KERNEL,
                                        NULL);
                if (rv) {
-                       dev_err(DEV, "local disk flush failed with status %d\n", rv);
+                       dev_info(DEV, "local disk flush failed with status %d\n", rv);
                        /* would rather check on EOPNOTSUPP, but that is not reliable.
                         * don't try again for ANY return value != 0
                         * if (rv == -EOPNOTSUPP) */
@@ -1001,13 +1010,14 @@ static enum finish_epoch drbd_may_finish_epoch(struct drbd_conf *mdev,
 
                if (epoch_size != 0 &&
                    atomic_read(&epoch->active) == 0 &&
-                   test_bit(DE_HAVE_BARRIER_NUMBER, &epoch->flags)) {
+                   (test_bit(DE_HAVE_BARRIER_NUMBER, &epoch->flags) || ev & EV_CLEANUP)) {
                        if (!(ev & EV_CLEANUP)) {
                                spin_unlock(&mdev->epoch_lock);
                                drbd_send_b_ack(mdev, epoch->barrier_nr, epoch_size);
                                spin_lock(&mdev->epoch_lock);
                        }
-                       dec_unacked(mdev);
+                       if (test_bit(DE_HAVE_BARRIER_NUMBER, &epoch->flags))
+                               dec_unacked(mdev);
 
                        if (mdev->current_epoch != epoch) {
                                next_epoch = list_entry(epoch->list.next, struct drbd_epoch, list);
@@ -1096,7 +1106,11 @@ int drbd_submit_ee(struct drbd_conf *mdev, struct drbd_epoch_entry *e,
        /* In most cases, we will only need one bio.  But in case the lower
         * level restrictions happen to be different at this offset on this
         * side than those of the sending peer, we may need to submit the
-        * request in more than one bio. */
+        * request in more than one bio.
+        *
+        * Plain bio_alloc is good enough here, this is no DRBD internally
+        * generated bio, but a bio allocated on behalf of the peer.
+        */
 next_bio:
        bio = bio_alloc(GFP_NOIO, nr_pages);
        if (!bio) {
@@ -1583,6 +1597,24 @@ static int e_send_discard_ack(struct drbd_conf *mdev, struct drbd_work *w, int u
        return ok;
 }
 
+static bool overlapping_resync_write(struct drbd_conf *mdev, struct drbd_epoch_entry *data_e)
+{
+
+       struct drbd_epoch_entry *rs_e;
+       bool rv = 0;
+
+       spin_lock_irq(&mdev->req_lock);
+       list_for_each_entry(rs_e, &mdev->sync_ee, w.list) {
+               if (overlaps(data_e->sector, data_e->size, rs_e->sector, rs_e->size)) {
+                       rv = 1;
+                       break;
+               }
+       }
+       spin_unlock_irq(&mdev->req_lock);
+
+       return rv;
+}
+
 /* Called from receive_Data.
  * Synchronize packets on sock with packets on msock.
  *
@@ -1826,6 +1858,9 @@ static int receive_Data(struct drbd_conf *mdev, enum drbd_packets cmd, unsigned
        list_add(&e->w.list, &mdev->active_ee);
        spin_unlock_irq(&mdev->req_lock);
 
+       if (mdev->state.conn == C_SYNC_TARGET)
+               wait_event(mdev->ee_wait, !overlapping_resync_write(mdev, e));
+
        switch (mdev->net_conf->wire_protocol) {
        case DRBD_PROT_C:
                inc_unacked(mdev);
@@ -2420,7 +2455,7 @@ static int drbd_uuid_compare(struct drbd_conf *mdev, int *rule_nr) __must_hold(l
                        mdev->p_uuid[UI_BITMAP] = mdev->p_uuid[UI_HISTORY_START];
                        mdev->p_uuid[UI_HISTORY_START] = mdev->p_uuid[UI_HISTORY_START + 1];
 
-                       dev_info(DEV, "Did not got last syncUUID packet, corrected:\n");
+                       dev_info(DEV, "Lost last syncUUID packet, corrected:\n");
                        drbd_uuid_dump(mdev, "peer", mdev->p_uuid, mdev->p_uuid[UI_SIZE], mdev->p_uuid[UI_FLAGS]);
 
                        return -1;
@@ -2806,10 +2841,10 @@ static int receive_SyncParam(struct drbd_conf *mdev, enum drbd_packets cmd, unsi
 
        if (apv >= 88) {
                if (apv == 88) {
-                       if (data_size > SHARED_SECRET_MAX) {
-                               dev_err(DEV, "verify-alg too long, "
-                                   "peer wants %u, accepting only %u byte\n",
-                                               data_size, SHARED_SECRET_MAX);
+                       if (data_size > SHARED_SECRET_MAX || data_size == 0) {
+                               dev_err(DEV, "verify-alg of wrong size, "
+                                       "peer wants %u, accepting only up to %u byte\n",
+                                       data_size, SHARED_SECRET_MAX);
                                return false;
                        }
 
@@ -3168,9 +3203,20 @@ static int receive_state(struct drbd_conf *mdev, enum drbd_packets cmd, unsigned
        os = ns = mdev->state;
        spin_unlock_irq(&mdev->req_lock);
 
-       /* peer says his disk is uptodate, while we think it is inconsistent,
-        * and this happens while we think we have a sync going on. */
-       if (os.pdsk == D_INCONSISTENT && real_peer_disk == D_UP_TO_DATE &&
+       /* If some other part of the code (asender thread, timeout)
+        * already decided to close the connection again,
+        * we must not "re-establish" it here. */
+       if (os.conn <= C_TEAR_DOWN)
+               return false;
+
+       /* If this is the "end of sync" confirmation, usually the peer disk
+        * transitions from D_INCONSISTENT to D_UP_TO_DATE. For empty (0 bits
+        * set) resync started in PausedSyncT, or if the timing of pause-/
+        * unpause-sync events has been "just right", the peer disk may
+        * transition from D_CONSISTENT to D_UP_TO_DATE as well.
+        */
+       if ((os.pdsk == D_INCONSISTENT || os.pdsk == D_CONSISTENT) &&
+           real_peer_disk == D_UP_TO_DATE &&
            os.conn > C_CONNECTED && os.disk == D_UP_TO_DATE) {
                /* If we are (becoming) SyncSource, but peer is still in sync
                 * preparation, ignore its uptodate-ness to avoid flapping, it
@@ -3288,7 +3334,7 @@ static int receive_state(struct drbd_conf *mdev, enum drbd_packets cmd, unsigned
                        /* Nowadays only used when forcing a node into primary role and
                           setting its disk to UpToDate with that */
                        drbd_send_uuids(mdev);
-                       drbd_send_state(mdev);
+                       drbd_send_current_state(mdev);
                }
        }
 
@@ -3776,6 +3822,13 @@ static void drbd_disconnect(struct drbd_conf *mdev)
        if (mdev->state.conn == C_STANDALONE)
                return;
 
+       /* We are about to start the cleanup after connection loss.
+        * Make sure drbd_make_request knows about that.
+        * Usually we should be in some network failure state already,
+        * but just in case we are not, we fix it up here.
+        */
+       drbd_force_state(mdev, NS(conn, C_NETWORK_FAILURE));
+
        /* asender does not clean up anything. it must not interfere, either */
        drbd_thread_stop(&mdev->asender);
        drbd_free_sock(mdev);
@@ -3803,8 +3856,6 @@ static void drbd_disconnect(struct drbd_conf *mdev)
        atomic_set(&mdev->rs_pending_cnt, 0);
        wake_up(&mdev->misc_wait);
 
-       del_timer(&mdev->request_timer);
-
        /* make sure syncer is stopped and w_resume_next_sg queued */
        del_timer_sync(&mdev->resync_timer);
        resync_timer_fn((unsigned long)mdev);
@@ -4433,7 +4484,7 @@ static int got_BarrierAck(struct drbd_conf *mdev, struct p_header80 *h)
 
        if (mdev->state.conn == C_AHEAD &&
            atomic_read(&mdev->ap_in_flight) == 0 &&
-           !test_and_set_bit(AHEAD_TO_SYNC_SOURCE, &mdev->current_epoch->flags)) {
+           !test_and_set_bit(AHEAD_TO_SYNC_SOURCE, &mdev->flags)) {
                mdev->start_resync_timer.expires = jiffies + HZ;
                add_timer(&mdev->start_resync_timer);
        }
index 4a0f314086e522f7c6172355d3c0c3b1d757c2ec..9c5c84946b056792fa45d99051bd5c28750db7e1 100644 (file)
@@ -37,6 +37,7 @@ static void _drbd_start_io_acct(struct drbd_conf *mdev, struct drbd_request *req
        const int rw = bio_data_dir(bio);
        int cpu;
        cpu = part_stat_lock();
+       part_round_stats(cpu, &mdev->vdisk->part0);
        part_stat_inc(cpu, &mdev->vdisk->part0, ios[rw]);
        part_stat_add(cpu, &mdev->vdisk->part0, sectors[rw], bio_sectors(bio));
        part_inc_in_flight(&mdev->vdisk->part0, rw);
@@ -214,8 +215,7 @@ void _req_may_be_done(struct drbd_request *req, struct bio_and_error *m)
 {
        const unsigned long s = req->rq_state;
        struct drbd_conf *mdev = req->mdev;
-       /* only WRITES may end up here without a master bio (on barrier ack) */
-       int rw = req->master_bio ? bio_data_dir(req->master_bio) : WRITE;
+       int rw = req->rq_state & RQ_WRITE ? WRITE : READ;
 
        /* we must not complete the master bio, while it is
         *      still being processed by _drbd_send_zc_bio (drbd_send_dblock)
@@ -230,7 +230,7 @@ void _req_may_be_done(struct drbd_request *req, struct bio_and_error *m)
                return;
        if (s & RQ_NET_PENDING)
                return;
-       if (s & RQ_LOCAL_PENDING)
+       if (s & RQ_LOCAL_PENDING && !(s & RQ_LOCAL_ABORTED))
                return;
 
        if (req->master_bio) {
@@ -277,6 +277,9 @@ void _req_may_be_done(struct drbd_request *req, struct bio_and_error *m)
                req->master_bio = NULL;
        }
 
+       if (s & RQ_LOCAL_PENDING)
+               return;
+
        if ((s & RQ_NET_MASK) == 0 || (s & RQ_NET_DONE)) {
                /* this is disconnected (local only) operation,
                 * or protocol C P_WRITE_ACK,
@@ -429,7 +432,7 @@ int __req_mod(struct drbd_request *req, enum drbd_req_event what,
                break;
 
        case completed_ok:
-               if (bio_data_dir(req->master_bio) == WRITE)
+               if (req->rq_state & RQ_WRITE)
                        mdev->writ_cnt += req->size>>9;
                else
                        mdev->read_cnt += req->size>>9;
@@ -438,7 +441,14 @@ int __req_mod(struct drbd_request *req, enum drbd_req_event what,
                req->rq_state &= ~RQ_LOCAL_PENDING;
 
                _req_may_be_done_not_susp(req, m);
-               put_ldev(mdev);
+               break;
+
+       case abort_disk_io:
+               req->rq_state |= RQ_LOCAL_ABORTED;
+               if (req->rq_state & RQ_WRITE)
+                       _req_may_be_done_not_susp(req, m);
+               else
+                       goto goto_queue_for_net_read;
                break;
 
        case write_completed_with_error:
@@ -447,7 +457,6 @@ int __req_mod(struct drbd_request *req, enum drbd_req_event what,
 
                __drbd_chk_io_error(mdev, false);
                _req_may_be_done_not_susp(req, m);
-               put_ldev(mdev);
                break;
 
        case read_ahead_completed_with_error:
@@ -455,7 +464,6 @@ int __req_mod(struct drbd_request *req, enum drbd_req_event what,
                req->rq_state |= RQ_LOCAL_COMPLETED;
                req->rq_state &= ~RQ_LOCAL_PENDING;
                _req_may_be_done_not_susp(req, m);
-               put_ldev(mdev);
                break;
 
        case read_completed_with_error:
@@ -467,7 +475,8 @@ int __req_mod(struct drbd_request *req, enum drbd_req_event what,
                D_ASSERT(!(req->rq_state & RQ_NET_MASK));
 
                __drbd_chk_io_error(mdev, false);
-               put_ldev(mdev);
+
+       goto_queue_for_net_read:
 
                /* no point in retrying if there is no good remote data,
                 * or we have no connection. */
@@ -556,10 +565,8 @@ int __req_mod(struct drbd_request *req, enum drbd_req_event what,
                drbd_queue_work(&mdev->data.work, &req->w);
                break;
 
-       case oos_handed_to_network:
-               /* actually the same */
+       case read_retry_remote_canceled:
        case send_canceled:
-               /* treat it the same */
        case send_failed:
                /* real cleanup will be done from tl_clear.  just update flags
                 * so it is no longer marked as on the worker queue */
@@ -589,17 +596,17 @@ int __req_mod(struct drbd_request *req, enum drbd_req_event what,
                }
                req->rq_state &= ~RQ_NET_QUEUED;
                req->rq_state |= RQ_NET_SENT;
-               /* because _drbd_send_zc_bio could sleep, and may want to
-                * dereference the bio even after the "write_acked_by_peer" and
-                * "completed_ok" events came in, once we return from
-                * _drbd_send_zc_bio (drbd_send_dblock), we have to check
-                * whether it is done already, and end it.  */
                _req_may_be_done_not_susp(req, m);
                break;
 
-       case read_retry_remote_canceled:
+       case oos_handed_to_network:
+               /* Was not set PENDING, no longer QUEUED, so is now DONE
+                * as far as this connection is concerned. */
                req->rq_state &= ~RQ_NET_QUEUED;
-               /* fall through, in case we raced with drbd_disconnect */
+               req->rq_state |= RQ_NET_DONE;
+               _req_may_be_done_not_susp(req, m);
+               break;
+
        case connection_lost_while_pending:
                /* transfer log cleanup after connection loss */
                /* assert something? */
@@ -616,8 +623,6 @@ int __req_mod(struct drbd_request *req, enum drbd_req_event what,
                        _req_may_be_done(req, m); /* Allowed while state.susp */
                break;
 
-       case write_acked_by_peer_and_sis:
-               req->rq_state |= RQ_NET_SIS;
        case conflict_discarded_by_peer:
                /* for discarded conflicting writes of multiple primaries,
                 * there is no need to keep anything in the tl, potential
@@ -628,18 +633,15 @@ int __req_mod(struct drbd_request *req, enum drbd_req_event what,
                              (unsigned long long)req->sector, req->size);
                req->rq_state |= RQ_NET_DONE;
                /* fall through */
+       case write_acked_by_peer_and_sis:
        case write_acked_by_peer:
+               if (what == write_acked_by_peer_and_sis)
+                       req->rq_state |= RQ_NET_SIS;
                /* protocol C; successfully written on peer.
-                * Nothing to do here.
+                * Nothing more to do here.
                 * We want to keep the tl in place for all protocols, to cater
-                * for volatile write-back caches on lower level devices.
-                *
-                * A barrier request is expected to have forced all prior
-                * requests onto stable storage, so completion of a barrier
-                * request could set NET_DONE right here, and not wait for the
-                * P_BARRIER_ACK, but that is an unnecessary optimization. */
+                * for volatile write-back caches on lower level devices. */
 
-               /* this makes it effectively the same as for: */
        case recv_acked_by_peer:
                /* protocol B; pretends to be successfully written on peer.
                 * see also notes above in handed_over_to_network about
@@ -773,6 +775,7 @@ static int drbd_make_request_common(struct drbd_conf *mdev, struct bio *bio, uns
        int local, remote, send_oos = 0;
        int err = -EIO;
        int ret = 0;
+       union drbd_state s;
 
        /* allocate outside of all locks; */
        req = drbd_req_new(mdev, bio);
@@ -834,8 +837,9 @@ static int drbd_make_request_common(struct drbd_conf *mdev, struct bio *bio, uns
                drbd_al_begin_io(mdev, sector);
        }
 
-       remote = remote && drbd_should_do_remote(mdev->state);
-       send_oos = rw == WRITE && drbd_should_send_oos(mdev->state);
+       s = mdev->state;
+       remote = remote && drbd_should_do_remote(s);
+       send_oos = rw == WRITE && drbd_should_send_oos(s);
        D_ASSERT(!(remote && send_oos));
 
        if (!(local || remote) && !is_susp(mdev->state)) {
@@ -867,7 +871,7 @@ allocate_barrier:
 
        if (is_susp(mdev->state)) {
                /* If we got suspended, use the retry mechanism of
-                  generic_make_request() to restart processing of this
+                  drbd_make_request() to restart processing of this
                   bio. In the next call to drbd_make_request
                   we sleep in inc_ap_bio() */
                ret = 1;
@@ -1091,7 +1095,6 @@ void drbd_make_request(struct request_queue *q, struct bio *bio)
         */
        D_ASSERT(bio->bi_size > 0);
        D_ASSERT((bio->bi_size & 0x1ff) == 0);
-       D_ASSERT(bio->bi_idx == 0);
 
        /* to make some things easier, force alignment of requests within the
         * granularity of our hash tables */
@@ -1099,8 +1102,9 @@ void drbd_make_request(struct request_queue *q, struct bio *bio)
        e_enr = (bio->bi_sector+(bio->bi_size>>9)-1) >> HT_SHIFT;
 
        if (likely(s_enr == e_enr)) {
-               inc_ap_bio(mdev, 1);
-               drbd_make_request_common(mdev, bio, start_time);
+               do {
+                       inc_ap_bio(mdev, 1);
+               } while (drbd_make_request_common(mdev, bio, start_time));
                return;
        }
 
@@ -1196,36 +1200,66 @@ void request_timer_fn(unsigned long data)
        struct drbd_conf *mdev = (struct drbd_conf *) data;
        struct drbd_request *req; /* oldest request */
        struct list_head *le;
-       unsigned long et = 0; /* effective timeout = ko_count * timeout */
+       unsigned long ent = 0, dt = 0, et, nt; /* effective timeout = ko_count * timeout */
+       unsigned long now;
 
        if (get_net_conf(mdev)) {
-               et = mdev->net_conf->timeout*HZ/10 * mdev->net_conf->ko_count;
+               if (mdev->state.conn >= C_WF_REPORT_PARAMS)
+                       ent = mdev->net_conf->timeout*HZ/10
+                               * mdev->net_conf->ko_count;
                put_net_conf(mdev);
        }
-       if (!et || mdev->state.conn < C_WF_REPORT_PARAMS)
+       if (get_ldev(mdev)) { /* implicit state.disk >= D_INCONSISTENT */
+               dt = mdev->ldev->dc.disk_timeout * HZ / 10;
+               put_ldev(mdev);
+       }
+       et = min_not_zero(dt, ent);
+
+       if (!et)
                return; /* Recurring timer stopped */
 
+       now = jiffies;
+
        spin_lock_irq(&mdev->req_lock);
        le = &mdev->oldest_tle->requests;
        if (list_empty(le)) {
                spin_unlock_irq(&mdev->req_lock);
-               mod_timer(&mdev->request_timer, jiffies + et);
+               mod_timer(&mdev->request_timer, now + et);
                return;
        }
 
        le = le->prev;
        req = list_entry(le, struct drbd_request, tl_requests);
-       if (time_is_before_eq_jiffies(req->start_time + et)) {
-               if (req->rq_state & RQ_NET_PENDING) {
-                       dev_warn(DEV, "Remote failed to finish a request within ko-count * timeout\n");
-                       _drbd_set_state(_NS(mdev, conn, C_TIMEOUT), CS_VERBOSE, NULL);
-               } else {
-                       dev_warn(DEV, "Local backing block device frozen?\n");
-                       mod_timer(&mdev->request_timer, jiffies + et);
-               }
-       } else {
-               mod_timer(&mdev->request_timer, req->start_time + et);
-       }
 
+       /* The request is considered timed out, if
+        * - we have some effective timeout from the configuration,
+        *   with above state restrictions applied,
+        * - the oldest request is waiting for a response from the network
+        *   resp. the local disk,
+        * - the oldest request is in fact older than the effective timeout,
+        * - the connection was established (resp. disk was attached)
+        *   for longer than the timeout already.
+        * Note that for 32bit jiffies and very stable connections/disks,
+        * we may have a wrap around, which is catched by
+        *   !time_in_range(now, last_..._jif, last_..._jif + timeout).
+        *
+        * Side effect: once per 32bit wrap-around interval, which means every
+        * ~198 days with 250 HZ, we have a window where the timeout would need
+        * to expire twice (worst case) to become effective. Good enough.
+        */
+       if (ent && req->rq_state & RQ_NET_PENDING &&
+                time_after(now, req->start_time + ent) &&
+               !time_in_range(now, mdev->last_reconnect_jif, mdev->last_reconnect_jif + ent)) {
+               dev_warn(DEV, "Remote failed to finish a request within ko-count * timeout\n");
+               _drbd_set_state(_NS(mdev, conn, C_TIMEOUT), CS_VERBOSE | CS_HARD, NULL);
+       }
+       if (dt && req->rq_state & RQ_LOCAL_PENDING &&
+                time_after(now, req->start_time + dt) &&
+               !time_in_range(now, mdev->last_reattach_jif, mdev->last_reattach_jif + dt)) {
+               dev_warn(DEV, "Local backing device failed to meet the disk-timeout\n");
+               __drbd_chk_io_error(mdev, 1);
+       }
+       nt = (time_after(now, req->start_time + et) ? now : req->start_time) + et;
        spin_unlock_irq(&mdev->req_lock);
+       mod_timer(&mdev->request_timer, nt);
 }
index 68a234a5fdc5bc4ab19e2a5ab85cd198e588b9bd..3d211191948613c47736ee2f405918fb52df7392 100644 (file)
@@ -105,6 +105,7 @@ enum drbd_req_event {
        read_completed_with_error,
        read_ahead_completed_with_error,
        write_completed_with_error,
+       abort_disk_io,
        completed_ok,
        resend,
        fail_frozen_disk_io,
@@ -118,18 +119,21 @@ enum drbd_req_event {
  * same time, so we should hold the request lock anyways.
  */
 enum drbd_req_state_bits {
-       /* 210
-        * 000: no local possible
-        * 001: to be submitted
+       /* 3210
+        * 0000: no local possible
+        * 0001: to be submitted
         *    UNUSED, we could map: 011: submitted, completion still pending
-        * 110: completed ok
-        * 010: completed with error
+        * 0110: completed ok
+        * 0010: completed with error
+        * 1001: Aborted (before completion)
+        * 1x10: Aborted and completed -> free
         */
        __RQ_LOCAL_PENDING,
        __RQ_LOCAL_COMPLETED,
        __RQ_LOCAL_OK,
+       __RQ_LOCAL_ABORTED,
 
-       /* 76543
+       /* 87654
         * 00000: no network possible
         * 00001: to be send
         * 00011: to be send, on worker queue
@@ -199,8 +203,9 @@ enum drbd_req_state_bits {
 #define RQ_LOCAL_PENDING   (1UL << __RQ_LOCAL_PENDING)
 #define RQ_LOCAL_COMPLETED (1UL << __RQ_LOCAL_COMPLETED)
 #define RQ_LOCAL_OK        (1UL << __RQ_LOCAL_OK)
+#define RQ_LOCAL_ABORTED   (1UL << __RQ_LOCAL_ABORTED)
 
-#define RQ_LOCAL_MASK      ((RQ_LOCAL_OK << 1)-1) /* 0x07 */
+#define RQ_LOCAL_MASK      ((RQ_LOCAL_ABORTED << 1)-1)
 
 #define RQ_NET_PENDING     (1UL << __RQ_NET_PENDING)
 #define RQ_NET_QUEUED      (1UL << __RQ_NET_QUEUED)
index 4d3e6f6213ba0436cc2eceff16b7876e58d952b6..620c70ff223118e6f259a200512203f5010e2cda 100644 (file)
@@ -70,11 +70,29 @@ rwlock_t global_state_lock;
 void drbd_md_io_complete(struct bio *bio, int error)
 {
        struct drbd_md_io *md_io;
+       struct drbd_conf *mdev;
 
        md_io = (struct drbd_md_io *)bio->bi_private;
+       mdev = container_of(md_io, struct drbd_conf, md_io);
+
        md_io->error = error;
 
-       complete(&md_io->event);
+       /* We grabbed an extra reference in _drbd_md_sync_page_io() to be able
+        * to timeout on the lower level device, and eventually detach from it.
+        * If this io completion runs after that timeout expired, this
+        * drbd_md_put_buffer() may allow us to finally try and re-attach.
+        * During normal operation, this only puts that extra reference
+        * down to 1 again.
+        * Make sure we first drop the reference, and only then signal
+        * completion, or we may (in drbd_al_read_log()) cycle so fast into the
+        * next drbd_md_sync_page_io(), that we trigger the
+        * ASSERT(atomic_read(&mdev->md_io_in_use) == 1) there.
+        */
+       drbd_md_put_buffer(mdev);
+       md_io->done = 1;
+       wake_up(&mdev->misc_wait);
+       bio_put(bio);
+       put_ldev(mdev);
 }
 
 /* reads on behalf of the partner,
@@ -226,6 +244,7 @@ void drbd_endio_pri(struct bio *bio, int error)
        spin_lock_irqsave(&mdev->req_lock, flags);
        __req_mod(req, what, &m);
        spin_unlock_irqrestore(&mdev->req_lock, flags);
+       put_ldev(mdev);
 
        if (m.bio)
                complete_master_bio(mdev, &m);
@@ -290,7 +309,7 @@ void drbd_csum_bio(struct drbd_conf *mdev, struct crypto_hash *tfm, struct bio *
        sg_init_table(&sg, 1);
        crypto_hash_init(&desc);
 
-       __bio_for_each_segment(bvec, bio, i, 0) {
+       bio_for_each_segment(bvec, bio, i) {
                sg_set_page(&sg, bvec->bv_page, bvec->bv_len, bvec->bv_offset);
                crypto_hash_update(&desc, &sg, sg.length);
        }
@@ -728,7 +747,7 @@ int w_start_resync(struct drbd_conf *mdev, struct drbd_work *w, int cancel)
        }
 
        drbd_start_resync(mdev, C_SYNC_SOURCE);
-       clear_bit(AHEAD_TO_SYNC_SOURCE, &mdev->current_epoch->flags);
+       clear_bit(AHEAD_TO_SYNC_SOURCE, &mdev->flags);
        return 1;
 }
 
@@ -1519,14 +1538,14 @@ void drbd_start_resync(struct drbd_conf *mdev, enum drbd_conns side)
        }
 
        drbd_state_lock(mdev);
-
+       write_lock_irq(&global_state_lock);
        if (!get_ldev_if_state(mdev, D_NEGOTIATING)) {
+               write_unlock_irq(&global_state_lock);
                drbd_state_unlock(mdev);
                return;
        }
 
-       write_lock_irq(&global_state_lock);
-       ns = mdev->state;
+       ns.i = mdev->state.i;
 
        ns.aftr_isp = !_drbd_may_sync_now(mdev);
 
index b0b00d70c1669d6ef05993deb313dc516a23d552..cce7df367b793d111ef875bd5cd30a6f5e08782c 100644 (file)
@@ -551,7 +551,7 @@ static void floppy_ready(void);
 static void floppy_start(void);
 static void process_fd_request(void);
 static void recalibrate_floppy(void);
-static void floppy_shutdown(unsigned long);
+static void floppy_shutdown(struct work_struct *);
 
 static int floppy_request_regions(int);
 static void floppy_release_regions(int);
@@ -588,6 +588,8 @@ static int buffer_max = -1;
 static struct floppy_fdc_state fdc_state[N_FDC];
 static int fdc;                        /* current fdc */
 
+static struct workqueue_struct *floppy_wq;
+
 static struct floppy_struct *_floppy = floppy_type;
 static unsigned char current_drive;
 static long current_count_sectors;
@@ -629,16 +631,15 @@ static inline void set_debugt(void) { }
 static inline void debugt(const char *func, const char *msg) { }
 #endif /* DEBUGT */
 
-typedef void (*timeout_fn)(unsigned long);
-static DEFINE_TIMER(fd_timeout, floppy_shutdown, 0, 0);
 
+static DECLARE_DELAYED_WORK(fd_timeout, floppy_shutdown);
 static const char *timeout_message;
 
 static void is_alive(const char *func, const char *message)
 {
        /* this routine checks whether the floppy driver is "alive" */
        if (test_bit(0, &fdc_busy) && command_status < 2 &&
-           !timer_pending(&fd_timeout)) {
+           !delayed_work_pending(&fd_timeout)) {
                DPRINT("%s: timeout handler died.  %s\n", func, message);
        }
 }
@@ -666,15 +667,18 @@ static int output_log_pos;
 
 static void __reschedule_timeout(int drive, const char *message)
 {
+       unsigned long delay;
+
        if (drive == current_reqD)
                drive = current_drive;
-       del_timer(&fd_timeout);
+
        if (drive < 0 || drive >= N_DRIVE) {
-               fd_timeout.expires = jiffies + 20UL * HZ;
+               delay = 20UL * HZ;
                drive = 0;
        } else
-               fd_timeout.expires = jiffies + UDP->timeout;
-       add_timer(&fd_timeout);
+               delay = UDP->timeout;
+
+       queue_delayed_work(floppy_wq, &fd_timeout, delay);
        if (UDP->flags & FD_DEBUG)
                DPRINT("reschedule timeout %s\n", message);
        timeout_message = message;
@@ -872,7 +876,7 @@ static int lock_fdc(int drive, bool interruptible)
 
        command_status = FD_COMMAND_NONE;
 
-       __reschedule_timeout(drive, "lock fdc");
+       reschedule_timeout(drive, "lock fdc");
        set_fdc(drive);
        return 0;
 }
@@ -880,23 +884,15 @@ static int lock_fdc(int drive, bool interruptible)
 /* unlocks the driver */
 static void unlock_fdc(void)
 {
-       unsigned long flags;
-
-       raw_cmd = NULL;
        if (!test_bit(0, &fdc_busy))
                DPRINT("FDC access conflict!\n");
 
-       if (do_floppy)
-               DPRINT("device interrupt still active at FDC release: %pf!\n",
-                      do_floppy);
+       raw_cmd = NULL;
        command_status = FD_COMMAND_NONE;
-       spin_lock_irqsave(&floppy_lock, flags);
-       del_timer(&fd_timeout);
+       __cancel_delayed_work(&fd_timeout);
+       do_floppy = NULL;
        cont = NULL;
        clear_bit(0, &fdc_busy);
-       if (current_req || set_next_request())
-               do_fd_request(current_req->q);
-       spin_unlock_irqrestore(&floppy_lock, flags);
        wake_up(&fdc_wait);
 }
 
@@ -968,26 +964,24 @@ static DECLARE_WORK(floppy_work, NULL);
 
 static void schedule_bh(void (*handler)(void))
 {
+       WARN_ON(work_pending(&floppy_work));
+
        PREPARE_WORK(&floppy_work, (work_func_t)handler);
-       schedule_work(&floppy_work);
+       queue_work(floppy_wq, &floppy_work);
 }
 
-static DEFINE_TIMER(fd_timer, NULL, 0, 0);
+static DECLARE_DELAYED_WORK(fd_timer, NULL);
 
 static void cancel_activity(void)
 {
-       unsigned long flags;
-
-       spin_lock_irqsave(&floppy_lock, flags);
        do_floppy = NULL;
-       PREPARE_WORK(&floppy_work, (work_func_t)empty);
-       del_timer(&fd_timer);
-       spin_unlock_irqrestore(&floppy_lock, flags);
+       cancel_delayed_work_sync(&fd_timer);
+       cancel_work_sync(&floppy_work);
 }
 
 /* this function makes sure that the disk stays in the drive during the
  * transfer */
-static void fd_watchdog(void)
+static void fd_watchdog(struct work_struct *arg)
 {
        debug_dcl(DP->flags, "calling disk change from watchdog\n");
 
@@ -997,21 +991,20 @@ static void fd_watchdog(void)
                cont->done(0);
                reset_fdc();
        } else {
-               del_timer(&fd_timer);
-               fd_timer.function = (timeout_fn)fd_watchdog;
-               fd_timer.expires = jiffies + HZ / 10;
-               add_timer(&fd_timer);
+               cancel_delayed_work(&fd_timer);
+               PREPARE_DELAYED_WORK(&fd_timer, fd_watchdog);
+               queue_delayed_work(floppy_wq, &fd_timer, HZ / 10);
        }
 }
 
 static void main_command_interrupt(void)
 {
-       del_timer(&fd_timer);
+       cancel_delayed_work(&fd_timer);
        cont->interrupt();
 }
 
 /* waits for a delay (spinup or select) to pass */
-static int fd_wait_for_completion(unsigned long delay, timeout_fn function)
+static int fd_wait_for_completion(unsigned long expires, work_func_t function)
 {
        if (FDCS->reset) {
                reset_fdc();    /* do the reset during sleep to win time
@@ -1020,11 +1013,10 @@ static int fd_wait_for_completion(unsigned long delay, timeout_fn function)
                return 1;
        }
 
-       if (time_before(jiffies, delay)) {
-               del_timer(&fd_timer);
-               fd_timer.function = function;
-               fd_timer.expires = delay;
-               add_timer(&fd_timer);
+       if (time_before(jiffies, expires)) {
+               cancel_delayed_work(&fd_timer);
+               PREPARE_DELAYED_WORK(&fd_timer, function);
+               queue_delayed_work(floppy_wq, &fd_timer, expires - jiffies);
                return 1;
        }
        return 0;
@@ -1342,7 +1334,7 @@ static int fdc_dtr(void)
         */
        FDCS->dtr = raw_cmd->rate & 3;
        return fd_wait_for_completion(jiffies + 2UL * HZ / 100,
-                                     (timeout_fn)floppy_ready);
+                                     (work_func_t)floppy_ready);
 }                              /* fdc_dtr */
 
 static void tell_sector(void)
@@ -1447,7 +1439,7 @@ static void setup_rw_floppy(void)
        int flags;
        int dflags;
        unsigned long ready_date;
-       timeout_fn function;
+       work_func_t function;
 
        flags = raw_cmd->flags;
        if (flags & (FD_RAW_READ | FD_RAW_WRITE))
@@ -1461,9 +1453,9 @@ static void setup_rw_floppy(void)
                 */
                if (time_after(ready_date, jiffies + DP->select_delay)) {
                        ready_date -= DP->select_delay;
-                       function = (timeout_fn)floppy_start;
+                       function = (work_func_t)floppy_start;
                } else
-                       function = (timeout_fn)setup_rw_floppy;
+                       function = (work_func_t)setup_rw_floppy;
 
                /* wait until the floppy is spinning fast enough */
                if (fd_wait_for_completion(ready_date, function))
@@ -1493,7 +1485,7 @@ static void setup_rw_floppy(void)
                inr = result();
                cont->interrupt();
        } else if (flags & FD_RAW_NEED_DISK)
-               fd_watchdog();
+               fd_watchdog(NULL);
 }
 
 static int blind_seek;
@@ -1802,20 +1794,22 @@ static void show_floppy(void)
                pr_info("do_floppy=%pf\n", do_floppy);
        if (work_pending(&floppy_work))
                pr_info("floppy_work.func=%pf\n", floppy_work.func);
-       if (timer_pending(&fd_timer))
-               pr_info("fd_timer.function=%pf\n", fd_timer.function);
-       if (timer_pending(&fd_timeout)) {
-               pr_info("timer_function=%pf\n", fd_timeout.function);
-               pr_info("expires=%lu\n", fd_timeout.expires - jiffies);
-               pr_info("now=%lu\n", jiffies);
-       }
+       if (delayed_work_pending(&fd_timer))
+               pr_info("delayed work.function=%p expires=%ld\n",
+                      fd_timer.work.func,
+                      fd_timer.timer.expires - jiffies);
+       if (delayed_work_pending(&fd_timeout))
+               pr_info("timer_function=%p expires=%ld\n",
+                      fd_timeout.work.func,
+                      fd_timeout.timer.expires - jiffies);
+
        pr_info("cont=%p\n", cont);
        pr_info("current_req=%p\n", current_req);
        pr_info("command_status=%d\n", command_status);
        pr_info("\n");
 }
 
-static void floppy_shutdown(unsigned long data)
+static void floppy_shutdown(struct work_struct *arg)
 {
        unsigned long flags;
 
@@ -1868,7 +1862,7 @@ static int start_motor(void (*function)(void))
 
        /* wait_for_completion also schedules reset if needed. */
        return fd_wait_for_completion(DRS->select_date + DP->select_delay,
-                                     (timeout_fn)function);
+                                     (work_func_t)function);
 }
 
 static void floppy_ready(void)
@@ -2821,7 +2815,6 @@ do_request:
                spin_lock_irq(&floppy_lock);
                pending = set_next_request();
                spin_unlock_irq(&floppy_lock);
-
                if (!pending) {
                        do_floppy = NULL;
                        unlock_fdc();
@@ -2898,13 +2891,15 @@ static void do_fd_request(struct request_queue *q)
                 current_req->cmd_flags))
                return;
 
-       if (test_bit(0, &fdc_busy)) {
+       if (test_and_set_bit(0, &fdc_busy)) {
                /* fdc busy, this new request will be treated when the
                   current one is done */
                is_alive(__func__, "old request running");
                return;
        }
-       lock_fdc(MAXTIMEOUT, false);
+       command_status = FD_COMMAND_NONE;
+       __reschedule_timeout(MAXTIMEOUT, "fd_request");
+       set_fdc(0);
        process_fd_request();
        is_alive(__func__, "");
 }
@@ -3612,9 +3607,7 @@ static int floppy_release(struct gendisk *disk, fmode_t mode)
 
        mutex_lock(&floppy_mutex);
        mutex_lock(&open_lock);
-       if (UDRS->fd_ref < 0)
-               UDRS->fd_ref = 0;
-       else if (!UDRS->fd_ref--) {
+       if (!UDRS->fd_ref--) {
                DPRINT("floppy_release with fd_ref == 0");
                UDRS->fd_ref = 0;
        }
@@ -3650,13 +3643,7 @@ static int floppy_open(struct block_device *bdev, fmode_t mode)
                set_bit(FD_VERIFY_BIT, &UDRS->flags);
        }
 
-       if (UDRS->fd_ref == -1 || (UDRS->fd_ref && (mode & FMODE_EXCL)))
-               goto out2;
-
-       if (mode & FMODE_EXCL)
-               UDRS->fd_ref = -1;
-       else
-               UDRS->fd_ref++;
+       UDRS->fd_ref++;
 
        opened_bdev[drive] = bdev;
 
@@ -3719,10 +3706,8 @@ static int floppy_open(struct block_device *bdev, fmode_t mode)
        mutex_unlock(&floppy_mutex);
        return 0;
 out:
-       if (UDRS->fd_ref < 0)
-               UDRS->fd_ref = 0;
-       else
-               UDRS->fd_ref--;
+       UDRS->fd_ref--;
+
        if (!UDRS->fd_ref)
                opened_bdev[drive] = NULL;
 out2:
@@ -4159,10 +4144,16 @@ static int __init floppy_init(void)
                        goto out_put_disk;
                }
 
+               floppy_wq = alloc_ordered_workqueue("floppy", 0);
+               if (!floppy_wq) {
+                       err = -ENOMEM;
+                       goto out_put_disk;
+               }
+
                disks[dr]->queue = blk_init_queue(do_fd_request, &floppy_lock);
                if (!disks[dr]->queue) {
                        err = -ENOMEM;
-                       goto out_put_disk;
+                       goto out_destroy_workq;
                }
 
                blk_queue_max_hw_sectors(disks[dr]->queue, 64);
@@ -4213,7 +4204,7 @@ static int __init floppy_init(void)
        use_virtual_dma = can_use_virtual_dma & 1;
        fdc_state[0].address = FDC1;
        if (fdc_state[0].address == -1) {
-               del_timer_sync(&fd_timeout);
+               cancel_delayed_work(&fd_timeout);
                err = -ENODEV;
                goto out_unreg_region;
        }
@@ -4224,7 +4215,7 @@ static int __init floppy_init(void)
        fdc = 0;                /* reset fdc in case of unexpected interrupt */
        err = floppy_grab_irq_and_dma();
        if (err) {
-               del_timer_sync(&fd_timeout);
+               cancel_delayed_work(&fd_timeout);
                err = -EBUSY;
                goto out_unreg_region;
        }
@@ -4281,13 +4272,13 @@ static int __init floppy_init(void)
                user_reset_fdc(-1, FD_RESET_ALWAYS, false);
        }
        fdc = 0;
-       del_timer_sync(&fd_timeout);
+       cancel_delayed_work(&fd_timeout);
        current_drive = 0;
        initialized = true;
        if (have_no_fdc) {
                DPRINT("no floppy controllers found\n");
                err = have_no_fdc;
-               goto out_flush_work;
+               goto out_release_dma;
        }
 
        for (drive = 0; drive < N_DRIVE; drive++) {
@@ -4302,7 +4293,7 @@ static int __init floppy_init(void)
 
                err = platform_device_register(&floppy_device[drive]);
                if (err)
-                       goto out_flush_work;
+                       goto out_release_dma;
 
                err = device_create_file(&floppy_device[drive].dev,
                                         &dev_attr_cmos);
@@ -4320,13 +4311,14 @@ static int __init floppy_init(void)
 
 out_unreg_platform_dev:
        platform_device_unregister(&floppy_device[drive]);
-out_flush_work:
-       flush_work_sync(&floppy_work);
+out_release_dma:
        if (atomic_read(&usage_count))
                floppy_release_irq_and_dma();
 out_unreg_region:
        blk_unregister_region(MKDEV(FLOPPY_MAJOR, 0), 256);
        platform_driver_unregister(&floppy_driver);
+out_destroy_workq:
+       destroy_workqueue(floppy_wq);
 out_unreg_blkdev:
        unregister_blkdev(FLOPPY_MAJOR, "fd");
 out_put_disk:
@@ -4397,7 +4389,7 @@ static int floppy_grab_irq_and_dma(void)
         * We might have scheduled a free_irq(), wait it to
         * drain first:
         */
-       flush_work_sync(&floppy_work);
+       flush_workqueue(floppy_wq);
 
        if (fd_request_irq()) {
                DPRINT("Unable to grab IRQ%d for the floppy driver\n",
@@ -4488,9 +4480,9 @@ static void floppy_release_irq_and_dma(void)
                        pr_info("motor off timer %d still active\n", drive);
 #endif
 
-       if (timer_pending(&fd_timeout))
+       if (delayed_work_pending(&fd_timeout))
                pr_info("floppy timer still active:%s\n", timeout_message);
-       if (timer_pending(&fd_timer))
+       if (delayed_work_pending(&fd_timer))
                pr_info("auxiliary floppy timer still active\n");
        if (work_pending(&floppy_work))
                pr_info("work still pending\n");
@@ -4560,8 +4552,9 @@ static void __exit floppy_module_exit(void)
                put_disk(disks[drive]);
        }
 
-       del_timer_sync(&fd_timeout);
-       del_timer_sync(&fd_timer);
+       cancel_delayed_work_sync(&fd_timeout);
+       cancel_delayed_work_sync(&fd_timer);
+       destroy_workqueue(floppy_wq);
 
        if (atomic_read(&usage_count))
                floppy_release_irq_and_dma();
index 304000c3d433f1d98afe6c0f0feea8553dc7bd04..264bc77dcb911c7030c787d3ad87cd79b654ce81 100644 (file)
@@ -294,18 +294,16 @@ static int hba_reset_nosleep(struct driver_data *dd)
  */
 static inline void mtip_issue_ncq_command(struct mtip_port *port, int tag)
 {
-       unsigned long flags = 0;
-
        atomic_set(&port->commands[tag].active, 1);
 
-       spin_lock_irqsave(&port->cmd_issue_lock, flags);
+       spin_lock(&port->cmd_issue_lock);
 
        writel((1 << MTIP_TAG_BIT(tag)),
                        port->s_active[MTIP_TAG_INDEX(tag)]);
        writel((1 << MTIP_TAG_BIT(tag)),
                        port->cmd_issue[MTIP_TAG_INDEX(tag)]);
 
-       spin_unlock_irqrestore(&port->cmd_issue_lock, flags);
+       spin_unlock(&port->cmd_issue_lock);
 
        /* Set the command's timeout value.*/
        port->commands[tag].comp_time = jiffies + msecs_to_jiffies(
@@ -436,8 +434,7 @@ static void mtip_init_port(struct mtip_port *port)
                writel(0xFFFFFFFF, port->completed[i]);
 
        /* Clear any pending interrupts for this port */
-       writel(readl(port->dd->mmio + PORT_IRQ_STAT),
-                                       port->dd->mmio + PORT_IRQ_STAT);
+       writel(readl(port->mmio + PORT_IRQ_STAT), port->mmio + PORT_IRQ_STAT);
 
        /* Clear any pending interrupts on the HBA. */
        writel(readl(port->dd->mmio + HOST_IRQ_STAT),
@@ -782,13 +779,24 @@ static void mtip_handle_tfe(struct driver_data *dd)
 
        /* Stop the timer to prevent command timeouts. */
        del_timer(&port->cmd_timer);
+       set_bit(MTIP_PF_EH_ACTIVE_BIT, &port->flags);
+
+       if (test_bit(MTIP_PF_IC_ACTIVE_BIT, &port->flags) &&
+                       test_bit(MTIP_TAG_INTERNAL, port->allocated)) {
+               cmd = &port->commands[MTIP_TAG_INTERNAL];
+               dbg_printk(MTIP_DRV_NAME " TFE for the internal command\n");
+
+               atomic_inc(&cmd->active); /* active > 1 indicates error */
+               if (cmd->comp_data && cmd->comp_func) {
+                       cmd->comp_func(port, MTIP_TAG_INTERNAL,
+                                       cmd->comp_data, PORT_IRQ_TF_ERR);
+               }
+               goto handle_tfe_exit;
+       }
 
        /* clear the tag accumulator */
        memset(tagaccum, 0, SLOTBITS_IN_LONGS * sizeof(long));
 
-       /* Set eh_active */
-       set_bit(MTIP_PF_EH_ACTIVE_BIT, &port->flags);
-
        /* Loop through all the groups */
        for (group = 0; group < dd->slot_groups; group++) {
                completed = readl(port->completed[group]);
@@ -940,6 +948,7 @@ static void mtip_handle_tfe(struct driver_data *dd)
        }
        print_tags(dd, "reissued (TFE)", tagaccum, cmd_cnt);
 
+handle_tfe_exit:
        /* clear eh_active */
        clear_bit(MTIP_PF_EH_ACTIVE_BIT, &port->flags);
        wake_up_interruptible(&port->svc_wait);
@@ -961,6 +970,8 @@ static inline void mtip_process_sdbf(struct driver_data *dd)
        /* walk all bits in all slot groups */
        for (group = 0; group < dd->slot_groups; group++) {
                completed = readl(port->completed[group]);
+               if (!completed)
+                       continue;
 
                /* clear completed status register in the hardware.*/
                writel(completed, port->completed[group]);
@@ -1329,22 +1340,6 @@ static int mtip_exec_internal_command(struct mtip_port *port,
                        }
                        rv = -EAGAIN;
                }
-
-               if (readl(port->cmd_issue[MTIP_TAG_INTERNAL])
-                       & (1 << MTIP_TAG_INTERNAL)) {
-                       dev_warn(&port->dd->pdev->dev,
-                               "Retiring internal command but CI is 1.\n");
-                       if (test_bit(MTIP_DDF_REMOVE_PENDING_BIT,
-                                               &port->dd->dd_flag)) {
-                               hba_reset_nosleep(port->dd);
-                               rv = -ENXIO;
-                       } else {
-                               mtip_restart_port(port);
-                               rv = -EAGAIN;
-                       }
-                       goto exec_ic_exit;
-               }
-
        } else {
                /* Spin for <timeout> checking if command still outstanding */
                timeout = jiffies + msecs_to_jiffies(timeout);
@@ -1361,21 +1356,25 @@ static int mtip_exec_internal_command(struct mtip_port *port,
                                rv = -ENXIO;
                                goto exec_ic_exit;
                        }
+                       if (readl(port->mmio + PORT_IRQ_STAT) & PORT_IRQ_ERR) {
+                               atomic_inc(&int_cmd->active); /* error */
+                               break;
+                       }
                }
+       }
 
-               if (readl(port->cmd_issue[MTIP_TAG_INTERNAL])
+       if (atomic_read(&int_cmd->active) > 1) {
+               dev_err(&port->dd->pdev->dev,
+                       "Internal command [%02X] failed\n", fis->command);
+               rv = -EIO;
+       }
+       if (readl(port->cmd_issue[MTIP_TAG_INTERNAL])
                        & (1 << MTIP_TAG_INTERNAL)) {
-                       dev_err(&port->dd->pdev->dev,
-                               "Internal command did not complete [atomic]\n");
+               rv = -ENXIO;
+               if (!test_bit(MTIP_DDF_REMOVE_PENDING_BIT,
+                                       &port->dd->dd_flag)) {
+                       mtip_restart_port(port);
                        rv = -EAGAIN;
-                       if (test_bit(MTIP_DDF_REMOVE_PENDING_BIT,
-                                               &port->dd->dd_flag)) {
-                               hba_reset_nosleep(port->dd);
-                               rv = -ENXIO;
-                       } else {
-                               mtip_restart_port(port);
-                               rv = -EAGAIN;
-                       }
                }
        }
 exec_ic_exit:
@@ -1893,13 +1892,33 @@ static int exec_drive_command(struct mtip_port *port, u8 *command,
                                void __user *user_buffer)
 {
        struct host_to_dev_fis  fis;
-       struct host_to_dev_fis *reply = (port->rxfis + RX_FIS_D2H_REG);
+       struct host_to_dev_fis *reply;
+       u8 *buf = NULL;
+       dma_addr_t dma_addr = 0;
+       int rv = 0, xfer_sz = command[3];
+
+       if (xfer_sz) {
+               if (user_buffer)
+                       return -EFAULT;
+
+               buf = dmam_alloc_coherent(&port->dd->pdev->dev,
+                               ATA_SECT_SIZE * xfer_sz,
+                               &dma_addr,
+                               GFP_KERNEL);
+               if (!buf) {
+                       dev_err(&port->dd->pdev->dev,
+                               "Memory allocation failed (%d bytes)\n",
+                               ATA_SECT_SIZE * xfer_sz);
+                       return -ENOMEM;
+               }
+               memset(buf, 0, ATA_SECT_SIZE * xfer_sz);
+       }
 
        /* Build the FIS. */
        memset(&fis, 0, sizeof(struct host_to_dev_fis));
-       fis.type                = 0x27;
-       fis.opts                = 1 << 7;
-       fis.command             = command[0];
+       fis.type        = 0x27;
+       fis.opts        = 1 << 7;
+       fis.command     = command[0];
        fis.features    = command[2];
        fis.sect_count  = command[3];
        if (fis.command == ATA_CMD_SMART) {
@@ -1908,6 +1927,11 @@ static int exec_drive_command(struct mtip_port *port, u8 *command,
                fis.cyl_hi      = 0xC2;
        }
 
+       if (xfer_sz)
+               reply = (port->rxfis + RX_FIS_PIO_SETUP);
+       else
+               reply = (port->rxfis + RX_FIS_D2H_REG);
+
        dbg_printk(MTIP_DRV_NAME
                " %s: User Command: cmd %x, sect %x, "
                "feat %x, sectcnt %x\n",
@@ -1917,43 +1941,46 @@ static int exec_drive_command(struct mtip_port *port, u8 *command,
                command[2],
                command[3]);
 
-       memset(port->sector_buffer, 0x00, ATA_SECT_SIZE);
-
        /* Execute the command. */
        if (mtip_exec_internal_command(port,
                                &fis,
                                 5,
-                                port->sector_buffer_dma,
-                                (command[3] != 0) ? ATA_SECT_SIZE : 0,
+                                (xfer_sz ? dma_addr : 0),
+                                (xfer_sz ? ATA_SECT_SIZE * xfer_sz : 0),
                                 0,
                                 GFP_KERNEL,
                                 MTIP_IOCTL_COMMAND_TIMEOUT_MS)
                                 < 0) {
-               return -1;
+               rv = -EFAULT;
+               goto exit_drive_command;
        }
 
        /* Collect the completion status. */
        command[0] = reply->command; /* Status*/
        command[1] = reply->features; /* Error*/
-       command[2] = command[3];
+       command[2] = reply->sect_count;
 
        dbg_printk(MTIP_DRV_NAME
                " %s: Completion Status: stat %x, "
-               "err %x, cmd %x\n",
+               "err %x, nsect %x\n",
                __func__,
                command[0],
                command[1],
                command[2]);
 
-       if (user_buffer && command[3]) {
+       if (xfer_sz) {
                if (copy_to_user(user_buffer,
-                                port->sector_buffer,
+                                buf,
                                 ATA_SECT_SIZE * command[3])) {
-                       return -EFAULT;
+                       rv = -EFAULT;
+                       goto exit_drive_command;
                }
        }
-
-       return 0;
+exit_drive_command:
+       if (buf)
+               dmam_free_coherent(&port->dd->pdev->dev,
+                               ATA_SECT_SIZE * xfer_sz, buf, dma_addr);
+       return rv;
 }
 
 /*
@@ -2003,6 +2030,32 @@ static unsigned int implicit_sector(unsigned char command,
        return rv;
 }
 
+static void mtip_set_timeout(struct host_to_dev_fis *fis, unsigned int *timeout)
+{
+       switch (fis->command) {
+       case ATA_CMD_DOWNLOAD_MICRO:
+               *timeout = 120000; /* 2 minutes */
+               break;
+       case ATA_CMD_SEC_ERASE_UNIT:
+       case 0xFC:
+               *timeout = 240000; /* 4 minutes */
+               break;
+       case ATA_CMD_STANDBYNOW1:
+               *timeout = 10000;  /* 10 seconds */
+               break;
+       case 0xF7:
+       case 0xFA:
+               *timeout = 60000;  /* 60 seconds */
+               break;
+       case ATA_CMD_SMART:
+               *timeout = 15000;  /* 15 seconds */
+               break;
+       default:
+               *timeout = MTIP_IOCTL_COMMAND_TIMEOUT_MS;
+               break;
+       }
+}
+
 /*
  * Executes a taskfile
  * See ide_taskfile_ioctl() for derivation
@@ -2023,7 +2076,7 @@ static int exec_drive_taskfile(struct driver_data *dd,
        unsigned int taskin = 0;
        unsigned int taskout = 0;
        u8 nsect = 0;
-       unsigned int timeout = MTIP_IOCTL_COMMAND_TIMEOUT_MS;
+       unsigned int timeout;
        unsigned int force_single_sector;
        unsigned int transfer_size;
        unsigned long task_file_data;
@@ -2153,32 +2206,7 @@ static int exec_drive_taskfile(struct driver_data *dd,
                fis.lba_hi,
                fis.device);
 
-       switch (fis.command) {
-       case ATA_CMD_DOWNLOAD_MICRO:
-               /* Change timeout for Download Microcode to 2 minutes */
-               timeout = 120000;
-               break;
-       case ATA_CMD_SEC_ERASE_UNIT:
-               /* Change timeout for Security Erase Unit to 4 minutes.*/
-               timeout = 240000;
-               break;
-       case ATA_CMD_STANDBYNOW1:
-               /* Change timeout for standby immediate to 10 seconds.*/
-               timeout = 10000;
-               break;
-       case 0xF7:
-       case 0xFA:
-               /* Change timeout for vendor unique command to 10 secs */
-               timeout = 10000;
-               break;
-       case ATA_CMD_SMART:
-               /* Change timeout for vendor unique command to 15 secs */
-               timeout = 15000;
-               break;
-       default:
-               timeout = MTIP_IOCTL_COMMAND_TIMEOUT_MS;
-               break;
-       }
+       mtip_set_timeout(&fis, &timeout);
 
        /* Determine the correct transfer size.*/
        if (force_single_sector)
@@ -2295,13 +2323,12 @@ static int mtip_hw_ioctl(struct driver_data *dd, unsigned int cmd,
 {
        switch (cmd) {
        case HDIO_GET_IDENTITY:
-               if (mtip_get_identify(dd->port, (void __user *) arg) < 0) {
-                       dev_warn(&dd->pdev->dev,
-                               "Unable to read identity\n");
-                       return -EIO;
-               }
-
+       {
+               if (copy_to_user((void __user *)arg, dd->port->identify,
+                                               sizeof(u16) * ATA_ID_WORDS))
+                       return -EFAULT;
                break;
+       }
        case HDIO_DRIVE_CMD:
        {
                u8 drive_command[4];
@@ -2537,40 +2564,58 @@ static ssize_t mtip_hw_show_registers(struct device *dev,
        int size = 0;
        int n;
 
-       size += sprintf(&buf[size], "S ACTive:\n");
+       size += sprintf(&buf[size], "Hardware\n--------\n");
+       size += sprintf(&buf[size], "S ACTive      : [ 0x");
 
-       for (n = 0; n < dd->slot_groups; n++)
-               size += sprintf(&buf[size], "0x%08x\n",
+       for (n = dd->slot_groups-1; n >= 0; n--)
+               size += sprintf(&buf[size], "%08X ",
                                         readl(dd->port->s_active[n]));
 
-       size += sprintf(&buf[size], "Command Issue:\n");
+       size += sprintf(&buf[size], "]\n");
+       size += sprintf(&buf[size], "Command Issue : [ 0x");
 
-       for (n = 0; n < dd->slot_groups; n++)
-               size += sprintf(&buf[size], "0x%08x\n",
+       for (n = dd->slot_groups-1; n >= 0; n--)
+               size += sprintf(&buf[size], "%08X ",
                                        readl(dd->port->cmd_issue[n]));
 
-       size += sprintf(&buf[size], "Allocated:\n");
+       size += sprintf(&buf[size], "]\n");
+       size += sprintf(&buf[size], "Completed     : [ 0x");
+
+       for (n = dd->slot_groups-1; n >= 0; n--)
+               size += sprintf(&buf[size], "%08X ",
+                               readl(dd->port->completed[n]));
+
+       size += sprintf(&buf[size], "]\n");
+       size += sprintf(&buf[size], "PORT IRQ STAT : [ 0x%08X ]\n",
+                               readl(dd->port->mmio + PORT_IRQ_STAT));
+       size += sprintf(&buf[size], "HOST IRQ STAT : [ 0x%08X ]\n",
+                               readl(dd->mmio + HOST_IRQ_STAT));
+       size += sprintf(&buf[size], "\n");
 
-       for (n = 0; n < dd->slot_groups; n++) {
+       size += sprintf(&buf[size], "Local\n-----\n");
+       size += sprintf(&buf[size], "Allocated    : [ 0x");
+
+       for (n = dd->slot_groups-1; n >= 0; n--) {
                if (sizeof(long) > sizeof(u32))
                        group_allocated =
                                dd->port->allocated[n/2] >> (32*(n&1));
                else
                        group_allocated = dd->port->allocated[n];
-               size += sprintf(&buf[size], "0x%08x\n",
-                                group_allocated);
+               size += sprintf(&buf[size], "%08X ", group_allocated);
        }
+       size += sprintf(&buf[size], "]\n");
 
-       size += sprintf(&buf[size], "Completed:\n");
-
-       for (n = 0; n < dd->slot_groups; n++)
-               size += sprintf(&buf[size], "0x%08x\n",
-                               readl(dd->port->completed[n]));
+       size += sprintf(&buf[size], "Commands in Q: [ 0x");
 
-       size += sprintf(&buf[size], "PORT IRQ STAT : 0x%08x\n",
-                               readl(dd->port->mmio + PORT_IRQ_STAT));
-       size += sprintf(&buf[size], "HOST IRQ STAT : 0x%08x\n",
-                               readl(dd->mmio + HOST_IRQ_STAT));
+       for (n = dd->slot_groups-1; n >= 0; n--) {
+               if (sizeof(long) > sizeof(u32))
+                       group_allocated =
+                               dd->port->cmds_to_issue[n/2] >> (32*(n&1));
+               else
+                       group_allocated = dd->port->cmds_to_issue[n];
+               size += sprintf(&buf[size], "%08X ", group_allocated);
+       }
+       size += sprintf(&buf[size], "]\n");
 
        return size;
 }
@@ -2592,8 +2637,24 @@ static ssize_t mtip_hw_show_status(struct device *dev,
        return size;
 }
 
+static ssize_t mtip_hw_show_flags(struct device *dev,
+                               struct device_attribute *attr,
+                               char *buf)
+{
+       struct driver_data *dd = dev_to_disk(dev)->private_data;
+       int size = 0;
+
+       size += sprintf(&buf[size], "Flag in port struct : [ %08lX ]\n",
+                                                       dd->port->flags);
+       size += sprintf(&buf[size], "Flag in dd struct   : [ %08lX ]\n",
+                                                       dd->dd_flag);
+
+       return size;
+}
+
 static DEVICE_ATTR(registers, S_IRUGO, mtip_hw_show_registers, NULL);
 static DEVICE_ATTR(status, S_IRUGO, mtip_hw_show_status, NULL);
+static DEVICE_ATTR(flags, S_IRUGO, mtip_hw_show_flags, NULL);
 
 /*
  * Create the sysfs related attributes.
@@ -2616,6 +2677,9 @@ static int mtip_hw_sysfs_init(struct driver_data *dd, struct kobject *kobj)
        if (sysfs_create_file(kobj, &dev_attr_status.attr))
                dev_warn(&dd->pdev->dev,
                        "Error creating 'status' sysfs entry\n");
+       if (sysfs_create_file(kobj, &dev_attr_flags.attr))
+               dev_warn(&dd->pdev->dev,
+                       "Error creating 'flags' sysfs entry\n");
        return 0;
 }
 
@@ -2636,6 +2700,7 @@ static int mtip_hw_sysfs_exit(struct driver_data *dd, struct kobject *kobj)
 
        sysfs_remove_file(kobj, &dev_attr_registers.attr);
        sysfs_remove_file(kobj, &dev_attr_status.attr);
+       sysfs_remove_file(kobj, &dev_attr_flags.attr);
 
        return 0;
 }
@@ -3634,7 +3699,10 @@ skip_create_disk:
        set_bit(QUEUE_FLAG_NONROT, &dd->queue->queue_flags);
        blk_queue_max_segments(dd->queue, MTIP_MAX_SG);
        blk_queue_physical_block_size(dd->queue, 4096);
+       blk_queue_max_hw_sectors(dd->queue, 0xffff);
+       blk_queue_max_segment_size(dd->queue, 0x400000);
        blk_queue_io_min(dd->queue, 4096);
+
        /*
         * write back cache is not supported in the device. FUA depends on
         * write back cache support, hence setting flush support to zero.
index 4ef58336310a126af9b4d0847dc4099e4af23610..b2c88da26b2a7b7f94ff77b6f1a1d2047f6b45f3 100644 (file)
 
 #define __force_bit2int (unsigned int __force)
 
-/* below are bit numbers in 'flags' defined in mtip_port */
-#define MTIP_PF_IC_ACTIVE_BIT          0 /* pio/ioctl */
-#define MTIP_PF_EH_ACTIVE_BIT          1 /* error handling */
-#define MTIP_PF_SE_ACTIVE_BIT          2 /* secure erase */
-#define MTIP_PF_DM_ACTIVE_BIT          3 /* download microcde */
-#define MTIP_PF_PAUSE_IO       ((1 << MTIP_PF_IC_ACTIVE_BIT) | \
+enum {
+       /* below are bit numbers in 'flags' defined in mtip_port */
+       MTIP_PF_IC_ACTIVE_BIT       = 0, /* pio/ioctl */
+       MTIP_PF_EH_ACTIVE_BIT       = 1, /* error handling */
+       MTIP_PF_SE_ACTIVE_BIT       = 2, /* secure erase */
+       MTIP_PF_DM_ACTIVE_BIT       = 3, /* download microcde */
+       MTIP_PF_PAUSE_IO      = ((1 << MTIP_PF_IC_ACTIVE_BIT) | \
                                (1 << MTIP_PF_EH_ACTIVE_BIT) | \
                                (1 << MTIP_PF_SE_ACTIVE_BIT) | \
-                               (1 << MTIP_PF_DM_ACTIVE_BIT))
-
-#define MTIP_PF_SVC_THD_ACTIVE_BIT     4
-#define MTIP_PF_ISSUE_CMDS_BIT         5
-#define MTIP_PF_REBUILD_BIT            6
-#define MTIP_PF_SVC_THD_STOP_BIT       8
-
-/* below are bit numbers in 'dd_flag' defined in driver_data */
-#define MTIP_DDF_REMOVE_PENDING_BIT    1
-#define MTIP_DDF_OVER_TEMP_BIT         2
-#define MTIP_DDF_WRITE_PROTECT_BIT     3
-#define MTIP_DDF_STOP_IO       ((1 << MTIP_DDF_REMOVE_PENDING_BIT) | \
+                               (1 << MTIP_PF_DM_ACTIVE_BIT)),
+
+       MTIP_PF_SVC_THD_ACTIVE_BIT  = 4,
+       MTIP_PF_ISSUE_CMDS_BIT      = 5,
+       MTIP_PF_REBUILD_BIT         = 6,
+       MTIP_PF_SVC_THD_STOP_BIT    = 8,
+
+       /* below are bit numbers in 'dd_flag' defined in driver_data */
+       MTIP_DDF_REMOVE_PENDING_BIT = 1,
+       MTIP_DDF_OVER_TEMP_BIT      = 2,
+       MTIP_DDF_WRITE_PROTECT_BIT  = 3,
+       MTIP_DDF_STOP_IO      = ((1 << MTIP_DDF_REMOVE_PENDING_BIT) | \
                                (1 << MTIP_DDF_OVER_TEMP_BIT) | \
-                               (1 << MTIP_DDF_WRITE_PROTECT_BIT))
+                               (1 << MTIP_DDF_WRITE_PROTECT_BIT)),
 
-#define MTIP_DDF_CLEANUP_BIT           5
-#define MTIP_DDF_RESUME_BIT            6
-#define MTIP_DDF_INIT_DONE_BIT         7
-#define MTIP_DDF_REBUILD_FAILED_BIT    8
+       MTIP_DDF_CLEANUP_BIT        = 5,
+       MTIP_DDF_RESUME_BIT         = 6,
+       MTIP_DDF_INIT_DONE_BIT      = 7,
+       MTIP_DDF_REBUILD_FAILED_BIT = 8,
+};
 
 __packed struct smart_attr{
        u8 attr_id;
index 013c7a549fb6dbc3d5d1afe2e01730e951846507..65665c9c42c62ba5805324df96292e560f16b04a 100644 (file)
@@ -141,7 +141,7 @@ struct rbd_request {
 struct rbd_snap {
        struct  device          dev;
        const char              *name;
-       size_t                  size;
+       u64                     size;
        struct list_head        node;
        u64                     id;
 };
@@ -175,8 +175,7 @@ struct rbd_device {
        /* protects updating the header */
        struct rw_semaphore     header_rwsem;
        char                    snap_name[RBD_MAX_SNAP_NAME_LEN];
-       u32 cur_snap;   /* index+1 of current snapshot within snap context
-                          0 - for the head */
+       u64                     snap_id;        /* current snapshot id */
        int read_only;
 
        struct list_head        node;
@@ -241,7 +240,7 @@ static void rbd_put_dev(struct rbd_device *rbd_dev)
        put_device(&rbd_dev->dev);
 }
 
-static int __rbd_update_snaps(struct rbd_device *rbd_dev);
+static int __rbd_refresh_header(struct rbd_device *rbd_dev);
 
 static int rbd_open(struct block_device *bdev, fmode_t mode)
 {
@@ -450,7 +449,9 @@ static void rbd_client_release(struct kref *kref)
        struct rbd_client *rbdc = container_of(kref, struct rbd_client, kref);
 
        dout("rbd_release_client %p\n", rbdc);
+       spin_lock(&rbd_client_list_lock);
        list_del(&rbdc->node);
+       spin_unlock(&rbd_client_list_lock);
 
        ceph_destroy_client(rbdc->client);
        kfree(rbdc->rbd_opts);
@@ -463,9 +464,7 @@ static void rbd_client_release(struct kref *kref)
  */
 static void rbd_put_client(struct rbd_device *rbd_dev)
 {
-       spin_lock(&rbd_client_list_lock);
        kref_put(&rbd_dev->rbd_client->kref, rbd_client_release);
-       spin_unlock(&rbd_client_list_lock);
        rbd_dev->rbd_client = NULL;
 }
 
@@ -487,16 +486,18 @@ static void rbd_coll_release(struct kref *kref)
  */
 static int rbd_header_from_disk(struct rbd_image_header *header,
                                 struct rbd_image_header_ondisk *ondisk,
-                                int allocated_snaps,
+                                u32 allocated_snaps,
                                 gfp_t gfp_flags)
 {
-       int i;
-       u32 snap_count;
+       u32 i, snap_count;
 
        if (memcmp(ondisk, RBD_HEADER_TEXT, sizeof(RBD_HEADER_TEXT)))
                return -ENXIO;
 
        snap_count = le32_to_cpu(ondisk->snap_count);
+       if (snap_count > (UINT_MAX - sizeof(struct ceph_snap_context))
+                        / sizeof (*ondisk))
+               return -EINVAL;
        header->snapc = kmalloc(sizeof(struct ceph_snap_context) +
                                snap_count * sizeof (*ondisk),
                                gfp_flags);
@@ -506,11 +507,11 @@ static int rbd_header_from_disk(struct rbd_image_header *header,
        header->snap_names_len = le64_to_cpu(ondisk->snap_names_len);
        if (snap_count) {
                header->snap_names = kmalloc(header->snap_names_len,
-                                            GFP_KERNEL);
+                                            gfp_flags);
                if (!header->snap_names)
                        goto err_snapc;
                header->snap_sizes = kmalloc(snap_count * sizeof(u64),
-                                            GFP_KERNEL);
+                                            gfp_flags);
                if (!header->snap_sizes)
                        goto err_names;
        } else {
@@ -552,21 +553,6 @@ err_snapc:
        return -ENOMEM;
 }
 
-static int snap_index(struct rbd_image_header *header, int snap_num)
-{
-       return header->total_snaps - snap_num;
-}
-
-static u64 cur_snap_id(struct rbd_device *rbd_dev)
-{
-       struct rbd_image_header *header = &rbd_dev->header;
-
-       if (!rbd_dev->cur_snap)
-               return 0;
-
-       return header->snapc->snaps[snap_index(header, rbd_dev->cur_snap)];
-}
-
 static int snap_by_name(struct rbd_image_header *header, const char *snap_name,
                        u64 *seq, u64 *size)
 {
@@ -605,7 +591,7 @@ static int rbd_header_set_snap(struct rbd_device *dev, u64 *size)
                        snapc->seq = header->snap_seq;
                else
                        snapc->seq = 0;
-               dev->cur_snap = 0;
+               dev->snap_id = CEPH_NOSNAP;
                dev->read_only = 0;
                if (size)
                        *size = header->image_size;
@@ -613,8 +599,7 @@ static int rbd_header_set_snap(struct rbd_device *dev, u64 *size)
                ret = snap_by_name(header, dev->snap_name, &snapc->seq, size);
                if (ret < 0)
                        goto done;
-
-               dev->cur_snap = header->total_snaps - ret;
+               dev->snap_id = snapc->seq;
                dev->read_only = 1;
        }
 
@@ -935,7 +920,6 @@ static int rbd_do_request(struct request *rq,
        layout->fl_stripe_unit = cpu_to_le32(1 << RBD_MAX_OBJ_ORDER);
        layout->fl_stripe_count = cpu_to_le32(1);
        layout->fl_object_size = cpu_to_le32(1 << RBD_MAX_OBJ_ORDER);
-       layout->fl_pg_preferred = cpu_to_le32(-1);
        layout->fl_pg_pool = cpu_to_le32(dev->poolid);
        ceph_calc_raw_layout(osdc, layout, snapid, ofs, &len, &bno,
                                req, ops);
@@ -1168,7 +1152,7 @@ static int rbd_req_read(struct request *rq,
                         int coll_index)
 {
        return rbd_do_op(rq, rbd_dev, NULL,
-                        (snapid ? snapid : CEPH_NOSNAP),
+                        snapid,
                         CEPH_OSD_OP_READ,
                         CEPH_OSD_FLAG_READ,
                         2,
@@ -1187,7 +1171,7 @@ static int rbd_req_sync_read(struct rbd_device *dev,
                          u64 *ver)
 {
        return rbd_req_sync_op(dev, NULL,
-                              (snapid ? snapid : CEPH_NOSNAP),
+                              snapid,
                               CEPH_OSD_OP_READ,
                               CEPH_OSD_FLAG_READ,
                               NULL,
@@ -1238,7 +1222,7 @@ static void rbd_watch_cb(u64 ver, u64 notify_id, u8 opcode, void *data)
        dout("rbd_watch_cb %s notify_id=%lld opcode=%d\n", dev->obj_md_name,
                notify_id, (int)opcode);
        mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
-       rc = __rbd_update_snaps(dev);
+       rc = __rbd_refresh_header(dev);
        mutex_unlock(&ctl_mutex);
        if (rc)
                pr_warning(RBD_DRV_NAME "%d got notification but failed to "
@@ -1521,7 +1505,7 @@ static void rbd_rq_fn(struct request_queue *q)
                                              coll, cur_seg);
                        else
                                rbd_req_read(rq, rbd_dev,
-                                            cur_snap_id(rbd_dev),
+                                            rbd_dev->snap_id,
                                             ofs,
                                             op_size, bio,
                                             coll, cur_seg);
@@ -1592,7 +1576,7 @@ static int rbd_read_header(struct rbd_device *rbd_dev,
 {
        ssize_t rc;
        struct rbd_image_header_ondisk *dh;
-       int snap_count = 0;
+       u32 snap_count = 0;
        u64 ver;
        size_t len;
 
@@ -1656,7 +1640,7 @@ static int rbd_header_add_snap(struct rbd_device *dev,
        struct ceph_mon_client *monc;
 
        /* we should create a snapshot only if we're pointing at the head */
-       if (dev->cur_snap)
+       if (dev->snap_id != CEPH_NOSNAP)
                return -EINVAL;
 
        monc = &dev->rbd_client->client->monc;
@@ -1683,7 +1667,9 @@ static int rbd_header_add_snap(struct rbd_device *dev,
        if (ret < 0)
                return ret;
 
-       dev->header.snapc->seq =  new_snapid;
+       down_write(&dev->header_rwsem);
+       dev->header.snapc->seq = new_snapid;
+       up_write(&dev->header_rwsem);
 
        return 0;
 bad:
@@ -1703,7 +1689,7 @@ static void __rbd_remove_all_snaps(struct rbd_device *rbd_dev)
 /*
  * only read the first part of the ondisk header, without the snaps info
  */
-static int __rbd_update_snaps(struct rbd_device *rbd_dev)
+static int __rbd_refresh_header(struct rbd_device *rbd_dev)
 {
        int ret;
        struct rbd_image_header h;
@@ -1890,7 +1876,7 @@ static ssize_t rbd_image_refresh(struct device *dev,
 
        mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
 
-       rc = __rbd_update_snaps(rbd_dev);
+       rc = __rbd_refresh_header(rbd_dev);
        if (rc < 0)
                ret = rc;
 
@@ -1949,7 +1935,7 @@ static ssize_t rbd_snap_size_show(struct device *dev,
 {
        struct rbd_snap *snap = container_of(dev, struct rbd_snap, dev);
 
-       return sprintf(buf, "%zd\n", snap->size);
+       return sprintf(buf, "%llu\n", (unsigned long long)snap->size);
 }
 
 static ssize_t rbd_snap_id_show(struct device *dev,
@@ -1958,7 +1944,7 @@ static ssize_t rbd_snap_id_show(struct device *dev,
 {
        struct rbd_snap *snap = container_of(dev, struct rbd_snap, dev);
 
-       return sprintf(buf, "%llu\n", (unsigned long long) snap->id);
+       return sprintf(buf, "%llu\n", (unsigned long long)snap->id);
 }
 
 static DEVICE_ATTR(snap_size, S_IRUGO, rbd_snap_size_show, NULL);
@@ -2173,7 +2159,7 @@ static int rbd_init_watch_dev(struct rbd_device *rbd_dev)
                                         rbd_dev->header.obj_version);
                if (ret == -ERANGE) {
                        mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
-                       rc = __rbd_update_snaps(rbd_dev);
+                       rc = __rbd_refresh_header(rbd_dev);
                        mutex_unlock(&ctl_mutex);
                        if (rc < 0)
                                return rc;
@@ -2558,7 +2544,7 @@ static ssize_t rbd_snap_add(struct device *dev,
        if (ret < 0)
                goto err_unlock;
 
-       ret = __rbd_update_snaps(rbd_dev);
+       ret = __rbd_refresh_header(rbd_dev);
        if (ret < 0)
                goto err_unlock;
 
index 4e86393a09cf5c880917fd81d3e67e507a83c0f6..60eed4bdd2e4528ae3c3b8871cd65f85d3c34952 100644 (file)
@@ -526,6 +526,14 @@ static int xen_translate_vdev(int vdevice, int *minor, unsigned int *offset)
        return 0;
 }
 
+static char *encode_disk_name(char *ptr, unsigned int n)
+{
+       if (n >= 26)
+               ptr = encode_disk_name(ptr, n / 26 - 1);
+       *ptr = 'a' + n % 26;
+       return ptr + 1;
+}
+
 static int xlvbd_alloc_gendisk(blkif_sector_t capacity,
                               struct blkfront_info *info,
                               u16 vdisk_info, u16 sector_size)
@@ -536,6 +544,7 @@ static int xlvbd_alloc_gendisk(blkif_sector_t capacity,
        unsigned int offset;
        int minor;
        int nr_parts;
+       char *ptr;
 
        BUG_ON(info->gd != NULL);
        BUG_ON(info->rq != NULL);
@@ -560,7 +569,11 @@ static int xlvbd_alloc_gendisk(blkif_sector_t capacity,
                                        "emulated IDE disks,\n\t choose an xvd device name"
                                        "from xvde on\n", info->vdevice);
        }
-       err = -ENODEV;
+       if (minor >> MINORBITS) {
+               pr_warn("blkfront: %#x's minor (%#x) out of range; ignoring\n",
+                       info->vdevice, minor);
+               return -ENODEV;
+       }
 
        if ((minor % nr_parts) == 0)
                nr_minors = nr_parts;
@@ -574,23 +587,14 @@ static int xlvbd_alloc_gendisk(blkif_sector_t capacity,
        if (gd == NULL)
                goto release;
 
-       if (nr_minors > 1) {
-               if (offset < 26)
-                       sprintf(gd->disk_name, "%s%c", DEV_NAME, 'a' + offset);
-               else
-                       sprintf(gd->disk_name, "%s%c%c", DEV_NAME,
-                               'a' + ((offset / 26)-1), 'a' + (offset % 26));
-       } else {
-               if (offset < 26)
-                       sprintf(gd->disk_name, "%s%c%d", DEV_NAME,
-                               'a' + offset,
-                               minor & (nr_parts - 1));
-               else
-                       sprintf(gd->disk_name, "%s%c%c%d", DEV_NAME,
-                               'a' + ((offset / 26) - 1),
-                               'a' + (offset % 26),
-                               minor & (nr_parts - 1));
-       }
+       strcpy(gd->disk_name, DEV_NAME);
+       ptr = encode_disk_name(gd->disk_name + sizeof(DEV_NAME) - 1, offset);
+       BUG_ON(ptr >= gd->disk_name + DISK_NAME_LEN);
+       if (nr_minors > 1)
+               *ptr = 0;
+       else
+               snprintf(ptr, gd->disk_name + DISK_NAME_LEN - ptr,
+                        "%d", minor & (nr_parts - 1));
 
        gd->major = XENVBD_MAJOR;
        gd->first_minor = minor;
@@ -1496,7 +1500,9 @@ module_init(xlblk_init);
 
 static void __exit xlblk_exit(void)
 {
-       return xenbus_unregister_driver(&blkfront_driver);
+       xenbus_unregister_driver(&blkfront_driver);
+       unregister_blkdev(XENVBD_MAJOR, DEV_NAME);
+       kfree(minors);
 }
 module_exit(xlblk_exit);
 
index 7ef73c919c5da24a0e260a600f97c775e7fb8117..7be9b7288e90eaaf5fab79f34dcac2ceafbc3a2b 100644 (file)
@@ -715,25 +715,6 @@ static inline u64 input_addr_to_sys_addr(struct mem_ctl_info *mci,
                                     input_addr_to_dram_addr(mci, input_addr));
 }
 
-/*
- * Find the minimum and maximum InputAddr values that map to the given @csrow.
- * Pass back these values in *input_addr_min and *input_addr_max.
- */
-static void find_csrow_limits(struct mem_ctl_info *mci, int csrow,
-                             u64 *input_addr_min, u64 *input_addr_max)
-{
-       struct amd64_pvt *pvt;
-       u64 base, mask;
-
-       pvt = mci->pvt_info;
-       BUG_ON((csrow < 0) || (csrow >= pvt->csels[0].b_cnt));
-
-       get_cs_base_and_mask(pvt, csrow, 0, &base, &mask);
-
-       *input_addr_min = base & ~mask;
-       *input_addr_max = base | mask;
-}
-
 /* Map the Error address to a PAGE and PAGE OFFSET. */
 static inline void error_address_to_page_and_offset(u64 error_address,
                                                    u32 *page, u32 *offset)
@@ -1058,6 +1039,37 @@ static void k8_map_sysaddr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr,
        int channel, csrow;
        u32 page, offset;
 
+       error_address_to_page_and_offset(sys_addr, &page, &offset);
+
+       /*
+        * Find out which node the error address belongs to. This may be
+        * different from the node that detected the error.
+        */
+       src_mci = find_mc_by_sys_addr(mci, sys_addr);
+       if (!src_mci) {
+               amd64_mc_err(mci, "failed to map error addr 0x%lx to a node\n",
+                            (unsigned long)sys_addr);
+               edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
+                                    page, offset, syndrome,
+                                    -1, -1, -1,
+                                    EDAC_MOD_STR,
+                                    "failed to map error addr to a node",
+                                    NULL);
+               return;
+       }
+
+       /* Now map the sys_addr to a CSROW */
+       csrow = sys_addr_to_csrow(src_mci, sys_addr);
+       if (csrow < 0) {
+               edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
+                                    page, offset, syndrome,
+                                    -1, -1, -1,
+                                    EDAC_MOD_STR,
+                                    "failed to map error addr to a csrow",
+                                    NULL);
+               return;
+       }
+
        /* CHIPKILL enabled */
        if (pvt->nbcfg & NBCFG_CHIPKILL) {
                channel = get_channel_from_ecc_syndrome(mci, syndrome);
@@ -1067,9 +1079,15 @@ static void k8_map_sysaddr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr,
                         * 2 DIMMs is in error. So we need to ID 'both' of them
                         * as suspect.
                         */
-                       amd64_mc_warn(mci, "unknown syndrome 0x%04x - possible "
-                                          "error reporting race\n", syndrome);
-                       edac_mc_handle_ce_no_info(mci, EDAC_MOD_STR);
+                       amd64_mc_warn(src_mci, "unknown syndrome 0x%04x - "
+                                     "possible error reporting race\n",
+                                     syndrome);
+                       edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
+                                            page, offset, syndrome,
+                                            csrow, -1, -1,
+                                            EDAC_MOD_STR,
+                                            "unknown syndrome - possible error reporting race",
+                                            NULL);
                        return;
                }
        } else {
@@ -1084,28 +1102,10 @@ static void k8_map_sysaddr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr,
                channel = ((sys_addr & BIT(3)) != 0);
        }
 
-       /*
-        * Find out which node the error address belongs to. This may be
-        * different from the node that detected the error.
-        */
-       src_mci = find_mc_by_sys_addr(mci, sys_addr);
-       if (!src_mci) {
-               amd64_mc_err(mci, "failed to map error addr 0x%lx to a node\n",
-                            (unsigned long)sys_addr);
-               edac_mc_handle_ce_no_info(mci, EDAC_MOD_STR);
-               return;
-       }
-
-       /* Now map the sys_addr to a CSROW */
-       csrow = sys_addr_to_csrow(src_mci, sys_addr);
-       if (csrow < 0) {
-               edac_mc_handle_ce_no_info(src_mci, EDAC_MOD_STR);
-       } else {
-               error_address_to_page_and_offset(sys_addr, &page, &offset);
-
-               edac_mc_handle_ce(src_mci, page, offset, syndrome, csrow,
-                                 channel, EDAC_MOD_STR);
-       }
+       edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, src_mci,
+                            page, offset, syndrome,
+                            csrow, channel, -1,
+                            EDAC_MOD_STR, "", NULL);
 }
 
 static int ddr2_cs_size(unsigned i, bool dct_width)
@@ -1611,15 +1611,20 @@ static void f1x_map_sysaddr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr,
        u32 page, offset;
        int nid, csrow, chan = 0;
 
+       error_address_to_page_and_offset(sys_addr, &page, &offset);
+
        csrow = f1x_translate_sysaddr_to_cs(pvt, sys_addr, &nid, &chan);
 
        if (csrow < 0) {
-               edac_mc_handle_ce_no_info(mci, EDAC_MOD_STR);
+               edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
+                                    page, offset, syndrome,
+                                    -1, -1, -1,
+                                    EDAC_MOD_STR,
+                                    "failed to map error addr to a csrow",
+                                    NULL);
                return;
        }
 
-       error_address_to_page_and_offset(sys_addr, &page, &offset);
-
        /*
         * We need the syndromes for channel detection only when we're
         * ganged. Otherwise @chan should already contain the channel at
@@ -1628,16 +1633,10 @@ static void f1x_map_sysaddr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr,
        if (dct_ganging_enabled(pvt))
                chan = get_channel_from_ecc_syndrome(mci, syndrome);
 
-       if (chan >= 0)
-               edac_mc_handle_ce(mci, page, offset, syndrome, csrow, chan,
-                                 EDAC_MOD_STR);
-       else
-               /*
-                * Channel unknown, report all channels on this CSROW as failed.
-                */
-               for (chan = 0; chan < mci->csrows[csrow].nr_channels; chan++)
-                       edac_mc_handle_ce(mci, page, offset, syndrome,
-                                         csrow, chan, EDAC_MOD_STR);
+       edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
+                            page, offset, syndrome,
+                            csrow, chan, -1,
+                            EDAC_MOD_STR, "", NULL);
 }
 
 /*
@@ -1918,7 +1917,12 @@ static void amd64_handle_ce(struct mem_ctl_info *mci, struct mce *m)
        /* Ensure that the Error Address is VALID */
        if (!(m->status & MCI_STATUS_ADDRV)) {
                amd64_mc_err(mci, "HW has no ERROR_ADDRESS available\n");
-               edac_mc_handle_ce_no_info(mci, EDAC_MOD_STR);
+               edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
+                                    0, 0, 0,
+                                    -1, -1, -1,
+                                    EDAC_MOD_STR,
+                                    "HW has no ERROR_ADDRESS available",
+                                    NULL);
                return;
        }
 
@@ -1942,11 +1946,17 @@ static void amd64_handle_ue(struct mem_ctl_info *mci, struct mce *m)
 
        if (!(m->status & MCI_STATUS_ADDRV)) {
                amd64_mc_err(mci, "HW has no ERROR_ADDRESS available\n");
-               edac_mc_handle_ue_no_info(log_mci, EDAC_MOD_STR);
+               edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
+                                    0, 0, 0,
+                                    -1, -1, -1,
+                                    EDAC_MOD_STR,
+                                    "HW has no ERROR_ADDRESS available",
+                                    NULL);
                return;
        }
 
        sys_addr = get_error_address(m);
+       error_address_to_page_and_offset(sys_addr, &page, &offset);
 
        /*
         * Find out which node the error address belongs to. This may be
@@ -1956,7 +1966,11 @@ static void amd64_handle_ue(struct mem_ctl_info *mci, struct mce *m)
        if (!src_mci) {
                amd64_mc_err(mci, "ERROR ADDRESS (0x%lx) NOT mapped to a MC\n",
                                  (unsigned long)sys_addr);
-               edac_mc_handle_ue_no_info(log_mci, EDAC_MOD_STR);
+               edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
+                                    page, offset, 0,
+                                    -1, -1, -1,
+                                    EDAC_MOD_STR,
+                                    "ERROR ADDRESS NOT mapped to a MC", NULL);
                return;
        }
 
@@ -1966,10 +1980,17 @@ static void amd64_handle_ue(struct mem_ctl_info *mci, struct mce *m)
        if (csrow < 0) {
                amd64_mc_err(mci, "ERROR_ADDRESS (0x%lx) NOT mapped to CS\n",
                                  (unsigned long)sys_addr);
-               edac_mc_handle_ue_no_info(log_mci, EDAC_MOD_STR);
+               edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
+                                    page, offset, 0,
+                                    -1, -1, -1,
+                                    EDAC_MOD_STR,
+                                    "ERROR ADDRESS NOT mapped to CS",
+                                    NULL);
        } else {
-               error_address_to_page_and_offset(sys_addr, &page, &offset);
-               edac_mc_handle_ue(log_mci, page, offset, csrow, EDAC_MOD_STR);
+               edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
+                                    page, offset, 0,
+                                    csrow, -1, -1,
+                                    EDAC_MOD_STR, "", NULL);
        }
 }
 
@@ -2171,7 +2192,7 @@ static u32 amd64_csrow_nr_pages(struct amd64_pvt *pvt, u8 dct, int csrow_nr)
        nr_pages = pvt->ops->dbam_to_cs(pvt, dct, cs_mode) << (20 - PAGE_SHIFT);
 
        debugf0("  (csrow=%d) DBAM map index= %d\n", csrow_nr, cs_mode);
-       debugf0("    nr_pages= %u  channel-count = %d\n",
+       debugf0("    nr_pages/channel= %u  channel-count = %d\n",
                nr_pages, pvt->channel_count);
 
        return nr_pages;
@@ -2185,9 +2206,12 @@ static int init_csrows(struct mem_ctl_info *mci)
 {
        struct csrow_info *csrow;
        struct amd64_pvt *pvt = mci->pvt_info;
-       u64 input_addr_min, input_addr_max, sys_addr, base, mask;
+       u64 base, mask;
        u32 val;
-       int i, empty = 1;
+       int i, j, empty = 1;
+       enum mem_type mtype;
+       enum edac_type edac_mode;
+       int nr_pages = 0;
 
        amd64_read_pci_cfg(pvt->F3, NBCFG, &val);
 
@@ -2211,41 +2235,32 @@ static int init_csrows(struct mem_ctl_info *mci)
 
                empty = 0;
                if (csrow_enabled(i, 0, pvt))
-                       csrow->nr_pages = amd64_csrow_nr_pages(pvt, 0, i);
+                       nr_pages = amd64_csrow_nr_pages(pvt, 0, i);
                if (csrow_enabled(i, 1, pvt))
-                       csrow->nr_pages += amd64_csrow_nr_pages(pvt, 1, i);
-               find_csrow_limits(mci, i, &input_addr_min, &input_addr_max);
-               sys_addr = input_addr_to_sys_addr(mci, input_addr_min);
-               csrow->first_page = (u32) (sys_addr >> PAGE_SHIFT);
-               sys_addr = input_addr_to_sys_addr(mci, input_addr_max);
-               csrow->last_page = (u32) (sys_addr >> PAGE_SHIFT);
+                       nr_pages += amd64_csrow_nr_pages(pvt, 1, i);
 
                get_cs_base_and_mask(pvt, i, 0, &base, &mask);
-               csrow->page_mask = ~mask;
                /* 8 bytes of resolution */
 
-               csrow->mtype = amd64_determine_memory_type(pvt, i);
+               mtype = amd64_determine_memory_type(pvt, i);
 
                debugf1("  for MC node %d csrow %d:\n", pvt->mc_node_id, i);
-               debugf1("    input_addr_min: 0x%lx input_addr_max: 0x%lx\n",
-                       (unsigned long)input_addr_min,
-                       (unsigned long)input_addr_max);
-               debugf1("    sys_addr: 0x%lx  page_mask: 0x%lx\n",
-                       (unsigned long)sys_addr, csrow->page_mask);
-               debugf1("    nr_pages: %u  first_page: 0x%lx "
-                       "last_page: 0x%lx\n",
-                       (unsigned)csrow->nr_pages,
-                       csrow->first_page, csrow->last_page);
+               debugf1("    nr_pages: %u\n", nr_pages * pvt->channel_count);
 
                /*
                 * determine whether CHIPKILL or JUST ECC or NO ECC is operating
                 */
                if (pvt->nbcfg & NBCFG_ECC_ENABLE)
-                       csrow->edac_mode =
-                           (pvt->nbcfg & NBCFG_CHIPKILL) ?
-                           EDAC_S4ECD4ED : EDAC_SECDED;
+                       edac_mode = (pvt->nbcfg & NBCFG_CHIPKILL) ?
+                                   EDAC_S4ECD4ED : EDAC_SECDED;
                else
-                       csrow->edac_mode = EDAC_NONE;
+                       edac_mode = EDAC_NONE;
+
+               for (j = 0; j < pvt->channel_count; j++) {
+                       csrow->channels[j].dimm->mtype = mtype;
+                       csrow->channels[j].dimm->edac_mode = edac_mode;
+                       csrow->channels[j].dimm->nr_pages = nr_pages;
+               }
        }
 
        return empty;
@@ -2540,6 +2555,7 @@ static int amd64_init_one_instance(struct pci_dev *F2)
        struct amd64_pvt *pvt = NULL;
        struct amd64_family_type *fam_type = NULL;
        struct mem_ctl_info *mci = NULL;
+       struct edac_mc_layer layers[2];
        int err = 0, ret;
        u8 nid = get_node_id(F2);
 
@@ -2574,7 +2590,13 @@ static int amd64_init_one_instance(struct pci_dev *F2)
                goto err_siblings;
 
        ret = -ENOMEM;
-       mci = edac_mc_alloc(0, pvt->csels[0].b_cnt, pvt->channel_count, nid);
+       layers[0].type = EDAC_MC_LAYER_CHIP_SELECT;
+       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;
+       layers[1].is_virt_csrow = false;
+       mci = edac_mc_alloc(nid, ARRAY_SIZE(layers), layers, 0);
        if (!mci)
                goto err_siblings;
 
index f8fd3c807bde02c93de9d5e912cfc94bbd9b1916..9774d443fa57616a9f7e64ba06a2fae3e3bc2f92 100644 (file)
@@ -29,7 +29,6 @@
        edac_mc_chipset_printk(mci, level, "amd76x", fmt, ##arg)
 
 #define AMD76X_NR_CSROWS 8
-#define AMD76X_NR_CHANS  1
 #define AMD76X_NR_DIMMS  4
 
 /* AMD 76x register addresses - device 0 function 0 - PCI bridge */
@@ -146,8 +145,10 @@ static int amd76x_process_error_info(struct mem_ctl_info *mci,
 
                if (handle_errors) {
                        row = (info->ecc_mode_status >> 4) & 0xf;
-                       edac_mc_handle_ue(mci, mci->csrows[row].first_page, 0,
-                                       row, mci->ctl_name);
+                       edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
+                                            mci->csrows[row].first_page, 0, 0,
+                                            row, 0, -1,
+                                            mci->ctl_name, "", NULL);
                }
        }
 
@@ -159,8 +160,10 @@ static int amd76x_process_error_info(struct mem_ctl_info *mci,
 
                if (handle_errors) {
                        row = info->ecc_mode_status & 0xf;
-                       edac_mc_handle_ce(mci, mci->csrows[row].first_page, 0,
-                                       0, row, 0, mci->ctl_name);
+                       edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
+                                            mci->csrows[row].first_page, 0, 0,
+                                            row, 0, -1,
+                                            mci->ctl_name, "", NULL);
                }
        }
 
@@ -186,11 +189,13 @@ static void amd76x_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev,
                        enum edac_type edac_mode)
 {
        struct csrow_info *csrow;
+       struct dimm_info *dimm;
        u32 mba, mba_base, mba_mask, dms;
        int index;
 
        for (index = 0; index < mci->nr_csrows; index++) {
                csrow = &mci->csrows[index];
+               dimm = csrow->channels[0].dimm;
 
                /* find the DRAM Chip Select Base address and mask */
                pci_read_config_dword(pdev,
@@ -203,13 +208,13 @@ static void amd76x_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev,
                mba_mask = ((mba & 0xff80) << 16) | 0x7fffffUL;
                pci_read_config_dword(pdev, AMD76X_DRAM_MODE_STATUS, &dms);
                csrow->first_page = mba_base >> PAGE_SHIFT;
-               csrow->nr_pages = (mba_mask + 1) >> PAGE_SHIFT;
-               csrow->last_page = csrow->first_page + csrow->nr_pages - 1;
+               dimm->nr_pages = (mba_mask + 1) >> PAGE_SHIFT;
+               csrow->last_page = csrow->first_page + dimm->nr_pages - 1;
                csrow->page_mask = mba_mask >> PAGE_SHIFT;
-               csrow->grain = csrow->nr_pages << PAGE_SHIFT;
-               csrow->mtype = MEM_RDDR;
-               csrow->dtype = ((dms >> index) & 0x1) ? DEV_X4 : DEV_UNKNOWN;
-               csrow->edac_mode = edac_mode;
+               dimm->grain = dimm->nr_pages << PAGE_SHIFT;
+               dimm->mtype = MEM_RDDR;
+               dimm->dtype = ((dms >> index) & 0x1) ? DEV_X4 : DEV_UNKNOWN;
+               dimm->edac_mode = edac_mode;
        }
 }
 
@@ -230,7 +235,8 @@ static int amd76x_probe1(struct pci_dev *pdev, int dev_idx)
                EDAC_SECDED,
                EDAC_SECDED
        };
-       struct mem_ctl_info *mci = NULL;
+       struct mem_ctl_info *mci;
+       struct edac_mc_layer layers[2];
        u32 ems;
        u32 ems_mode;
        struct amd76x_error_info discard;
@@ -238,11 +244,17 @@ static int amd76x_probe1(struct pci_dev *pdev, int dev_idx)
        debugf0("%s()\n", __func__);
        pci_read_config_dword(pdev, AMD76X_ECC_MODE_STATUS, &ems);
        ems_mode = (ems >> 10) & 0x3;
-       mci = edac_mc_alloc(0, AMD76X_NR_CSROWS, AMD76X_NR_CHANS, 0);
 
-       if (mci == NULL) {
+       layers[0].type = EDAC_MC_LAYER_CHIP_SELECT;
+       layers[0].size = AMD76X_NR_CSROWS;
+       layers[0].is_virt_csrow = true;
+       layers[1].type = EDAC_MC_LAYER_CHANNEL;
+       layers[1].size = 1;
+       layers[1].is_virt_csrow = false;
+       mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers, 0);
+
+       if (mci == NULL)
                return -ENOMEM;
-       }
 
        debugf0("%s(): mci = %p\n", __func__, mci);
        mci->dev = &pdev->dev;
index 9a6a274e6925f2c03edf2a92fda72b28f1d65aa6..69ee6aab5c716fefd0b919a5da4b46ef5c7c4a64 100644 (file)
@@ -48,8 +48,9 @@ static void cell_edac_count_ce(struct mem_ctl_info *mci, int chan, u64 ar)
        syndrome = (ar & 0x000000001fe00000ul) >> 21;
 
        /* TODO: Decoding of the error address */
-       edac_mc_handle_ce(mci, csrow->first_page + pfn, offset,
-                         syndrome, 0, chan, "");
+       edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
+                            csrow->first_page + pfn, offset, syndrome,
+                            0, chan, -1, "", "", NULL);
 }
 
 static void cell_edac_count_ue(struct mem_ctl_info *mci, int chan, u64 ar)
@@ -69,7 +70,9 @@ static void cell_edac_count_ue(struct mem_ctl_info *mci, int chan, u64 ar)
        offset = address & ~PAGE_MASK;
 
        /* TODO: Decoding of the error address */
-       edac_mc_handle_ue(mci, csrow->first_page + pfn, offset, 0, "");
+       edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
+                            csrow->first_page + pfn, offset, 0,
+                            0, chan, -1, "", "", NULL);
 }
 
 static void cell_edac_check(struct mem_ctl_info *mci)
@@ -124,8 +127,11 @@ static void cell_edac_check(struct mem_ctl_info *mci)
 static void __devinit cell_edac_init_csrows(struct mem_ctl_info *mci)
 {
        struct csrow_info               *csrow = &mci->csrows[0];
+       struct dimm_info                *dimm;
        struct cell_edac_priv           *priv = mci->pvt_info;
        struct device_node              *np;
+       int                             j;
+       u32                             nr_pages;
 
        for (np = NULL;
             (np = of_find_node_by_name(np, "memory")) != NULL;) {
@@ -140,15 +146,20 @@ static void __devinit cell_edac_init_csrows(struct mem_ctl_info *mci)
                if (of_node_to_nid(np) != priv->node)
                        continue;
                csrow->first_page = r.start >> PAGE_SHIFT;
-               csrow->nr_pages = resource_size(&r) >> PAGE_SHIFT;
-               csrow->last_page = csrow->first_page + csrow->nr_pages - 1;
-               csrow->mtype = MEM_XDR;
-               csrow->edac_mode = EDAC_SECDED;
+               nr_pages = resource_size(&r) >> PAGE_SHIFT;
+               csrow->last_page = csrow->first_page + nr_pages - 1;
+
+               for (j = 0; j < csrow->nr_channels; j++) {
+                       dimm = csrow->channels[j].dimm;
+                       dimm->mtype = MEM_XDR;
+                       dimm->edac_mode = EDAC_SECDED;
+                       dimm->nr_pages = nr_pages / csrow->nr_channels;
+               }
                dev_dbg(mci->dev,
                        "Initialized on node %d, chanmask=0x%x,"
                        " first_page=0x%lx, nr_pages=0x%x\n",
                        priv->node, priv->chanmask,
-                       csrow->first_page, csrow->nr_pages);
+                       csrow->first_page, nr_pages);
                break;
        }
 }
@@ -157,9 +168,10 @@ static int __devinit cell_edac_probe(struct platform_device *pdev)
 {
        struct cbe_mic_tm_regs __iomem  *regs;
        struct mem_ctl_info             *mci;
+       struct edac_mc_layer            layers[2];
        struct cell_edac_priv           *priv;
        u64                             reg;
-       int                             rc, chanmask;
+       int                             rc, chanmask, num_chans;
 
        regs = cbe_get_cpu_mic_tm_regs(cbe_node_to_cpu(pdev->id));
        if (regs == NULL)
@@ -184,8 +196,16 @@ static int __devinit cell_edac_probe(struct platform_device *pdev)
                in_be64(&regs->mic_fir));
 
        /* Allocate & init EDAC MC data structure */
-       mci = edac_mc_alloc(sizeof(struct cell_edac_priv), 1,
-                           chanmask == 3 ? 2 : 1, pdev->id);
+       num_chans = chanmask == 3 ? 2 : 1;
+
+       layers[0].type = EDAC_MC_LAYER_CHIP_SELECT;
+       layers[0].size = 1;
+       layers[0].is_virt_csrow = true;
+       layers[1].type = EDAC_MC_LAYER_CHANNEL;
+       layers[1].size = num_chans;
+       layers[1].is_virt_csrow = false;
+       mci = edac_mc_alloc(pdev->id, ARRAY_SIZE(layers), layers,
+                           sizeof(struct cell_edac_priv));
        if (mci == NULL)
                return -ENOMEM;
        priv = mci->pvt_info;
index a774c0ddaf5b06f05e19ddacccf0423a824f1423..e22030a9de66fbc0c38fd2d0b274b1464dda7a2f 100644 (file)
@@ -329,9 +329,10 @@ static void cpc925_init_csrows(struct mem_ctl_info *mci)
 {
        struct cpc925_mc_pdata *pdata = mci->pvt_info;
        struct csrow_info *csrow;
-       int index;
+       struct dimm_info *dimm;
+       int index, j;
        u32 mbmr, mbbar, bba;
-       unsigned long row_size, last_nr_pages = 0;
+       unsigned long row_size, nr_pages, last_nr_pages = 0;
 
        get_total_mem(pdata);
 
@@ -350,36 +351,41 @@ static void cpc925_init_csrows(struct mem_ctl_info *mci)
 
                row_size = bba * (1UL << 28);   /* 256M */
                csrow->first_page = last_nr_pages;
-               csrow->nr_pages = row_size >> PAGE_SHIFT;
-               csrow->last_page = csrow->first_page + csrow->nr_pages - 1;
+               nr_pages = row_size >> PAGE_SHIFT;
+               csrow->last_page = csrow->first_page + nr_pages - 1;
                last_nr_pages = csrow->last_page + 1;
 
-               csrow->mtype = MEM_RDDR;
-               csrow->edac_mode = EDAC_SECDED;
-
-               switch (csrow->nr_channels) {
-               case 1: /* Single channel */
-                       csrow->grain = 32; /* four-beat burst of 32 bytes */
-                       break;
-               case 2: /* Dual channel */
-               default:
-                       csrow->grain = 64; /* four-beat burst of 64 bytes */
-                       break;
-               }
-
-               switch ((mbmr & MBMR_MODE_MASK) >> MBMR_MODE_SHIFT) {
-               case 6: /* 0110, no way to differentiate X8 VS X16 */
-               case 5: /* 0101 */
-               case 8: /* 1000 */
-                       csrow->dtype = DEV_X16;
-                       break;
-               case 7: /* 0111 */
-               case 9: /* 1001 */
-                       csrow->dtype = DEV_X8;
-                       break;
-               default:
-                       csrow->dtype = DEV_UNKNOWN;
-                       break;
+               for (j = 0; j < csrow->nr_channels; j++) {
+                       dimm = csrow->channels[j].dimm;
+
+                       dimm->nr_pages = nr_pages / csrow->nr_channels;
+                       dimm->mtype = MEM_RDDR;
+                       dimm->edac_mode = EDAC_SECDED;
+
+                       switch (csrow->nr_channels) {
+                       case 1: /* Single channel */
+                               dimm->grain = 32; /* four-beat burst of 32 bytes */
+                               break;
+                       case 2: /* Dual channel */
+                       default:
+                               dimm->grain = 64; /* four-beat burst of 64 bytes */
+                               break;
+                       }
+
+                       switch ((mbmr & MBMR_MODE_MASK) >> MBMR_MODE_SHIFT) {
+                       case 6: /* 0110, no way to differentiate X8 VS X16 */
+                       case 5: /* 0101 */
+                       case 8: /* 1000 */
+                               dimm->dtype = DEV_X16;
+                               break;
+                       case 7: /* 0111 */
+                       case 9: /* 1001 */
+                               dimm->dtype = DEV_X8;
+                               break;
+                       default:
+                               dimm->dtype = DEV_UNKNOWN;
+                               break;
+                       }
                }
        }
 }
@@ -549,13 +555,18 @@ static void cpc925_mc_check(struct mem_ctl_info *mci)
        if (apiexcp & CECC_EXCP_DETECTED) {
                cpc925_mc_printk(mci, KERN_INFO, "DRAM CECC Fault\n");
                channel = cpc925_mc_find_channel(mci, syndrome);
-               edac_mc_handle_ce(mci, pfn, offset, syndrome,
-                                 csrow, channel, mci->ctl_name);
+               edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
+                                    pfn, offset, syndrome,
+                                    csrow, channel, -1,
+                                    mci->ctl_name, "", NULL);
        }
 
        if (apiexcp & UECC_EXCP_DETECTED) {
                cpc925_mc_printk(mci, KERN_INFO, "DRAM UECC Fault\n");
-               edac_mc_handle_ue(mci, pfn, offset, csrow, mci->ctl_name);
+               edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
+                                    pfn, offset, 0,
+                                    csrow, -1, -1,
+                                    mci->ctl_name, "", NULL);
        }
 
        cpc925_mc_printk(mci, KERN_INFO, "Dump registers:\n");
@@ -927,6 +938,7 @@ static int __devinit cpc925_probe(struct platform_device *pdev)
 {
        static int edac_mc_idx;
        struct mem_ctl_info *mci;
+       struct edac_mc_layer layers[2];
        void __iomem *vbase;
        struct cpc925_mc_pdata *pdata;
        struct resource *r;
@@ -962,9 +974,16 @@ static int __devinit cpc925_probe(struct platform_device *pdev)
                goto err2;
        }
 
-       nr_channels = cpc925_mc_get_channels(vbase);
-       mci = edac_mc_alloc(sizeof(struct cpc925_mc_pdata),
-                       CPC925_NR_CSROWS, nr_channels + 1, edac_mc_idx);
+       nr_channels = cpc925_mc_get_channels(vbase) + 1;
+
+       layers[0].type = EDAC_MC_LAYER_CHIP_SELECT;
+       layers[0].size = CPC925_NR_CSROWS;
+       layers[0].is_virt_csrow = true;
+       layers[1].type = EDAC_MC_LAYER_CHANNEL;
+       layers[1].size = nr_channels;
+       layers[1].is_virt_csrow = false;
+       mci = edac_mc_alloc(edac_mc_idx, ARRAY_SIZE(layers), layers,
+                           sizeof(struct cpc925_mc_pdata));
        if (!mci) {
                cpc925_printk(KERN_ERR, "No memory for mem_ctl_info\n");
                res = -ENOMEM;
index 41223261ede9bb858cb5424431f9aeb04a70e36f..3186512c97393f80e1da0748cf496746ba91e52e 100644 (file)
@@ -4,7 +4,11 @@
  * This file may be distributed under the terms of the
  * GNU General Public License.
  *
- * See "enum e752x_chips" below for supported chipsets
+ * Implement support for the e7520, E7525, e7320 and i3100 memory controllers.
+ *
+ * Datasheets:
+ *     http://www.intel.in/content/www/in/en/chipsets/e7525-memory-controller-hub-datasheet.html
+ *     ftp://download.intel.com/design/intarch/datashts/31345803.pdf
  *
  * Written by Tom Zimmerman
  *
@@ -13,8 +17,6 @@
  *     Wang Zhenyu at intel.com
  *     Dave Jiang at mvista.com
  *
- * $Id: edac_e752x.c,v 1.5.2.11 2005/10/05 00:43:44 dsp_llnl Exp $
- *
  */
 
 #include <linux/module.h>
@@ -187,6 +189,25 @@ enum e752x_chips {
        I3100 = 3
 };
 
+/*
+ * Those chips Support single-rank and dual-rank memories only.
+ *
+ * On e752x chips, the odd rows are present only on dual-rank memories.
+ * Dividing the rank by two will provide the dimm#
+ *
+ * i3100 MC has a different mapping: it supports only 4 ranks.
+ *
+ * The mapping is (from 1 to n):
+ *     slot       single-ranked        double-ranked
+ *     dimm #1 -> rank #4              NA
+ *     dimm #2 -> rank #3              NA
+ *     dimm #3 -> rank #2              Ranks 2 and 3
+ *     dimm #4 -> rank $1              Ranks 1 and 4
+ *
+ * FIXME: The current mapping for i3100 considers that it supports up to 8
+ *       ranks/chanel, but datasheet says that the MC supports only 4 ranks.
+ */
+
 struct e752x_pvt {
        struct pci_dev *bridge_ck;
        struct pci_dev *dev_d0f0;
@@ -350,8 +371,10 @@ static void do_process_ce(struct mem_ctl_info *mci, u16 error_one,
        channel = !(error_one & 1);
 
        /* e752x mc reads 34:6 of the DRAM linear address */
-       edac_mc_handle_ce(mci, page, offset_in_page(sec1_add << 4),
-                       sec1_syndrome, row, channel, "e752x CE");
+       edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
+                            page, offset_in_page(sec1_add << 4), sec1_syndrome,
+                            row, channel, -1,
+                            "e752x CE", "", NULL);
 }
 
 static inline void process_ce(struct mem_ctl_info *mci, u16 error_one,
@@ -385,9 +408,12 @@ static void do_process_ue(struct mem_ctl_info *mci, u16 error_one,
                        edac_mc_find_csrow_by_page(mci, block_page);
 
                /* e752x mc reads 34:6 of the DRAM linear address */
-               edac_mc_handle_ue(mci, block_page,
-                               offset_in_page(error_2b << 4),
-                               row, "e752x UE from Read");
+               edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
+                                       block_page,
+                                       offset_in_page(error_2b << 4), 0,
+                                        row, -1, -1,
+                                       "e752x UE from Read", "", NULL);
+
        }
        if (error_one & 0x0404) {
                error_2b = scrb_add;
@@ -401,9 +427,11 @@ static void do_process_ue(struct mem_ctl_info *mci, u16 error_one,
                        edac_mc_find_csrow_by_page(mci, block_page);
 
                /* e752x mc reads 34:6 of the DRAM linear address */
-               edac_mc_handle_ue(mci, block_page,
-                               offset_in_page(error_2b << 4),
-                               row, "e752x UE from Scruber");
+               edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
+                                       block_page,
+                                       offset_in_page(error_2b << 4), 0,
+                                       row, -1, -1,
+                                       "e752x UE from Scruber", "", NULL);
        }
 }
 
@@ -426,7 +454,9 @@ static inline void process_ue_no_info_wr(struct mem_ctl_info *mci,
                return;
 
        debugf3("%s()\n", __func__);
-       edac_mc_handle_ue_no_info(mci, "e752x UE log memory write");
+       edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 0, 0, 0,
+                            -1, -1, -1,
+                            "e752x UE log memory write", "", NULL);
 }
 
 static void do_process_ded_retry(struct mem_ctl_info *mci, u16 error,
@@ -1044,7 +1074,7 @@ static void e752x_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev,
        int drc_drbg;           /* DRB granularity 0=64mb, 1=128mb */
        int drc_ddim;           /* DRAM Data Integrity Mode 0=none, 2=edac */
        u8 value;
-       u32 dra, drc, cumul_size;
+       u32 dra, drc, cumul_size, i, nr_pages;
 
        dra = 0;
        for (index = 0; index < 4; index++) {
@@ -1053,7 +1083,7 @@ static void e752x_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev,
                dra |= dra_reg << (index * 8);
        }
        pci_read_config_dword(pdev, E752X_DRC, &drc);
-       drc_chan = dual_channel_active(ddrcsr);
+       drc_chan = dual_channel_active(ddrcsr) ? 1 : 0;
        drc_drbg = drc_chan + 1;        /* 128 in dual mode, 64 in single */
        drc_ddim = (drc >> 20) & 0x3;
 
@@ -1078,26 +1108,33 @@ static void e752x_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev,
 
                csrow->first_page = last_cumul_size;
                csrow->last_page = cumul_size - 1;
-               csrow->nr_pages = cumul_size - last_cumul_size;
+               nr_pages = cumul_size - last_cumul_size;
                last_cumul_size = cumul_size;
-               csrow->grain = 1 << 12; /* 4KiB - resolution of CELOG */
-               csrow->mtype = MEM_RDDR;        /* only one type supported */
-               csrow->dtype = mem_dev ? DEV_X4 : DEV_X8;
-
-               /*
-                * if single channel or x8 devices then SECDED
-                * if dual channel and x4 then S4ECD4ED
-                */
-               if (drc_ddim) {
-                       if (drc_chan && mem_dev) {
-                               csrow->edac_mode = EDAC_S4ECD4ED;
-                               mci->edac_cap |= EDAC_FLAG_S4ECD4ED;
-                       } else {
-                               csrow->edac_mode = EDAC_SECDED;
-                               mci->edac_cap |= EDAC_FLAG_SECDED;
-                       }
-               } else
-                       csrow->edac_mode = EDAC_NONE;
+
+               for (i = 0; i < csrow->nr_channels; i++) {
+                       struct dimm_info *dimm = csrow->channels[i].dimm;
+
+                       debugf3("Initializing rank at (%i,%i)\n", index, i);
+                       dimm->nr_pages = nr_pages / csrow->nr_channels;
+                       dimm->grain = 1 << 12;  /* 4KiB - resolution of CELOG */
+                       dimm->mtype = MEM_RDDR; /* only one type supported */
+                       dimm->dtype = mem_dev ? DEV_X4 : DEV_X8;
+
+                       /*
+                       * if single channel or x8 devices then SECDED
+                       * if dual channel and x4 then S4ECD4ED
+                       */
+                       if (drc_ddim) {
+                               if (drc_chan && mem_dev) {
+                                       dimm->edac_mode = EDAC_S4ECD4ED;
+                                       mci->edac_cap |= EDAC_FLAG_S4ECD4ED;
+                               } else {
+                                       dimm->edac_mode = EDAC_SECDED;
+                                       mci->edac_cap |= EDAC_FLAG_SECDED;
+                               }
+                       } else
+                               dimm->edac_mode = EDAC_NONE;
+               }
        }
 }
 
@@ -1226,6 +1263,7 @@ static int e752x_probe1(struct pci_dev *pdev, int dev_idx)
        u16 pci_data;
        u8 stat8;
        struct mem_ctl_info *mci;
+       struct edac_mc_layer layers[2];
        struct e752x_pvt *pvt;
        u16 ddrcsr;
        int drc_chan;           /* Number of channels 0=1chan,1=2chan */
@@ -1252,11 +1290,15 @@ static int e752x_probe1(struct pci_dev *pdev, int dev_idx)
        /* Dual channel = 1, Single channel = 0 */
        drc_chan = dual_channel_active(ddrcsr);
 
-       mci = edac_mc_alloc(sizeof(*pvt), E752X_NR_CSROWS, drc_chan + 1, 0);
-
-       if (mci == NULL) {
+       layers[0].type = EDAC_MC_LAYER_CHIP_SELECT;
+       layers[0].size = E752X_NR_CSROWS;
+       layers[0].is_virt_csrow = true;
+       layers[1].type = EDAC_MC_LAYER_CHANNEL;
+       layers[1].size = drc_chan + 1;
+       layers[1].is_virt_csrow = false;
+       mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers, sizeof(*pvt));
+       if (mci == NULL)
                return -ENOMEM;
-       }
 
        debugf3("%s(): init mci\n", __func__);
        mci->mtype_cap = MEM_FLAG_RDDR;
index 68dea87b72e639448b2eb75ed0f219be3e6b9d39..9a9c1a5467977ca6bb69042651310b2830275757 100644 (file)
@@ -10,6 +10,9 @@
  * Based on work by Dan Hollis <goemon at anime dot net> and others.
  *     http://www.anime.net/~goemon/linux-ecc/
  *
+ * Datasheet:
+ *     http://www.intel.com/content/www/us/en/chipsets/e7501-chipset-memory-controller-hub-datasheet.html
+ *
  * Contributors:
  *     Eric Biederman (Linux Networx)
  *     Tom Zimmerman (Linux Networx)
@@ -71,7 +74,7 @@
 #endif                         /* PCI_DEVICE_ID_INTEL_7505_1_ERR */
 
 #define E7XXX_NR_CSROWS                8       /* number of csrows */
-#define E7XXX_NR_DIMMS         8       /* FIXME - is this correct? */
+#define E7XXX_NR_DIMMS         8       /* 2 channels, 4 dimms/channel */
 
 /* E7XXX register addresses - device 0 function 0 */
 #define E7XXX_DRB              0x60    /* DRAM row boundary register (8b) */
@@ -216,13 +219,15 @@ static void process_ce(struct mem_ctl_info *mci, struct e7xxx_error_info *info)
        row = edac_mc_find_csrow_by_page(mci, page);
        /* convert syndrome to channel */
        channel = e7xxx_find_channel(syndrome);
-       edac_mc_handle_ce(mci, page, 0, syndrome, row, channel, "e7xxx CE");
+       edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, page, 0, syndrome,
+                            row, channel, -1, "e7xxx CE", "", NULL);
 }
 
 static void process_ce_no_info(struct mem_ctl_info *mci)
 {
        debugf3("%s()\n", __func__);
-       edac_mc_handle_ce_no_info(mci, "e7xxx CE log register overflow");
+       edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 0, 0, 0, -1, -1, -1,
+                            "e7xxx CE log register overflow", "", NULL);
 }
 
 static void process_ue(struct mem_ctl_info *mci, struct e7xxx_error_info *info)
@@ -236,13 +241,17 @@ static void process_ue(struct mem_ctl_info *mci, struct e7xxx_error_info *info)
        /* FIXME - should use PAGE_SHIFT */
        block_page = error_2b >> 6;     /* convert to 4k address */
        row = edac_mc_find_csrow_by_page(mci, block_page);
-       edac_mc_handle_ue(mci, block_page, 0, row, "e7xxx UE");
+
+       edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, block_page, 0, 0,
+                            row, -1, -1, "e7xxx UE", "", NULL);
 }
 
 static void process_ue_no_info(struct mem_ctl_info *mci)
 {
        debugf3("%s()\n", __func__);
-       edac_mc_handle_ue_no_info(mci, "e7xxx UE log register overflow");
+
+       edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 0, 0, 0, -1, -1, -1,
+                            "e7xxx UE log register overflow", "", NULL);
 }
 
 static void e7xxx_get_error_info(struct mem_ctl_info *mci,
@@ -347,11 +356,12 @@ static void e7xxx_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev,
                        int dev_idx, u32 drc)
 {
        unsigned long last_cumul_size;
-       int index;
+       int index, j;
        u8 value;
-       u32 dra, cumul_size;
+       u32 dra, cumul_size, nr_pages;
        int drc_chan, drc_drbg, drc_ddim, mem_dev;
        struct csrow_info *csrow;
+       struct dimm_info *dimm;
 
        pci_read_config_dword(pdev, E7XXX_DRA, &dra);
        drc_chan = dual_channel_active(drc, dev_idx);
@@ -379,26 +389,32 @@ static void e7xxx_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev,
 
                csrow->first_page = last_cumul_size;
                csrow->last_page = cumul_size - 1;
-               csrow->nr_pages = cumul_size - last_cumul_size;
+               nr_pages = cumul_size - last_cumul_size;
                last_cumul_size = cumul_size;
-               csrow->grain = 1 << 12; /* 4KiB - resolution of CELOG */
-               csrow->mtype = MEM_RDDR;        /* only one type supported */
-               csrow->dtype = mem_dev ? DEV_X4 : DEV_X8;
-
-               /*
-                * if single channel or x8 devices then SECDED
-                * if dual channel and x4 then S4ECD4ED
-                */
-               if (drc_ddim) {
-                       if (drc_chan && mem_dev) {
-                               csrow->edac_mode = EDAC_S4ECD4ED;
-                               mci->edac_cap |= EDAC_FLAG_S4ECD4ED;
-                       } else {
-                               csrow->edac_mode = EDAC_SECDED;
-                               mci->edac_cap |= EDAC_FLAG_SECDED;
-                       }
-               } else
-                       csrow->edac_mode = EDAC_NONE;
+
+               for (j = 0; j < drc_chan + 1; j++) {
+                       dimm = csrow->channels[j].dimm;
+
+                       dimm->nr_pages = nr_pages / (drc_chan + 1);
+                       dimm->grain = 1 << 12;  /* 4KiB - resolution of CELOG */
+                       dimm->mtype = MEM_RDDR; /* only one type supported */
+                       dimm->dtype = mem_dev ? DEV_X4 : DEV_X8;
+
+                       /*
+                       * if single channel or x8 devices then SECDED
+                       * if dual channel and x4 then S4ECD4ED
+                       */
+                       if (drc_ddim) {
+                               if (drc_chan && mem_dev) {
+                                       dimm->edac_mode = EDAC_S4ECD4ED;
+                                       mci->edac_cap |= EDAC_FLAG_S4ECD4ED;
+                               } else {
+                                       dimm->edac_mode = EDAC_SECDED;
+                                       mci->edac_cap |= EDAC_FLAG_SECDED;
+                               }
+                       } else
+                               dimm->edac_mode = EDAC_NONE;
+               }
        }
 }
 
@@ -406,6 +422,7 @@ static int e7xxx_probe1(struct pci_dev *pdev, int dev_idx)
 {
        u16 pci_data;
        struct mem_ctl_info *mci = NULL;
+       struct edac_mc_layer layers[2];
        struct e7xxx_pvt *pvt = NULL;
        u32 drc;
        int drc_chan;
@@ -416,8 +433,21 @@ static int e7xxx_probe1(struct pci_dev *pdev, int dev_idx)
        pci_read_config_dword(pdev, E7XXX_DRC, &drc);
 
        drc_chan = dual_channel_active(drc, dev_idx);
-       mci = edac_mc_alloc(sizeof(*pvt), E7XXX_NR_CSROWS, drc_chan + 1, 0);
-
+       /*
+        * According with the datasheet, this device has a maximum of
+        * 4 DIMMS per channel, either single-rank or dual-rank. So, the
+        * total amount of dimms is 8 (E7XXX_NR_DIMMS).
+        * That means that the DIMM is mapped as CSROWs, and the channel
+        * will map the rank. So, an error to either channel should be
+        * attributed to the same dimm.
+        */
+       layers[0].type = EDAC_MC_LAYER_CHIP_SELECT;
+       layers[0].size = E7XXX_NR_CSROWS;
+       layers[0].is_virt_csrow = true;
+       layers[1].type = EDAC_MC_LAYER_CHANNEL;
+       layers[1].size = drc_chan + 1;
+       layers[1].is_virt_csrow = false;
+       mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers, sizeof(*pvt));
        if (mci == NULL)
                return -ENOMEM;
 
index 5b739411d62f69b026bbd54f16006e8f0b957b42..117490d4f8359d0fbe9ab50729270e1e0424fe09 100644 (file)
@@ -447,8 +447,10 @@ static inline void pci_write_bits32(struct pci_dev *pdev, int offset,
 
 #endif                         /* CONFIG_PCI */
 
-extern struct mem_ctl_info *edac_mc_alloc(unsigned sz_pvt, unsigned nr_csrows,
-                                         unsigned nr_chans, int edac_index);
+struct mem_ctl_info *edac_mc_alloc(unsigned mc_num,
+                                  unsigned n_layers,
+                                  struct edac_mc_layer *layers,
+                                  unsigned sz_pvt);
 extern int edac_mc_add_mc(struct mem_ctl_info *mci);
 extern void edac_mc_free(struct mem_ctl_info *mci);
 extern struct mem_ctl_info *edac_mc_find(int idx);
@@ -456,35 +458,17 @@ extern struct mem_ctl_info *find_mci_by_dev(struct device *dev);
 extern struct mem_ctl_info *edac_mc_del_mc(struct device *dev);
 extern int edac_mc_find_csrow_by_page(struct mem_ctl_info *mci,
                                      unsigned long page);
-
-/*
- * The no info errors are used when error overflows are reported.
- * There are a limited number of error logging registers that can
- * be exausted.  When all registers are exhausted and an additional
- * error occurs then an error overflow register records that an
- * error occurred and the type of error, but doesn't have any
- * further information.  The ce/ue versions make for cleaner
- * reporting logic and function interface - reduces conditional
- * statement clutter and extra function arguments.
- */
-extern void edac_mc_handle_ce(struct mem_ctl_info *mci,
-                             unsigned long page_frame_number,
-                             unsigned long offset_in_page,
-                             unsigned long syndrome, int row, int channel,
-                             const char *msg);
-extern void edac_mc_handle_ce_no_info(struct mem_ctl_info *mci,
-                                     const char *msg);
-extern void edac_mc_handle_ue(struct mem_ctl_info *mci,
-                             unsigned long page_frame_number,
-                             unsigned long offset_in_page, int row,
-                             const char *msg);
-extern void edac_mc_handle_ue_no_info(struct mem_ctl_info *mci,
-                                     const char *msg);
-extern void edac_mc_handle_fbd_ue(struct mem_ctl_info *mci, unsigned int csrow,
-                                 unsigned int channel0, unsigned int channel1,
-                                 char *msg);
-extern void edac_mc_handle_fbd_ce(struct mem_ctl_info *mci, unsigned int csrow,
-                                 unsigned int channel, char *msg);
+void edac_mc_handle_error(const enum hw_event_mc_err_type type,
+                         struct mem_ctl_info *mci,
+                         const unsigned long page_frame_number,
+                         const unsigned long offset_in_page,
+                         const unsigned long syndrome,
+                         const int layer0,
+                         const int layer1,
+                         const int layer2,
+                         const char *msg,
+                         const char *other_detail,
+                         const void *mcelog);
 
 /*
  * edac_device APIs
@@ -496,6 +480,7 @@ extern void edac_device_handle_ue(struct edac_device_ctl_info *edac_dev,
 extern void edac_device_handle_ce(struct edac_device_ctl_info *edac_dev,
                                int inst_nr, int block_nr, const char *msg);
 extern int edac_device_alloc_index(void);
+extern const char *edac_layer_name[];
 
 /*
  * edac_pci APIs
index 45b8f4bdd773ca324c0e5f04f1c3c4a883bd03be..ee3f1f810c1e094c27dbd012d51c4cdfa9b4ee55 100644 (file)
@@ -79,7 +79,7 @@ struct edac_device_ctl_info *edac_device_alloc_ctl_info(
        unsigned total_size;
        unsigned count;
        unsigned instance, block, attr;
-       void *pvt;
+       void *pvt, *p;
        int err;
 
        debugf4("%s() instances=%d blocks=%d\n",
@@ -92,35 +92,30 @@ struct edac_device_ctl_info *edac_device_alloc_ctl_info(
         * to be at least as stringent as what the compiler would
         * provide if we could simply hardcode everything into a single struct.
         */
-       dev_ctl = (struct edac_device_ctl_info *)NULL;
+       p = NULL;
+       dev_ctl = edac_align_ptr(&p, sizeof(*dev_ctl), 1);
 
        /* Calc the 'end' offset past end of ONE ctl_info structure
         * which will become the start of the 'instance' array
         */
-       dev_inst = edac_align_ptr(&dev_ctl[1], sizeof(*dev_inst));
+       dev_inst = edac_align_ptr(&p, sizeof(*dev_inst), nr_instances);
 
        /* Calc the 'end' offset past the instance array within the ctl_info
         * which will become the start of the block array
         */
-       dev_blk = edac_align_ptr(&dev_inst[nr_instances], sizeof(*dev_blk));
+       count = nr_instances * nr_blocks;
+       dev_blk = edac_align_ptr(&p, sizeof(*dev_blk), count);
 
        /* Calc the 'end' offset past the dev_blk array
         * which will become the start of the attrib array, if any.
         */
-       count = nr_instances * nr_blocks;
-       dev_attrib = edac_align_ptr(&dev_blk[count], sizeof(*dev_attrib));
-
-       /* Check for case of when an attribute array is specified */
-       if (nr_attrib > 0) {
-               /* calc how many nr_attrib we need */
+       /* calc how many nr_attrib we need */
+       if (nr_attrib > 0)
                count *= nr_attrib;
+       dev_attrib = edac_align_ptr(&p, sizeof(*dev_attrib), count);
 
-               /* Calc the 'end' offset past the attributes array */
-               pvt = edac_align_ptr(&dev_attrib[count], sz_private);
-       } else {
-               /* no attribute array specified */
-               pvt = edac_align_ptr(dev_attrib, sz_private);
-       }
+       /* Calc the 'end' offset past the attributes array */
+       pvt = edac_align_ptr(&p, sz_private, 1);
 
        /* 'pvt' now points to where the private data area is.
         * At this point 'pvt' (like dev_inst,dev_blk and dev_attrib)
index feef7733fae7702733feb642bfcf0ac7c431f484..10f375032e9686f7c719c857ceca5dd3f9ef81ed 100644 (file)
@@ -43,9 +43,26 @@ static void edac_mc_dump_channel(struct rank_info *chan)
 {
        debugf4("\tchannel = %p\n", chan);
        debugf4("\tchannel->chan_idx = %d\n", chan->chan_idx);
-       debugf4("\tchannel->ce_count = %d\n", chan->ce_count);
-       debugf4("\tchannel->label = '%s'\n", chan->label);
        debugf4("\tchannel->csrow = %p\n\n", chan->csrow);
+       debugf4("\tchannel->dimm = %p\n", chan->dimm);
+}
+
+static void edac_mc_dump_dimm(struct dimm_info *dimm)
+{
+       int i;
+
+       debugf4("\tdimm = %p\n", dimm);
+       debugf4("\tdimm->label = '%s'\n", dimm->label);
+       debugf4("\tdimm->nr_pages = 0x%x\n", dimm->nr_pages);
+       debugf4("\tdimm location ");
+       for (i = 0; i < dimm->mci->n_layers; i++) {
+               printk(KERN_CONT "%d", dimm->location[i]);
+               if (i < dimm->mci->n_layers - 1)
+                       printk(KERN_CONT ".");
+       }
+       printk(KERN_CONT "\n");
+       debugf4("\tdimm->grain = %d\n", dimm->grain);
+       debugf4("\tdimm->nr_pages = 0x%x\n", dimm->nr_pages);
 }
 
 static void edac_mc_dump_csrow(struct csrow_info *csrow)
@@ -55,7 +72,6 @@ static void edac_mc_dump_csrow(struct csrow_info *csrow)
        debugf4("\tcsrow->first_page = 0x%lx\n", csrow->first_page);
        debugf4("\tcsrow->last_page = 0x%lx\n", csrow->last_page);
        debugf4("\tcsrow->page_mask = 0x%lx\n", csrow->page_mask);
-       debugf4("\tcsrow->nr_pages = 0x%x\n", csrow->nr_pages);
        debugf4("\tcsrow->nr_channels = %d\n", csrow->nr_channels);
        debugf4("\tcsrow->channels = %p\n", csrow->channels);
        debugf4("\tcsrow->mci = %p\n\n", csrow->mci);
@@ -70,6 +86,8 @@ static void edac_mc_dump_mci(struct mem_ctl_info *mci)
        debugf4("\tmci->edac_check = %p\n", mci->edac_check);
        debugf3("\tmci->nr_csrows = %d, csrows = %p\n",
                mci->nr_csrows, mci->csrows);
+       debugf3("\tmci->nr_dimms = %d, dimms = %p\n",
+               mci->tot_dimms, mci->dimms);
        debugf3("\tdev = %p\n", mci->dev);
        debugf3("\tmod_name:ctl_name = %s:%s\n", mci->mod_name, mci->ctl_name);
        debugf3("\tpvt_info = %p\n\n", mci->pvt_info);
@@ -101,18 +119,37 @@ const char *edac_mem_types[] = {
 };
 EXPORT_SYMBOL_GPL(edac_mem_types);
 
-/* 'ptr' points to a possibly unaligned item X such that sizeof(X) is 'size'.
- * Adjust 'ptr' so that its alignment is at least as stringent as what the
- * compiler would provide for X and return the aligned result.
+/**
+ * edac_align_ptr - Prepares the pointer offsets for a single-shot allocation
+ * @p:         pointer to a pointer with the memory offset to be used. At
+ *             return, this will be incremented to point to the next offset
+ * @size:      Size of the data structure to be reserved
+ * @n_elems:   Number of elements that should be reserved
  *
  * If 'size' is a constant, the compiler will optimize this whole function
- * down to either a no-op or the addition of a constant to the value of 'ptr'.
+ * down to either a no-op or the addition of a constant to the value of '*p'.
+ *
+ * The 'p' pointer is absolutely needed to keep the proper advancing
+ * further in memory to the proper offsets when allocating the struct along
+ * with its embedded structs, as edac_device_alloc_ctl_info() does it
+ * above, for example.
+ *
+ * At return, the pointer 'p' will be incremented to be used on a next call
+ * to this function.
  */
-void *edac_align_ptr(void *ptr, unsigned size)
+void *edac_align_ptr(void **p, unsigned size, int n_elems)
 {
        unsigned align, r;
+       void *ptr = *p;
+
+       *p += size * n_elems;
 
-       /* Here we assume that the alignment of a "long long" is the most
+       /*
+        * 'p' can possibly be an unaligned item X such that sizeof(X) is
+        * 'size'.  Adjust 'p' so that its alignment is at least as
+        * stringent as what the compiler would provide for X and return
+        * the aligned result.
+        * Here we assume that the alignment of a "long long" is the most
         * stringent alignment that the compiler will ever provide by default.
         * As far as I know, this is a reasonable assumption.
         */
@@ -132,14 +169,18 @@ void *edac_align_ptr(void *ptr, unsigned size)
        if (r == 0)
                return (char *)ptr;
 
+       *p += align - r;
+
        return (void *)(((unsigned long)ptr) + align - r);
 }
 
 /**
- * edac_mc_alloc: Allocate a struct mem_ctl_info structure
- * @size_pvt:  size of private storage needed
- * @nr_csrows: Number of CWROWS needed for this MC
- * @nr_chans:  Number of channels for the MC
+ * edac_mc_alloc: Allocate and partially fill a struct mem_ctl_info structure
+ * @mc_num:            Memory controller number
+ * @n_layers:          Number of MC hierarchy layers
+ * layers:             Describes each layer as seen by the Memory Controller
+ * @size_pvt:          size of private storage needed
+ *
  *
  * Everything is kmalloc'ed as one big chunk - more efficient.
  * Only can be used if all structures have the same lifetime - otherwise
@@ -147,32 +188,77 @@ void *edac_align_ptr(void *ptr, unsigned size)
  *
  * Use edac_mc_free() to free mc structures allocated by this function.
  *
+ * NOTE: drivers handle multi-rank memories in different ways: in some
+ * drivers, one multi-rank memory stick is mapped as one entry, while, in
+ * others, a single multi-rank memory stick would be mapped into several
+ * entries. Currently, this function will allocate multiple struct dimm_info
+ * on such scenarios, as grouping the multiple ranks require drivers change.
+ *
  * Returns:
- *     NULL allocation failed
- *     struct mem_ctl_info pointer
+ *     On failure: NULL
+ *     On success: struct mem_ctl_info pointer
  */
-struct mem_ctl_info *edac_mc_alloc(unsigned sz_pvt, unsigned nr_csrows,
-                               unsigned nr_chans, int edac_index)
+struct mem_ctl_info *edac_mc_alloc(unsigned mc_num,
+                                  unsigned n_layers,
+                                  struct edac_mc_layer *layers,
+                                  unsigned sz_pvt)
 {
        struct mem_ctl_info *mci;
-       struct csrow_info *csi, *csrow;
+       struct edac_mc_layer *layer;
+       struct csrow_info *csi, *csr;
        struct rank_info *chi, *chp, *chan;
-       void *pvt;
-       unsigned size;
-       int row, chn;
-       int err;
+       struct dimm_info *dimm;
+       u32 *ce_per_layer[EDAC_MAX_LAYERS], *ue_per_layer[EDAC_MAX_LAYERS];
+       unsigned pos[EDAC_MAX_LAYERS];
+       unsigned size, tot_dimms = 1, count = 1;
+       unsigned tot_csrows = 1, tot_channels = 1, tot_errcount = 0;
+       void *pvt, *p, *ptr = NULL;
+       int i, j, err, row, chn, n, len;
+       bool per_rank = false;
+
+       BUG_ON(n_layers > EDAC_MAX_LAYERS || n_layers == 0);
+       /*
+        * Calculate the total amount of dimms and csrows/cschannels while
+        * in the old API emulation mode
+        */
+       for (i = 0; i < n_layers; i++) {
+               tot_dimms *= layers[i].size;
+               if (layers[i].is_virt_csrow)
+                       tot_csrows *= layers[i].size;
+               else
+                       tot_channels *= layers[i].size;
+
+               if (layers[i].type == EDAC_MC_LAYER_CHIP_SELECT)
+                       per_rank = true;
+       }
 
        /* Figure out the offsets of the various items from the start of an mc
         * structure.  We want the alignment of each item to be at least as
         * stringent as what the compiler would provide if we could simply
         * hardcode everything into a single struct.
         */
-       mci = (struct mem_ctl_info *)0;
-       csi = edac_align_ptr(&mci[1], sizeof(*csi));
-       chi = edac_align_ptr(&csi[nr_csrows], sizeof(*chi));
-       pvt = edac_align_ptr(&chi[nr_chans * nr_csrows], sz_pvt);
+       mci = edac_align_ptr(&ptr, sizeof(*mci), 1);
+       layer = edac_align_ptr(&ptr, sizeof(*layer), n_layers);
+       csi = edac_align_ptr(&ptr, sizeof(*csi), tot_csrows);
+       chi = edac_align_ptr(&ptr, sizeof(*chi), tot_csrows * tot_channels);
+       dimm = edac_align_ptr(&ptr, sizeof(*dimm), tot_dimms);
+       for (i = 0; i < n_layers; i++) {
+               count *= layers[i].size;
+               debugf4("%s: errcount layer %d size %d\n", __func__, i, count);
+               ce_per_layer[i] = edac_align_ptr(&ptr, sizeof(u32), count);
+               ue_per_layer[i] = edac_align_ptr(&ptr, sizeof(u32), count);
+               tot_errcount += 2 * count;
+       }
+
+       debugf4("%s: allocating %d error counters\n", __func__, tot_errcount);
+       pvt = edac_align_ptr(&ptr, sz_pvt, 1);
        size = ((unsigned long)pvt) + sz_pvt;
 
+       debugf1("%s(): allocating %u bytes for mci data (%d %s, %d csrows/channels)\n",
+               __func__, size,
+               tot_dimms,
+               per_rank ? "ranks" : "dimms",
+               tot_csrows * tot_channels);
        mci = kzalloc(size, GFP_KERNEL);
        if (mci == NULL)
                return NULL;
@@ -180,28 +266,103 @@ struct mem_ctl_info *edac_mc_alloc(unsigned sz_pvt, unsigned nr_csrows,
        /* Adjust pointers so they point within the memory we just allocated
         * rather than an imaginary chunk of memory located at address 0.
         */
+       layer = (struct edac_mc_layer *)(((char *)mci) + ((unsigned long)layer));
        csi = (struct csrow_info *)(((char *)mci) + ((unsigned long)csi));
        chi = (struct rank_info *)(((char *)mci) + ((unsigned long)chi));
+       dimm = (struct dimm_info *)(((char *)mci) + ((unsigned long)dimm));
+       for (i = 0; i < n_layers; i++) {
+               mci->ce_per_layer[i] = (u32 *)((char *)mci + ((unsigned long)ce_per_layer[i]));
+               mci->ue_per_layer[i] = (u32 *)((char *)mci + ((unsigned long)ue_per_layer[i]));
+       }
        pvt = sz_pvt ? (((char *)mci) + ((unsigned long)pvt)) : NULL;
 
        /* setup index and various internal pointers */
-       mci->mc_idx = edac_index;
+       mci->mc_idx = mc_num;
        mci->csrows = csi;
+       mci->dimms  = dimm;
+       mci->tot_dimms = tot_dimms;
        mci->pvt_info = pvt;
-       mci->nr_csrows = nr_csrows;
-
-       for (row = 0; row < nr_csrows; row++) {
-               csrow = &csi[row];
-               csrow->csrow_idx = row;
-               csrow->mci = mci;
-               csrow->nr_channels = nr_chans;
-               chp = &chi[row * nr_chans];
-               csrow->channels = chp;
+       mci->n_layers = n_layers;
+       mci->layers = layer;
+       memcpy(mci->layers, layers, sizeof(*layer) * n_layers);
+       mci->nr_csrows = tot_csrows;
+       mci->num_cschannel = tot_channels;
+       mci->mem_is_per_rank = per_rank;
 
-               for (chn = 0; chn < nr_chans; chn++) {
+       /*
+        * Fill the csrow struct
+        */
+       for (row = 0; row < tot_csrows; row++) {
+               csr = &csi[row];
+               csr->csrow_idx = row;
+               csr->mci = mci;
+               csr->nr_channels = tot_channels;
+               chp = &chi[row * tot_channels];
+               csr->channels = chp;
+
+               for (chn = 0; chn < tot_channels; chn++) {
                        chan = &chp[chn];
                        chan->chan_idx = chn;
-                       chan->csrow = csrow;
+                       chan->csrow = csr;
+               }
+       }
+
+       /*
+        * Fill the dimm struct
+        */
+       memset(&pos, 0, sizeof(pos));
+       row = 0;
+       chn = 0;
+       debugf4("%s: initializing %d %s\n", __func__, tot_dimms,
+               per_rank ? "ranks" : "dimms");
+       for (i = 0; i < tot_dimms; i++) {
+               chan = &csi[row].channels[chn];
+               dimm = EDAC_DIMM_PTR(layer, mci->dimms, n_layers,
+                              pos[0], pos[1], pos[2]);
+               dimm->mci = mci;
+
+               debugf2("%s: %d: %s%zd (%d:%d:%d): row %d, chan %d\n", __func__,
+                       i, per_rank ? "rank" : "dimm", (dimm - mci->dimms),
+                       pos[0], pos[1], pos[2], row, chn);
+
+               /*
+                * Copy DIMM location and initialize it.
+                */
+               len = sizeof(dimm->label);
+               p = dimm->label;
+               n = snprintf(p, len, "mc#%u", mc_num);
+               p += n;
+               len -= n;
+               for (j = 0; j < n_layers; j++) {
+                       n = snprintf(p, len, "%s#%u",
+                                    edac_layer_name[layers[j].type],
+                                    pos[j]);
+                       p += n;
+                       len -= n;
+                       dimm->location[j] = pos[j];
+
+                       if (len <= 0)
+                               break;
+               }
+
+               /* Link it to the csrows old API data */
+               chan->dimm = dimm;
+               dimm->csrow = row;
+               dimm->cschannel = chn;
+
+               /* Increment csrow location */
+               row++;
+               if (row == tot_csrows) {
+                       row = 0;
+                       chn++;
+               }
+
+               /* Increment dimm location */
+               for (j = n_layers - 1; j >= 0; j--) {
+                       pos[j]++;
+                       if (pos[j] < layers[j].size)
+                               break;
+                       pos[j] = 0;
                }
        }
 
@@ -490,7 +651,6 @@ EXPORT_SYMBOL(edac_mc_find);
  * edac_mc_add_mc: Insert the 'mci' structure into the mci global list and
  *                 create sysfs entries associated with mci structure
  * @mci: pointer to the mci structure to be added to the list
- * @mc_idx: A unique numeric identifier to be assigned to the 'mci' structure.
  *
  * Return:
  *     0       Success
@@ -517,6 +677,8 @@ int edac_mc_add_mc(struct mem_ctl_info *mci)
                                edac_mc_dump_channel(&mci->csrows[i].
                                                channels[j]);
                }
+               for (i = 0; i < mci->tot_dimms; i++)
+                       edac_mc_dump_dimm(&mci->dimms[i]);
        }
 #endif
        mutex_lock(&mem_ctls_mutex);
@@ -636,15 +798,19 @@ static void edac_mc_scrub_block(unsigned long page, unsigned long offset,
 int edac_mc_find_csrow_by_page(struct mem_ctl_info *mci, unsigned long page)
 {
        struct csrow_info *csrows = mci->csrows;
-       int row, i;
+       int row, i, j, n;
 
        debugf1("MC%d: %s(): 0x%lx\n", mci->mc_idx, __func__, page);
        row = -1;
 
        for (i = 0; i < mci->nr_csrows; i++) {
                struct csrow_info *csrow = &csrows[i];
-
-               if (csrow->nr_pages == 0)
+               n = 0;
+               for (j = 0; j < csrow->nr_channels; j++) {
+                       struct dimm_info *dimm = csrow->channels[j].dimm;
+                       n += dimm->nr_pages;
+               }
+               if (n == 0)
                        continue;
 
                debugf3("MC%d: %s(): first(0x%lx) page(0x%lx) last(0x%lx) "
@@ -670,249 +836,307 @@ int edac_mc_find_csrow_by_page(struct mem_ctl_info *mci, unsigned long page)
 }
 EXPORT_SYMBOL_GPL(edac_mc_find_csrow_by_page);
 
-/* FIXME - setable log (warning/emerg) levels */
-/* FIXME - integrate with evlog: http://evlog.sourceforge.net/ */
-void edac_mc_handle_ce(struct mem_ctl_info *mci,
-               unsigned long page_frame_number,
-               unsigned long offset_in_page, unsigned long syndrome,
-               int row, int channel, const char *msg)
-{
-       unsigned long remapped_page;
+const char *edac_layer_name[] = {
+       [EDAC_MC_LAYER_BRANCH] = "branch",
+       [EDAC_MC_LAYER_CHANNEL] = "channel",
+       [EDAC_MC_LAYER_SLOT] = "slot",
+       [EDAC_MC_LAYER_CHIP_SELECT] = "csrow",
+};
+EXPORT_SYMBOL_GPL(edac_layer_name);
 
-       debugf3("MC%d: %s()\n", mci->mc_idx, __func__);
+static void edac_inc_ce_error(struct mem_ctl_info *mci,
+                                   bool enable_per_layer_report,
+                                   const int pos[EDAC_MAX_LAYERS])
+{
+       int i, index = 0;
 
-       /* FIXME - maybe make panic on INTERNAL ERROR an option */
-       if (row >= mci->nr_csrows || row < 0) {
-               /* something is wrong */
-               edac_mc_printk(mci, KERN_ERR,
-                       "INTERNAL ERROR: row out of range "
-                       "(%d >= %d)\n", row, mci->nr_csrows);
-               edac_mc_handle_ce_no_info(mci, "INTERNAL ERROR");
-               return;
-       }
+       mci->ce_mc++;
 
-       if (channel >= mci->csrows[row].nr_channels || channel < 0) {
-               /* something is wrong */
-               edac_mc_printk(mci, KERN_ERR,
-                       "INTERNAL ERROR: channel out of range "
-                       "(%d >= %d)\n", channel,
-                       mci->csrows[row].nr_channels);
-               edac_mc_handle_ce_no_info(mci, "INTERNAL ERROR");
+       if (!enable_per_layer_report) {
+               mci->ce_noinfo_count++;
                return;
        }
 
-       if (edac_mc_get_log_ce())
-               /* FIXME - put in DIMM location */
-               edac_mc_printk(mci, KERN_WARNING,
-                       "CE page 0x%lx, offset 0x%lx, grain %d, syndrome "
-                       "0x%lx, row %d, channel %d, label \"%s\": %s\n",
-                       page_frame_number, offset_in_page,
-                       mci->csrows[row].grain, syndrome, row, channel,
-                       mci->csrows[row].channels[channel].label, msg);
-
-       mci->ce_count++;
-       mci->csrows[row].ce_count++;
-       mci->csrows[row].channels[channel].ce_count++;
-
-       if (mci->scrub_mode & SCRUB_SW_SRC) {
-               /*
-                * Some MC's can remap memory so that it is still available
-                * at a different address when PCI devices map into memory.
-                * MC's that can't do this lose the memory where PCI devices
-                * are mapped.  This mapping is MC dependent and so we call
-                * back into the MC driver for it to map the MC page to
-                * a physical (CPU) page which can then be mapped to a virtual
-                * page - which can then be scrubbed.
-                */
-               remapped_page = mci->ctl_page_to_phys ?
-                       mci->ctl_page_to_phys(mci, page_frame_number) :
-                       page_frame_number;
+       for (i = 0; i < mci->n_layers; i++) {
+               if (pos[i] < 0)
+                       break;
+               index += pos[i];
+               mci->ce_per_layer[i][index]++;
 
-               edac_mc_scrub_block(remapped_page, offset_in_page,
-                               mci->csrows[row].grain);
+               if (i < mci->n_layers - 1)
+                       index *= mci->layers[i + 1].size;
        }
 }
-EXPORT_SYMBOL_GPL(edac_mc_handle_ce);
 
-void edac_mc_handle_ce_no_info(struct mem_ctl_info *mci, const char *msg)
+static void edac_inc_ue_error(struct mem_ctl_info *mci,
+                                   bool enable_per_layer_report,
+                                   const int pos[EDAC_MAX_LAYERS])
 {
-       if (edac_mc_get_log_ce())
-               edac_mc_printk(mci, KERN_WARNING,
-                       "CE - no information available: %s\n", msg);
+       int i, index = 0;
 
-       mci->ce_noinfo_count++;
-       mci->ce_count++;
-}
-EXPORT_SYMBOL_GPL(edac_mc_handle_ce_no_info);
+       mci->ue_mc++;
 
-void edac_mc_handle_ue(struct mem_ctl_info *mci,
-               unsigned long page_frame_number,
-               unsigned long offset_in_page, int row, const char *msg)
-{
-       int len = EDAC_MC_LABEL_LEN * 4;
-       char labels[len + 1];
-       char *pos = labels;
-       int chan;
-       int chars;
-
-       debugf3("MC%d: %s()\n", mci->mc_idx, __func__);
-
-       /* FIXME - maybe make panic on INTERNAL ERROR an option */
-       if (row >= mci->nr_csrows || row < 0) {
-               /* something is wrong */
-               edac_mc_printk(mci, KERN_ERR,
-                       "INTERNAL ERROR: row out of range "
-                       "(%d >= %d)\n", row, mci->nr_csrows);
-               edac_mc_handle_ue_no_info(mci, "INTERNAL ERROR");
+       if (!enable_per_layer_report) {
+               mci->ce_noinfo_count++;
                return;
        }
 
-       chars = snprintf(pos, len + 1, "%s",
-                        mci->csrows[row].channels[0].label);
-       len -= chars;
-       pos += chars;
+       for (i = 0; i < mci->n_layers; i++) {
+               if (pos[i] < 0)
+                       break;
+               index += pos[i];
+               mci->ue_per_layer[i][index]++;
 
-       for (chan = 1; (chan < mci->csrows[row].nr_channels) && (len > 0);
-               chan++) {
-               chars = snprintf(pos, len + 1, ":%s",
-                                mci->csrows[row].channels[chan].label);
-               len -= chars;
-               pos += chars;
+               if (i < mci->n_layers - 1)
+                       index *= mci->layers[i + 1].size;
        }
+}
 
-       if (edac_mc_get_log_ue())
-               edac_mc_printk(mci, KERN_EMERG,
-                       "UE page 0x%lx, offset 0x%lx, grain %d, row %d, "
-                       "labels \"%s\": %s\n", page_frame_number,
-                       offset_in_page, mci->csrows[row].grain, row,
-                       labels, msg);
+static void edac_ce_error(struct mem_ctl_info *mci,
+                         const int pos[EDAC_MAX_LAYERS],
+                         const char *msg,
+                         const char *location,
+                         const char *label,
+                         const char *detail,
+                         const char *other_detail,
+                         const bool enable_per_layer_report,
+                         const unsigned long page_frame_number,
+                         const unsigned long offset_in_page,
+                         u32 grain)
+{
+       unsigned long remapped_page;
 
-       if (edac_mc_get_panic_on_ue())
-               panic("EDAC MC%d: UE page 0x%lx, offset 0x%lx, grain %d, "
-                       "row %d, labels \"%s\": %s\n", mci->mc_idx,
-                       page_frame_number, offset_in_page,
-                       mci->csrows[row].grain, row, labels, msg);
+       if (edac_mc_get_log_ce()) {
+               if (other_detail && *other_detail)
+                       edac_mc_printk(mci, KERN_WARNING,
+                                      "CE %s on %s (%s%s - %s)\n",
+                                      msg, label, location,
+                                      detail, other_detail);
+               else
+                       edac_mc_printk(mci, KERN_WARNING,
+                                      "CE %s on %s (%s%s)\n",
+                                      msg, label, location,
+                                      detail);
+       }
+       edac_inc_ce_error(mci, enable_per_layer_report, pos);
 
-       mci->ue_count++;
-       mci->csrows[row].ue_count++;
+       if (mci->scrub_mode & SCRUB_SW_SRC) {
+               /*
+                       * Some memory controllers (called MCs below) can remap
+                       * memory so that it is still available at a different
+                       * address when PCI devices map into memory.
+                       * MC's that can't do this, lose the memory where PCI
+                       * devices are mapped. This mapping is MC-dependent
+                       * and so we call back into the MC driver for it to
+                       * map the MC page to a physical (CPU) page which can
+                       * then be mapped to a virtual page - which can then
+                       * be scrubbed.
+                       */
+               remapped_page = mci->ctl_page_to_phys ?
+                       mci->ctl_page_to_phys(mci, page_frame_number) :
+                       page_frame_number;
+
+               edac_mc_scrub_block(remapped_page,
+                                       offset_in_page, grain);
+       }
 }
-EXPORT_SYMBOL_GPL(edac_mc_handle_ue);
 
-void edac_mc_handle_ue_no_info(struct mem_ctl_info *mci, const char *msg)
+static void edac_ue_error(struct mem_ctl_info *mci,
+                         const int pos[EDAC_MAX_LAYERS],
+                         const char *msg,
+                         const char *location,
+                         const char *label,
+                         const char *detail,
+                         const char *other_detail,
+                         const bool enable_per_layer_report)
 {
-       if (edac_mc_get_panic_on_ue())
-               panic("EDAC MC%d: Uncorrected Error", mci->mc_idx);
+       if (edac_mc_get_log_ue()) {
+               if (other_detail && *other_detail)
+                       edac_mc_printk(mci, KERN_WARNING,
+                                      "UE %s on %s (%s%s - %s)\n",
+                                      msg, label, location, detail,
+                                      other_detail);
+               else
+                       edac_mc_printk(mci, KERN_WARNING,
+                                      "UE %s on %s (%s%s)\n",
+                                      msg, label, location, detail);
+       }
 
-       if (edac_mc_get_log_ue())
-               edac_mc_printk(mci, KERN_WARNING,
-                       "UE - no information available: %s\n", msg);
-       mci->ue_noinfo_count++;
-       mci->ue_count++;
+       if (edac_mc_get_panic_on_ue()) {
+               if (other_detail && *other_detail)
+                       panic("UE %s on %s (%s%s - %s)\n",
+                             msg, label, location, detail, other_detail);
+               else
+                       panic("UE %s on %s (%s%s)\n",
+                             msg, label, location, detail);
+       }
+
+       edac_inc_ue_error(mci, enable_per_layer_report, pos);
 }
-EXPORT_SYMBOL_GPL(edac_mc_handle_ue_no_info);
 
-/*************************************************************
- * On Fully Buffered DIMM modules, this help function is
- * called to process UE events
- */
-void edac_mc_handle_fbd_ue(struct mem_ctl_info *mci,
-                       unsigned int csrow,
-                       unsigned int channela,
-                       unsigned int channelb, char *msg)
+#define OTHER_LABEL " or "
+void edac_mc_handle_error(const enum hw_event_mc_err_type type,
+                         struct mem_ctl_info *mci,
+                         const unsigned long page_frame_number,
+                         const unsigned long offset_in_page,
+                         const unsigned long syndrome,
+                         const int layer0,
+                         const int layer1,
+                         const int layer2,
+                         const char *msg,
+                         const char *other_detail,
+                         const void *mcelog)
 {
-       int len = EDAC_MC_LABEL_LEN * 4;
-       char labels[len + 1];
-       char *pos = labels;
-       int chars;
+       /* FIXME: too much for stack: move it to some pre-alocated area */
+       char detail[80], location[80];
+       char label[(EDAC_MC_LABEL_LEN + 1 + sizeof(OTHER_LABEL)) * mci->tot_dimms];
+       char *p;
+       int row = -1, chan = -1;
+       int pos[EDAC_MAX_LAYERS] = { layer0, layer1, layer2 };
+       int i;
+       u32 grain;
+       bool enable_per_layer_report = false;
 
-       if (csrow >= mci->nr_csrows) {
-               /* something is wrong */
-               edac_mc_printk(mci, KERN_ERR,
-                       "INTERNAL ERROR: row out of range (%d >= %d)\n",
-                       csrow, mci->nr_csrows);
-               edac_mc_handle_ue_no_info(mci, "INTERNAL ERROR");
-               return;
-       }
+       debugf3("MC%d: %s()\n", mci->mc_idx, __func__);
 
-       if (channela >= mci->csrows[csrow].nr_channels) {
-               /* something is wrong */
-               edac_mc_printk(mci, KERN_ERR,
-                       "INTERNAL ERROR: channel-a out of range "
-                       "(%d >= %d)\n",
-                       channela, mci->csrows[csrow].nr_channels);
-               edac_mc_handle_ue_no_info(mci, "INTERNAL ERROR");
-               return;
+       /*
+        * Check if the event report is consistent and if the memory
+        * location is known. If it is known, enable_per_layer_report will be
+        * true, the DIMM(s) label info will be filled and the per-layer
+        * error counters will be incremented.
+        */
+       for (i = 0; i < mci->n_layers; i++) {
+               if (pos[i] >= (int)mci->layers[i].size) {
+                       if (type == HW_EVENT_ERR_CORRECTED)
+                               p = "CE";
+                       else
+                               p = "UE";
+
+                       edac_mc_printk(mci, KERN_ERR,
+                                      "INTERNAL ERROR: %s value is out of range (%d >= %d)\n",
+                                      edac_layer_name[mci->layers[i].type],
+                                      pos[i], mci->layers[i].size);
+                       /*
+                        * Instead of just returning it, let's use what's
+                        * known about the error. The increment routines and
+                        * the DIMM filter logic will do the right thing by
+                        * pointing the likely damaged DIMMs.
+                        */
+                       pos[i] = -1;
+               }
+               if (pos[i] >= 0)
+                       enable_per_layer_report = true;
        }
 
-       if (channelb >= mci->csrows[csrow].nr_channels) {
-               /* something is wrong */
-               edac_mc_printk(mci, KERN_ERR,
-                       "INTERNAL ERROR: channel-b out of range "
-                       "(%d >= %d)\n",
-                       channelb, mci->csrows[csrow].nr_channels);
-               edac_mc_handle_ue_no_info(mci, "INTERNAL ERROR");
-               return;
-       }
+       /*
+        * Get the dimm label/grain that applies to the match criteria.
+        * As the error algorithm may not be able to point to just one memory
+        * stick, the logic here will get all possible labels that could
+        * pottentially be affected by the error.
+        * On FB-DIMM memory controllers, for uncorrected errors, it is common
+        * to have only the MC channel and the MC dimm (also called "branch")
+        * but the channel is not known, as the memory is arranged in pairs,
+        * where each memory belongs to a separate channel within the same
+        * branch.
+        */
+       grain = 0;
+       p = label;
+       *p = '\0';
+       for (i = 0; i < mci->tot_dimms; i++) {
+               struct dimm_info *dimm = &mci->dimms[i];
 
-       mci->ue_count++;
-       mci->csrows[csrow].ue_count++;
+               if (layer0 >= 0 && layer0 != dimm->location[0])
+                       continue;
+               if (layer1 >= 0 && layer1 != dimm->location[1])
+                       continue;
+               if (layer2 >= 0 && layer2 != dimm->location[2])
+                       continue;
 
-       /* Generate the DIMM labels from the specified channels */
-       chars = snprintf(pos, len + 1, "%s",
-                        mci->csrows[csrow].channels[channela].label);
-       len -= chars;
-       pos += chars;
-       chars = snprintf(pos, len + 1, "-%s",
-                        mci->csrows[csrow].channels[channelb].label);
+               /* get the max grain, over the error match range */
+               if (dimm->grain > grain)
+                       grain = dimm->grain;
 
-       if (edac_mc_get_log_ue())
-               edac_mc_printk(mci, KERN_EMERG,
-                       "UE row %d, channel-a= %d channel-b= %d "
-                       "labels \"%s\": %s\n", csrow, channela, channelb,
-                       labels, msg);
+               /*
+                * If the error is memory-controller wide, there's no need to
+                * seek for the affected DIMMs because the whole
+                * channel/memory controller/...  may be affected.
+                * Also, don't show errors for empty DIMM slots.
+                */
+               if (enable_per_layer_report && dimm->nr_pages) {
+                       if (p != label) {
+                               strcpy(p, OTHER_LABEL);
+                               p += strlen(OTHER_LABEL);
+                       }
+                       strcpy(p, dimm->label);
+                       p += strlen(p);
+                       *p = '\0';
+
+                       /*
+                        * get csrow/channel of the DIMM, in order to allow
+                        * incrementing the compat API counters
+                        */
+                       debugf4("%s: %s csrows map: (%d,%d)\n",
+                               __func__,
+                               mci->mem_is_per_rank ? "rank" : "dimm",
+                               dimm->csrow, dimm->cschannel);
+
+                       if (row == -1)
+                               row = dimm->csrow;
+                       else if (row >= 0 && row != dimm->csrow)
+                               row = -2;
+
+                       if (chan == -1)
+                               chan = dimm->cschannel;
+                       else if (chan >= 0 && chan != dimm->cschannel)
+                               chan = -2;
+               }
+       }
 
-       if (edac_mc_get_panic_on_ue())
-               panic("UE row %d, channel-a= %d channel-b= %d "
-                       "labels \"%s\": %s\n", csrow, channela,
-                       channelb, labels, msg);
-}
-EXPORT_SYMBOL(edac_mc_handle_fbd_ue);
+       if (!enable_per_layer_report) {
+               strcpy(label, "any memory");
+       } else {
+               debugf4("%s: csrow/channel to increment: (%d,%d)\n",
+                       __func__, row, chan);
+               if (p == label)
+                       strcpy(label, "unknown memory");
+               if (type == HW_EVENT_ERR_CORRECTED) {
+                       if (row >= 0) {
+                               mci->csrows[row].ce_count++;
+                               if (chan >= 0)
+                                       mci->csrows[row].channels[chan].ce_count++;
+                       }
+               } else
+                       if (row >= 0)
+                               mci->csrows[row].ue_count++;
+       }
 
-/*************************************************************
- * On Fully Buffered DIMM modules, this help function is
- * called to process CE events
- */
-void edac_mc_handle_fbd_ce(struct mem_ctl_info *mci,
-                       unsigned int csrow, unsigned int channel, char *msg)
-{
+       /* Fill the RAM location data */
+       p = location;
+       for (i = 0; i < mci->n_layers; i++) {
+               if (pos[i] < 0)
+                       continue;
 
-       /* Ensure boundary values */
-       if (csrow >= mci->nr_csrows) {
-               /* something is wrong */
-               edac_mc_printk(mci, KERN_ERR,
-                       "INTERNAL ERROR: row out of range (%d >= %d)\n",
-                       csrow, mci->nr_csrows);
-               edac_mc_handle_ce_no_info(mci, "INTERNAL ERROR");
-               return;
-       }
-       if (channel >= mci->csrows[csrow].nr_channels) {
-               /* something is wrong */
-               edac_mc_printk(mci, KERN_ERR,
-                       "INTERNAL ERROR: channel out of range (%d >= %d)\n",
-                       channel, mci->csrows[csrow].nr_channels);
-               edac_mc_handle_ce_no_info(mci, "INTERNAL ERROR");
-               return;
+               p += sprintf(p, "%s:%d ",
+                            edac_layer_name[mci->layers[i].type],
+                            pos[i]);
        }
 
-       if (edac_mc_get_log_ce())
-               /* FIXME - put in DIMM location */
-               edac_mc_printk(mci, KERN_WARNING,
-                       "CE row %d, channel %d, label \"%s\": %s\n",
-                       csrow, channel,
-                       mci->csrows[csrow].channels[channel].label, msg);
+       /* Memory type dependent details about the error */
+       if (type == HW_EVENT_ERR_CORRECTED) {
+               snprintf(detail, sizeof(detail),
+                       "page:0x%lx offset:0x%lx grain:%d syndrome:0x%lx",
+                       page_frame_number, offset_in_page,
+                       grain, syndrome);
+               edac_ce_error(mci, pos, msg, location, label, detail,
+                             other_detail, enable_per_layer_report,
+                             page_frame_number, offset_in_page, grain);
+       } else {
+               snprintf(detail, sizeof(detail),
+                       "page:0x%lx offset:0x%lx grain:%d",
+                       page_frame_number, offset_in_page, grain);
 
-       mci->ce_count++;
-       mci->csrows[csrow].ce_count++;
-       mci->csrows[csrow].channels[channel].ce_count++;
+               edac_ue_error(mci, pos, msg, location, label, detail,
+                             other_detail, enable_per_layer_report);
+       }
 }
-EXPORT_SYMBOL(edac_mc_handle_fbd_ce);
+EXPORT_SYMBOL_GPL(edac_mc_handle_error);
index e9a28f576d144dee247d70cb43e4d06952acfa46..f6a29b0eedc8535bb33769ca81cf8f88abe602e8 100644 (file)
@@ -144,25 +144,31 @@ static ssize_t csrow_ce_count_show(struct csrow_info *csrow, char *data,
 static ssize_t csrow_size_show(struct csrow_info *csrow, char *data,
                                int private)
 {
-       return sprintf(data, "%u\n", PAGES_TO_MiB(csrow->nr_pages));
+       int i;
+       u32 nr_pages = 0;
+
+       for (i = 0; i < csrow->nr_channels; i++)
+               nr_pages += csrow->channels[i].dimm->nr_pages;
+
+       return sprintf(data, "%u\n", PAGES_TO_MiB(nr_pages));
 }
 
 static ssize_t csrow_mem_type_show(struct csrow_info *csrow, char *data,
                                int private)
 {
-       return sprintf(data, "%s\n", mem_types[csrow->mtype]);
+       return sprintf(data, "%s\n", mem_types[csrow->channels[0].dimm->mtype]);
 }
 
 static ssize_t csrow_dev_type_show(struct csrow_info *csrow, char *data,
                                int private)
 {
-       return sprintf(data, "%s\n", dev_types[csrow->dtype]);
+       return sprintf(data, "%s\n", dev_types[csrow->channels[0].dimm->dtype]);
 }
 
 static ssize_t csrow_edac_mode_show(struct csrow_info *csrow, char *data,
                                int private)
 {
-       return sprintf(data, "%s\n", edac_caps[csrow->edac_mode]);
+       return sprintf(data, "%s\n", edac_caps[csrow->channels[0].dimm->edac_mode]);
 }
 
 /* show/store functions for DIMM Label attributes */
@@ -170,11 +176,11 @@ static ssize_t channel_dimm_label_show(struct csrow_info *csrow,
                                char *data, int channel)
 {
        /* if field has not been initialized, there is nothing to send */
-       if (!csrow->channels[channel].label[0])
+       if (!csrow->channels[channel].dimm->label[0])
                return 0;
 
        return snprintf(data, EDAC_MC_LABEL_LEN, "%s\n",
-                       csrow->channels[channel].label);
+                       csrow->channels[channel].dimm->label);
 }
 
 static ssize_t channel_dimm_label_store(struct csrow_info *csrow,
@@ -184,8 +190,8 @@ static ssize_t channel_dimm_label_store(struct csrow_info *csrow,
        ssize_t max_size = 0;
 
        max_size = min((ssize_t) count, (ssize_t) EDAC_MC_LABEL_LEN - 1);
-       strncpy(csrow->channels[channel].label, data, max_size);
-       csrow->channels[channel].label[max_size] = '\0';
+       strncpy(csrow->channels[channel].dimm->label, data, max_size);
+       csrow->channels[channel].dimm->label[max_size] = '\0';
 
        return max_size;
 }
@@ -419,8 +425,8 @@ static ssize_t mci_reset_counters_store(struct mem_ctl_info *mci,
 
        mci->ue_noinfo_count = 0;
        mci->ce_noinfo_count = 0;
-       mci->ue_count = 0;
-       mci->ce_count = 0;
+       mci->ue_mc = 0;
+       mci->ce_mc = 0;
 
        for (row = 0; row < mci->nr_csrows; row++) {
                struct csrow_info *ri = &mci->csrows[row];
@@ -489,12 +495,12 @@ static ssize_t mci_sdram_scrub_rate_show(struct mem_ctl_info *mci, char *data)
 /* default attribute files for the MCI object */
 static ssize_t mci_ue_count_show(struct mem_ctl_info *mci, char *data)
 {
-       return sprintf(data, "%d\n", mci->ue_count);
+       return sprintf(data, "%d\n", mci->ue_mc);
 }
 
 static ssize_t mci_ce_count_show(struct mem_ctl_info *mci, char *data)
 {
-       return sprintf(data, "%d\n", mci->ce_count);
+       return sprintf(data, "%d\n", mci->ce_mc);
 }
 
 static ssize_t mci_ce_noinfo_show(struct mem_ctl_info *mci, char *data)
@@ -519,16 +525,16 @@ static ssize_t mci_ctl_name_show(struct mem_ctl_info *mci, char *data)
 
 static ssize_t mci_size_mb_show(struct mem_ctl_info *mci, char *data)
 {
-       int total_pages, csrow_idx;
+       int total_pages = 0, csrow_idx, j;
 
-       for (total_pages = csrow_idx = 0; csrow_idx < mci->nr_csrows;
-               csrow_idx++) {
+       for (csrow_idx = 0; csrow_idx < mci->nr_csrows; csrow_idx++) {
                struct csrow_info *csrow = &mci->csrows[csrow_idx];
 
-               if (!csrow->nr_pages)
-                       continue;
+               for (j = 0; j < csrow->nr_channels; j++) {
+                       struct dimm_info *dimm = csrow->channels[j].dimm;
 
-               total_pages += csrow->nr_pages;
+                       total_pages += dimm->nr_pages;
+               }
        }
 
        return sprintf(data, "%u\n", PAGES_TO_MiB(total_pages));
@@ -900,7 +906,7 @@ static void edac_remove_mci_instance_attributes(struct mem_ctl_info *mci,
  */
 int edac_create_sysfs_mci_device(struct mem_ctl_info *mci)
 {
-       int i;
+       int i, j;
        int err;
        struct csrow_info *csrow;
        struct kobject *kobj_mci = &mci->edac_mci_kobj;
@@ -934,10 +940,13 @@ int edac_create_sysfs_mci_device(struct mem_ctl_info *mci)
        /* Make directories for each CSROW object under the mc<id> kobject
         */
        for (i = 0; i < mci->nr_csrows; i++) {
+               int nr_pages = 0;
+
                csrow = &mci->csrows[i];
+               for (j = 0; j < csrow->nr_channels; j++)
+                       nr_pages += csrow->channels[j].dimm->nr_pages;
 
-               /* Only expose populated CSROWs */
-               if (csrow->nr_pages > 0) {
+               if (nr_pages > 0) {
                        err = edac_create_csrow_object(mci, csrow, i);
                        if (err) {
                                debugf1("%s() failure: create csrow %d obj\n",
@@ -949,12 +958,15 @@ int edac_create_sysfs_mci_device(struct mem_ctl_info *mci)
 
        return 0;
 
-       /* CSROW error: backout what has already been registered,  */
 fail1:
        for (i--; i >= 0; i--) {
-               if (csrow->nr_pages > 0) {
+               int nr_pages = 0;
+
+               csrow = &mci->csrows[i];
+               for (j = 0; j < csrow->nr_channels; j++)
+                       nr_pages += csrow->channels[j].dimm->nr_pages;
+               if (nr_pages > 0)
                        kobject_put(&mci->csrows[i].kobj);
-               }
        }
 
        /* remove the mci instance's attributes, if any */
@@ -973,14 +985,20 @@ fail0:
  */
 void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci)
 {
-       int i;
+       struct csrow_info *csrow;
+       int i, j;
 
        debugf0("%s()\n", __func__);
 
        /* remove all csrow kobjects */
        debugf4("%s()  unregister this mci kobj\n", __func__);
        for (i = 0; i < mci->nr_csrows; i++) {
-               if (mci->csrows[i].nr_pages > 0) {
+               int nr_pages = 0;
+
+               csrow = &mci->csrows[i];
+               for (j = 0; j < csrow->nr_channels; j++)
+                       nr_pages += csrow->channels[j].dimm->nr_pages;
+               if (nr_pages > 0) {
                        debugf0("%s()  unreg csrow-%d\n", __func__, i);
                        kobject_put(&mci->csrows[i].kobj);
                }
index 00f81b47a51ffd886e58c4aeaa70d8c0b896246f..0ea7d14cb930748e75aadbb18e48e4616fdc315e 100644 (file)
@@ -50,7 +50,7 @@ extern void edac_device_reset_delay_period(struct edac_device_ctl_info
                                           *edac_dev, unsigned long value);
 extern void edac_mc_reset_delay_period(int value);
 
-extern void *edac_align_ptr(void *ptr, unsigned size);
+extern void *edac_align_ptr(void **p, unsigned size, int n_elems);
 
 /*
  * EDAC PCI functions
index 63af1c5673d1bbc790d5a98228dd050fc9de36b5..f1ac866498864dfbfc8e73ad091860d790142a68 100644 (file)
@@ -42,13 +42,13 @@ struct edac_pci_ctl_info *edac_pci_alloc_ctl_info(unsigned int sz_pvt,
                                                const char *edac_pci_name)
 {
        struct edac_pci_ctl_info *pci;
-       void *pvt;
+       void *p = NULL, *pvt;
        unsigned int size;
 
        debugf1("%s()\n", __func__);
 
-       pci = (struct edac_pci_ctl_info *)0;
-       pvt = edac_align_ptr(&pci[1], sz_pvt);
+       pci = edac_align_ptr(&p, sizeof(*pci), 1);
+       pvt = edac_align_ptr(&p, 1, sz_pvt);
        size = ((unsigned long)pvt) + sz_pvt;
 
        /* Alloc the needed control struct memory */
index 277689a688413147b5271211dc1182a9fe44dffa..8ad1744faacd9559f6f4ab66eb1bf1b960fd4ee0 100644 (file)
@@ -245,7 +245,9 @@ static int i3000_process_error_info(struct mem_ctl_info *mci,
                return 1;
 
        if ((info->errsts ^ info->errsts2) & I3000_ERRSTS_BITS) {
-               edac_mc_handle_ce_no_info(mci, "UE overwrote CE");
+               edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 0, 0, 0,
+                                    -1, -1, -1,
+                                    "UE overwrote CE", "", NULL);
                info->errsts = info->errsts2;
        }
 
@@ -256,10 +258,15 @@ static int i3000_process_error_info(struct mem_ctl_info *mci,
        row = edac_mc_find_csrow_by_page(mci, pfn);
 
        if (info->errsts & I3000_ERRSTS_UE)
-               edac_mc_handle_ue(mci, pfn, offset, row, "i3000 UE");
+               edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
+                                    pfn, offset, 0,
+                                    row, -1, -1,
+                                    "i3000 UE", "", NULL);
        else
-               edac_mc_handle_ce(mci, pfn, offset, info->derrsyn, row,
-                               multi_chan ? channel : 0, "i3000 CE");
+               edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
+                                    pfn, offset, info->derrsyn,
+                                    row, multi_chan ? channel : 0, -1,
+                                    "i3000 CE", "", NULL);
 
        return 1;
 }
@@ -304,9 +311,10 @@ static int i3000_is_interleaved(const unsigned char *c0dra,
 static int i3000_probe1(struct pci_dev *pdev, int dev_idx)
 {
        int rc;
-       int i;
+       int i, j;
        struct mem_ctl_info *mci = NULL;
-       unsigned long last_cumul_size;
+       struct edac_mc_layer layers[2];
+       unsigned long last_cumul_size, nr_pages;
        int interleaved, nr_channels;
        unsigned char dra[I3000_RANKS / 2], drb[I3000_RANKS];
        unsigned char *c0dra = dra, *c1dra = &dra[I3000_RANKS_PER_CHANNEL / 2];
@@ -347,7 +355,14 @@ static int i3000_probe1(struct pci_dev *pdev, int dev_idx)
         */
        interleaved = i3000_is_interleaved(c0dra, c1dra, c0drb, c1drb);
        nr_channels = interleaved ? 2 : 1;
-       mci = edac_mc_alloc(0, I3000_RANKS / nr_channels, nr_channels, 0);
+
+       layers[0].type = EDAC_MC_LAYER_CHIP_SELECT;
+       layers[0].size = I3000_RANKS / nr_channels;
+       layers[0].is_virt_csrow = true;
+       layers[1].type = EDAC_MC_LAYER_CHANNEL;
+       layers[1].size = nr_channels;
+       layers[1].is_virt_csrow = false;
+       mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers, 0);
        if (!mci)
                return -ENOMEM;
 
@@ -386,19 +401,23 @@ static int i3000_probe1(struct pci_dev *pdev, int dev_idx)
                        cumul_size <<= 1;
                debugf3("MC: %s(): (%d) cumul_size 0x%x\n",
                        __func__, i, cumul_size);
-               if (cumul_size == last_cumul_size) {
-                       csrow->mtype = MEM_EMPTY;
+               if (cumul_size == last_cumul_size)
                        continue;
-               }
 
                csrow->first_page = last_cumul_size;
                csrow->last_page = cumul_size - 1;
-               csrow->nr_pages = cumul_size - last_cumul_size;
+               nr_pages = cumul_size - last_cumul_size;
                last_cumul_size = cumul_size;
-               csrow->grain = I3000_DEAP_GRAIN;
-               csrow->mtype = MEM_DDR2;
-               csrow->dtype = DEV_UNKNOWN;
-               csrow->edac_mode = EDAC_UNKNOWN;
+
+               for (j = 0; j < nr_channels; j++) {
+                       struct dimm_info *dimm = csrow->channels[j].dimm;
+
+                       dimm->nr_pages = nr_pages / nr_channels;
+                       dimm->grain = I3000_DEAP_GRAIN;
+                       dimm->mtype = MEM_DDR2;
+                       dimm->dtype = DEV_UNKNOWN;
+                       dimm->edac_mode = EDAC_UNKNOWN;
+               }
        }
 
        /*
index 046808c6357df00d39a120caa2a0089de2b59aca..bbe43ef718238c72d159affd4d2f80e22141f88f 100644 (file)
@@ -23,6 +23,7 @@
 
 #define PCI_DEVICE_ID_INTEL_3200_HB    0x29f0
 
+#define I3200_DIMMS            4
 #define I3200_RANKS            8
 #define I3200_RANKS_PER_CHANNEL        4
 #define I3200_CHANNELS         2
@@ -217,21 +218,25 @@ static void i3200_process_error_info(struct mem_ctl_info *mci,
                return;
 
        if ((info->errsts ^ info->errsts2) & I3200_ERRSTS_BITS) {
-               edac_mc_handle_ce_no_info(mci, "UE overwrote CE");
+               edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 0, 0, 0,
+                                    -1, -1, -1, "UE overwrote CE", "", NULL);
                info->errsts = info->errsts2;
        }
 
        for (channel = 0; channel < nr_channels; channel++) {
                log = info->eccerrlog[channel];
                if (log & I3200_ECCERRLOG_UE) {
-                       edac_mc_handle_ue(mci, 0, 0,
-                               eccerrlog_row(channel, log),
-                               "i3200 UE");
+                       edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
+                                            0, 0, 0,
+                                            eccerrlog_row(channel, log),
+                                            -1, -1,
+                                            "i3000 UE", "", NULL);
                } else if (log & I3200_ECCERRLOG_CE) {
-                       edac_mc_handle_ce(mci, 0, 0,
-                               eccerrlog_syndrome(log),
-                               eccerrlog_row(channel, log), 0,
-                               "i3200 CE");
+                       edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
+                                            0, 0, eccerrlog_syndrome(log),
+                                            eccerrlog_row(channel, log),
+                                            -1, -1,
+                                            "i3000 UE", "", NULL);
                }
        }
 }
@@ -319,9 +324,9 @@ static unsigned long drb_to_nr_pages(
 static int i3200_probe1(struct pci_dev *pdev, int dev_idx)
 {
        int rc;
-       int i;
+       int i, j;
        struct mem_ctl_info *mci = NULL;
-       unsigned long last_page;
+       struct edac_mc_layer layers[2];
        u16 drbs[I3200_CHANNELS][I3200_RANKS_PER_CHANNEL];
        bool stacked;
        void __iomem *window;
@@ -336,8 +341,14 @@ static int i3200_probe1(struct pci_dev *pdev, int dev_idx)
        i3200_get_drbs(window, drbs);
        nr_channels = how_many_channels(pdev);
 
-       mci = edac_mc_alloc(sizeof(struct i3200_priv), I3200_RANKS,
-               nr_channels, 0);
+       layers[0].type = EDAC_MC_LAYER_CHIP_SELECT;
+       layers[0].size = I3200_DIMMS;
+       layers[0].is_virt_csrow = true;
+       layers[1].type = EDAC_MC_LAYER_CHANNEL;
+       layers[1].size = nr_channels;
+       layers[1].is_virt_csrow = false;
+       mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers,
+                           sizeof(struct i3200_priv));
        if (!mci)
                return -ENOMEM;
 
@@ -366,7 +377,6 @@ static int i3200_probe1(struct pci_dev *pdev, int dev_idx)
         * cumulative; the last one will contain the total memory
         * contained in all ranks.
         */
-       last_page = -1UL;
        for (i = 0; i < mci->nr_csrows; i++) {
                unsigned long nr_pages;
                struct csrow_info *csrow = &mci->csrows[i];
@@ -375,20 +385,18 @@ static int i3200_probe1(struct pci_dev *pdev, int dev_idx)
                        i / I3200_RANKS_PER_CHANNEL,
                        i % I3200_RANKS_PER_CHANNEL);
 
-               if (nr_pages == 0) {
-                       csrow->mtype = MEM_EMPTY;
+               if (nr_pages == 0)
                        continue;
-               }
 
-               csrow->first_page = last_page + 1;
-               last_page += nr_pages;
-               csrow->last_page = last_page;
-               csrow->nr_pages = nr_pages;
+               for (j = 0; j < nr_channels; j++) {
+                       struct dimm_info *dimm = csrow->channels[j].dimm;
 
-               csrow->grain = nr_pages << PAGE_SHIFT;
-               csrow->mtype = MEM_DDR2;
-               csrow->dtype = DEV_UNKNOWN;
-               csrow->edac_mode = EDAC_UNKNOWN;
+                       dimm->nr_pages = nr_pages / nr_channels;
+                       dimm->grain = nr_pages << PAGE_SHIFT;
+                       dimm->mtype = MEM_DDR2;
+                       dimm->dtype = DEV_UNKNOWN;
+                       dimm->edac_mode = EDAC_UNKNOWN;
+               }
        }
 
        i3200_clear_error_info(mci);
index a2680d8e744b1f8de5239e50a1332535308ca26d..11ea835f155a840dc2ae3048ca67b904e1385801 100644 (file)
 #define MTR3           0x8C
 
 #define NUM_MTRS               4
-#define CHANNELS_PER_BRANCH    (2)
+#define CHANNELS_PER_BRANCH    2
+#define MAX_BRANCHES           2
 
 /* Defines to extract the vaious fields from the
  *     MTRx - Memory Technology Registers
@@ -473,7 +474,6 @@ static void i5000_process_fatal_error_info(struct mem_ctl_info *mci,
        char msg[EDAC_MC_LABEL_LEN + 1 + 160];
        char *specific = NULL;
        u32 allErrors;
-       int branch;
        int channel;
        int bank;
        int rank;
@@ -485,8 +485,7 @@ static void i5000_process_fatal_error_info(struct mem_ctl_info *mci,
        if (!allErrors)
                return;         /* if no error, return now */
 
-       branch = EXTRACT_FBDCHAN_INDX(info->ferr_fat_fbd);
-       channel = branch;
+       channel = EXTRACT_FBDCHAN_INDX(info->ferr_fat_fbd);
 
        /* Use the NON-Recoverable macros to extract data */
        bank = NREC_BANK(info->nrecmema);
@@ -495,9 +494,9 @@ static void i5000_process_fatal_error_info(struct mem_ctl_info *mci,
        ras = NREC_RAS(info->nrecmemb);
        cas = NREC_CAS(info->nrecmemb);
 
-       debugf0("\t\tCSROW= %d  Channels= %d,%d  (Branch= %d "
-               "DRAM Bank= %d rdwr= %s ras= %d cas= %d)\n",
-               rank, channel, channel + 1, branch >> 1, bank,
+       debugf0("\t\tCSROW= %d  Channel= %d "
+               "(DRAM Bank= %d rdwr= %s ras= %d cas= %d)\n",
+               rank, channel, bank,
                rdwr ? "Write" : "Read", ras, cas);
 
        /* Only 1 bit will be on */
@@ -533,13 +532,14 @@ static void i5000_process_fatal_error_info(struct mem_ctl_info *mci,
 
        /* Form out message */
        snprintf(msg, sizeof(msg),
-                "(Branch=%d DRAM-Bank=%d RDWR=%s RAS=%d CAS=%d "
-                "FATAL Err=0x%x (%s))",
-                branch >> 1, bank, rdwr ? "Write" : "Read", ras, cas,
-                allErrors, specific);
+                "Bank=%d RAS=%d CAS=%d FATAL Err=0x%x (%s)",
+                bank, ras, cas, allErrors, specific);
 
        /* Call the helper to output message */
-       edac_mc_handle_fbd_ue(mci, rank, channel, channel + 1, msg);
+       edac_mc_handle_error(HW_EVENT_ERR_FATAL, mci, 0, 0, 0,
+                            channel >> 1, channel & 1, rank,
+                            rdwr ? "Write error" : "Read error",
+                            msg, NULL);
 }
 
 /*
@@ -633,13 +633,14 @@ static void i5000_process_nonfatal_error_info(struct mem_ctl_info *mci,
 
                /* Form out message */
                snprintf(msg, sizeof(msg),
-                        "(Branch=%d DRAM-Bank=%d RDWR=%s RAS=%d "
-                        "CAS=%d, UE Err=0x%x (%s))",
-                        branch >> 1, bank, rdwr ? "Write" : "Read", ras, cas,
-                        ue_errors, specific);
+                        "Rank=%d Bank=%d RAS=%d CAS=%d, UE Err=0x%x (%s)",
+                        rank, bank, ras, cas, ue_errors, specific);
 
                /* Call the helper to output message */
-               edac_mc_handle_fbd_ue(mci, rank, channel, channel + 1, msg);
+               edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 0, 0, 0,
+                               channel >> 1, -1, rank,
+                               rdwr ? "Write error" : "Read error",
+                               msg, NULL);
        }
 
        /* Check correctable errors */
@@ -685,13 +686,16 @@ static void i5000_process_nonfatal_error_info(struct mem_ctl_info *mci,
 
                /* Form out message */
                snprintf(msg, sizeof(msg),
-                        "(Branch=%d DRAM-Bank=%d RDWR=%s RAS=%d "
+                        "Rank=%d Bank=%d RDWR=%s RAS=%d "
                         "CAS=%d, CE Err=0x%x (%s))", branch >> 1, bank,
                         rdwr ? "Write" : "Read", ras, cas, ce_errors,
                         specific);
 
                /* Call the helper to output message */
-               edac_mc_handle_fbd_ce(mci, rank, channel, msg);
+               edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 0, 0, 0,
+                               channel >> 1, channel % 2, rank,
+                               rdwr ? "Write error" : "Read error",
+                               msg, NULL);
        }
 
        if (!misc_messages)
@@ -731,11 +735,12 @@ static void i5000_process_nonfatal_error_info(struct mem_ctl_info *mci,
 
                /* Form out message */
                snprintf(msg, sizeof(msg),
-                        "(Branch=%d Err=%#x (%s))", branch >> 1,
-                        misc_errors, specific);
+                        "Err=%#x (%s)", misc_errors, specific);
 
                /* Call the helper to output message */
-               edac_mc_handle_fbd_ce(mci, 0, 0, msg);
+               edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 0, 0, 0,
+                               branch >> 1, -1, -1,
+                               "Misc error", msg, NULL);
        }
 }
 
@@ -956,14 +961,14 @@ static int determine_amb_present_reg(struct i5000_pvt *pvt, int channel)
  *
  *     return the proper MTR register as determine by the csrow and channel desired
  */
-static int determine_mtr(struct i5000_pvt *pvt, int csrow, int channel)
+static int determine_mtr(struct i5000_pvt *pvt, int slot, int channel)
 {
        int mtr;
 
        if (channel < CHANNELS_PER_BRANCH)
-               mtr = pvt->b0_mtr[csrow >> 1];
+               mtr = pvt->b0_mtr[slot];
        else
-               mtr = pvt->b1_mtr[csrow >> 1];
+               mtr = pvt->b1_mtr[slot];
 
        return mtr;
 }
@@ -988,37 +993,34 @@ static void decode_mtr(int slot_row, u16 mtr)
        debugf2("\t\tNUMCOL: %s\n", numcol_toString[MTR_DIMM_COLS(mtr)]);
 }
 
-static void handle_channel(struct i5000_pvt *pvt, int csrow, int channel,
+static void handle_channel(struct i5000_pvt *pvt, int slot, int channel,
                        struct i5000_dimm_info *dinfo)
 {
        int mtr;
        int amb_present_reg;
        int addrBits;
 
-       mtr = determine_mtr(pvt, csrow, channel);
+       mtr = determine_mtr(pvt, slot, channel);
        if (MTR_DIMMS_PRESENT(mtr)) {
                amb_present_reg = determine_amb_present_reg(pvt, channel);
 
-               /* Determine if there is  a  DIMM present in this DIMM slot */
-               if (amb_present_reg & (1 << (csrow >> 1))) {
+               /* Determine if there is a DIMM present in this DIMM slot */
+               if (amb_present_reg) {
                        dinfo->dual_rank = MTR_DIMM_RANK(mtr);
 
-                       if (!((dinfo->dual_rank == 0) &&
-                               ((csrow & 0x1) == 0x1))) {
-                               /* Start with the number of bits for a Bank
-                                * on the DRAM */
-                               addrBits = MTR_DRAM_BANKS_ADDR_BITS(mtr);
-                               /* Add thenumber of ROW bits */
-                               addrBits += MTR_DIMM_ROWS_ADDR_BITS(mtr);
-                               /* add the number of COLUMN bits */
-                               addrBits += MTR_DIMM_COLS_ADDR_BITS(mtr);
-
-                               addrBits += 6;  /* add 64 bits per DIMM */
-                               addrBits -= 20; /* divide by 2^^20 */
-                               addrBits -= 3;  /* 8 bits per bytes */
-
-                               dinfo->megabytes = 1 << addrBits;
-                       }
+                       /* Start with the number of bits for a Bank
+                               * on the DRAM */
+                       addrBits = MTR_DRAM_BANKS_ADDR_BITS(mtr);
+                       /* Add the number of ROW bits */
+                       addrBits += MTR_DIMM_ROWS_ADDR_BITS(mtr);
+                       /* add the number of COLUMN bits */
+                       addrBits += MTR_DIMM_COLS_ADDR_BITS(mtr);
+
+                       addrBits += 6;  /* add 64 bits per DIMM */
+                       addrBits -= 20; /* divide by 2^^20 */
+                       addrBits -= 3;  /* 8 bits per bytes */
+
+                       dinfo->megabytes = 1 << addrBits;
                }
        }
 }
@@ -1032,10 +1034,9 @@ static void handle_channel(struct i5000_pvt *pvt, int csrow, int channel,
 static void calculate_dimm_size(struct i5000_pvt *pvt)
 {
        struct i5000_dimm_info *dinfo;
-       int csrow, max_csrows;
+       int slot, channel, branch;
        char *p, *mem_buffer;
        int space, n;
-       int channel;
 
        /* ================= Generate some debug output ================= */
        space = PAGE_SIZE;
@@ -1046,22 +1047,17 @@ static void calculate_dimm_size(struct i5000_pvt *pvt)
                return;
        }
 
-       n = snprintf(p, space, "\n");
-       p += n;
-       space -= n;
-
-       /* Scan all the actual CSROWS (which is # of DIMMS * 2)
+       /* Scan all the actual slots
         * and calculate the information for each DIMM
-        * Start with the highest csrow first, to display it first
-        * and work toward the 0th csrow
+        * Start with the highest slot first, to display it first
+        * and work toward the 0th slot
         */
-       max_csrows = pvt->maxdimmperch * 2;
-       for (csrow = max_csrows - 1; csrow >= 0; csrow--) {
+       for (slot = pvt->maxdimmperch - 1; slot >= 0; slot--) {
 
-               /* on an odd csrow, first output a 'boundary' marker,
+               /* on an odd slot, first output a 'boundary' marker,
                 * then reset the message buffer  */
-               if (csrow & 0x1) {
-                       n = snprintf(p, space, "---------------------------"
+               if (slot & 0x1) {
+                       n = snprintf(p, space, "--------------------------"
                                "--------------------------------");
                        p += n;
                        space -= n;
@@ -1069,30 +1065,39 @@ static void calculate_dimm_size(struct i5000_pvt *pvt)
                        p = mem_buffer;
                        space = PAGE_SIZE;
                }
-               n = snprintf(p, space, "csrow %2d    ", csrow);
+               n = snprintf(p, space, "slot %2d    ", slot);
                p += n;
                space -= n;
 
                for (channel = 0; channel < pvt->maxch; channel++) {
-                       dinfo = &pvt->dimm_info[csrow][channel];
-                       handle_channel(pvt, csrow, channel, dinfo);
-                       n = snprintf(p, space, "%4d MB   | ", dinfo->megabytes);
+                       dinfo = &pvt->dimm_info[slot][channel];
+                       handle_channel(pvt, slot, channel, dinfo);
+                       if (dinfo->megabytes)
+                               n = snprintf(p, space, "%4d MB %dR| ",
+                                            dinfo->megabytes, dinfo->dual_rank + 1);
+                       else
+                               n = snprintf(p, space, "%4d MB   | ", 0);
                        p += n;
                        space -= n;
                }
-               n = snprintf(p, space, "\n");
                p += n;
                space -= n;
+               debugf2("%s\n", mem_buffer);
+               p = mem_buffer;
+               space = PAGE_SIZE;
        }
 
        /* Output the last bottom 'boundary' marker */
-       n = snprintf(p, space, "---------------------------"
-               "--------------------------------\n");
+       n = snprintf(p, space, "--------------------------"
+               "--------------------------------");
        p += n;
        space -= n;
+       debugf2("%s\n", mem_buffer);
+       p = mem_buffer;
+       space = PAGE_SIZE;
 
        /* now output the 'channel' labels */
-       n = snprintf(p, space, "            ");
+       n = snprintf(p, space, "           ");
        p += n;
        space -= n;
        for (channel = 0; channel < pvt->maxch; channel++) {
@@ -1100,9 +1105,17 @@ static void calculate_dimm_size(struct i5000_pvt *pvt)
                p += n;
                space -= n;
        }
-       n = snprintf(p, space, "\n");
+       debugf2("%s\n", mem_buffer);
+       p = mem_buffer;
+       space = PAGE_SIZE;
+
+       n = snprintf(p, space, "           ");
        p += n;
-       space -= n;
+       for (branch = 0; branch < MAX_BRANCHES; branch++) {
+               n = snprintf(p, space, "       branch %d       | ", branch);
+               p += n;
+               space -= n;
+       }
 
        /* output the last message and free buffer */
        debugf2("%s\n", mem_buffer);
@@ -1235,13 +1248,13 @@ static void i5000_get_mc_regs(struct mem_ctl_info *mci)
 static int i5000_init_csrows(struct mem_ctl_info *mci)
 {
        struct i5000_pvt *pvt;
-       struct csrow_info *p_csrow;
+       struct dimm_info *dimm;
        int empty, channel_count;
        int max_csrows;
-       int mtr, mtr1;
+       int mtr;
        int csrow_megs;
        int channel;
-       int csrow;
+       int slot;
 
        pvt = mci->pvt_info;
 
@@ -1250,43 +1263,40 @@ static int i5000_init_csrows(struct mem_ctl_info *mci)
 
        empty = 1;              /* Assume NO memory */
 
-       for (csrow = 0; csrow < max_csrows; csrow++) {
-               p_csrow = &mci->csrows[csrow];
-
-               p_csrow->csrow_idx = csrow;
-
-               /* use branch 0 for the basis */
-               mtr = pvt->b0_mtr[csrow >> 1];
-               mtr1 = pvt->b1_mtr[csrow >> 1];
-
-               /* if no DIMMS on this row, continue */
-               if (!MTR_DIMMS_PRESENT(mtr) && !MTR_DIMMS_PRESENT(mtr1))
-                       continue;
+       /*
+        * FIXME: The memory layout used to map slot/channel into the
+        * real memory architecture is weird: branch+slot are "csrows"
+        * and channel is channel. That required an extra array (dimm_info)
+        * to map the dimms. A good cleanup would be to remove this array,
+        * and do a loop here with branch, channel, slot
+        */
+       for (slot = 0; slot < max_csrows; slot++) {
+               for (channel = 0; channel < pvt->maxch; channel++) {
 
-               /* FAKE OUT VALUES, FIXME */
-               p_csrow->first_page = 0 + csrow * 20;
-               p_csrow->last_page = 9 + csrow * 20;
-               p_csrow->page_mask = 0xFFF;
+                       mtr = determine_mtr(pvt, slot, channel);
 
-               p_csrow->grain = 8;
+                       if (!MTR_DIMMS_PRESENT(mtr))
+                               continue;
 
-               csrow_megs = 0;
-               for (channel = 0; channel < pvt->maxch; channel++) {
-                       csrow_megs += pvt->dimm_info[csrow][channel].megabytes;
-               }
+                       dimm = EDAC_DIMM_PTR(mci->layers, mci->dimms, mci->n_layers,
+                                      channel / MAX_BRANCHES,
+                                      channel % MAX_BRANCHES, slot);
 
-               p_csrow->nr_pages = csrow_megs << 8;
+                       csrow_megs = pvt->dimm_info[slot][channel].megabytes;
+                       dimm->grain = 8;
 
-               /* Assume DDR2 for now */
-               p_csrow->mtype = MEM_FB_DDR2;
+                       /* Assume DDR2 for now */
+                       dimm->mtype = MEM_FB_DDR2;
 
-               /* ask what device type on this row */
-               if (MTR_DRAM_WIDTH(mtr))
-                       p_csrow->dtype = DEV_X8;
-               else
-                       p_csrow->dtype = DEV_X4;
+                       /* ask what device type on this row */
+                       if (MTR_DRAM_WIDTH(mtr))
+                               dimm->dtype = DEV_X8;
+                       else
+                               dimm->dtype = DEV_X4;
 
-               p_csrow->edac_mode = EDAC_S8ECD8ED;
+                       dimm->edac_mode = EDAC_S8ECD8ED;
+                       dimm->nr_pages = csrow_megs << 8;
+               }
 
                empty = 0;
        }
@@ -1317,7 +1327,7 @@ static void i5000_enable_error_reporting(struct mem_ctl_info *mci)
 }
 
 /*
- * i5000_get_dimm_and_channel_counts(pdev, &num_csrows, &num_channels)
+ * i5000_get_dimm_and_channel_counts(pdev, &nr_csrows, &num_channels)
  *
  *     ask the device how many channels are present and how many CSROWS
  *      as well
@@ -1332,7 +1342,7 @@ static void i5000_get_dimm_and_channel_counts(struct pci_dev *pdev,
         * supported on this memory controller
         */
        pci_read_config_byte(pdev, MAXDIMMPERCH, &value);
-       *num_dimms_per_channel = (int)value *2;
+       *num_dimms_per_channel = (int)value;
 
        pci_read_config_byte(pdev, MAXCH, &value);
        *num_channels = (int)value;
@@ -1348,10 +1358,10 @@ static void i5000_get_dimm_and_channel_counts(struct pci_dev *pdev,
 static int i5000_probe1(struct pci_dev *pdev, int dev_idx)
 {
        struct mem_ctl_info *mci;
+       struct edac_mc_layer layers[3];
        struct i5000_pvt *pvt;
        int num_channels;
        int num_dimms_per_channel;
-       int num_csrows;
 
        debugf0("MC: %s: %s(), pdev bus %u dev=0x%x fn=0x%x\n",
                __FILE__, __func__,
@@ -1377,14 +1387,22 @@ static int i5000_probe1(struct pci_dev *pdev, int dev_idx)
         */
        i5000_get_dimm_and_channel_counts(pdev, &num_dimms_per_channel,
                                        &num_channels);
-       num_csrows = num_dimms_per_channel * 2;
 
-       debugf0("MC: %s(): Number of - Channels= %d  DIMMS= %d  CSROWS= %d\n",
-               __func__, num_channels, num_dimms_per_channel, num_csrows);
+       debugf0("MC: %s(): Number of Branches=2 Channels= %d  DIMMS= %d\n",
+               __func__, num_channels, num_dimms_per_channel);
 
        /* allocate a new MC control structure */
-       mci = edac_mc_alloc(sizeof(*pvt), num_csrows, num_channels, 0);
 
+       layers[0].type = EDAC_MC_LAYER_BRANCH;
+       layers[0].size = MAX_BRANCHES;
+       layers[0].is_virt_csrow = false;
+       layers[1].type = EDAC_MC_LAYER_CHANNEL;
+       layers[1].size = num_channels / MAX_BRANCHES;
+       layers[1].is_virt_csrow = false;
+       layers[2].type = EDAC_MC_LAYER_SLOT;
+       layers[2].size = num_dimms_per_channel;
+       layers[2].is_virt_csrow = true;
+       mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers, sizeof(*pvt));
        if (mci == NULL)
                return -ENOMEM;
 
index d500749464ea6038e147f25404586c1289d28b1d..e9e7c2a29dc389d9462f50e5b1d17367b9a7d194 100644 (file)
  * rows for each respective channel are laid out one after another,
  * the first half belonging to channel 0, the second half belonging
  * to channel 1.
+ *
+ * This driver is for DDR2 DIMMs, and it uses chip select to select among the
+ * several ranks. However, instead of showing memories as ranks, it outputs
+ * them as DIMM's. An internal table creates the association between ranks
+ * and DIMM's.
  */
 #include <linux/module.h>
 #include <linux/init.h>
@@ -410,14 +415,6 @@ static int i5100_csrow_to_chan(const struct mem_ctl_info *mci, int csrow)
        return csrow / priv->ranksperchan;
 }
 
-static unsigned i5100_rank_to_csrow(const struct mem_ctl_info *mci,
-                                   int chan, int rank)
-{
-       const struct i5100_priv *priv = mci->pvt_info;
-
-       return chan * priv->ranksperchan + rank;
-}
-
 static void i5100_handle_ce(struct mem_ctl_info *mci,
                            int chan,
                            unsigned bank,
@@ -427,17 +424,17 @@ static void i5100_handle_ce(struct mem_ctl_info *mci,
                            unsigned ras,
                            const char *msg)
 {
-       const int csrow = i5100_rank_to_csrow(mci, chan, rank);
+       char detail[80];
 
-       printk(KERN_ERR
-               "CE chan %d, bank %u, rank %u, syndrome 0x%lx, "
-               "cas %u, ras %u, csrow %u, label \"%s\": %s\n",
-               chan, bank, rank, syndrome, cas, ras,
-               csrow, mci->csrows[csrow].channels[0].label, msg);
+       /* Form out message */
+       snprintf(detail, sizeof(detail),
+                "bank %u, cas %u, ras %u\n",
+                bank, cas, ras);
 
-       mci->ce_count++;
-       mci->csrows[csrow].ce_count++;
-       mci->csrows[csrow].channels[0].ce_count++;
+       edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
+                            0, 0, syndrome,
+                            chan, rank, -1,
+                            msg, detail, NULL);
 }
 
 static void i5100_handle_ue(struct mem_ctl_info *mci,
@@ -449,16 +446,17 @@ static void i5100_handle_ue(struct mem_ctl_info *mci,
                            unsigned ras,
                            const char *msg)
 {
-       const int csrow = i5100_rank_to_csrow(mci, chan, rank);
+       char detail[80];
 
-       printk(KERN_ERR
-               "UE chan %d, bank %u, rank %u, syndrome 0x%lx, "
-               "cas %u, ras %u, csrow %u, label \"%s\": %s\n",
-               chan, bank, rank, syndrome, cas, ras,
-               csrow, mci->csrows[csrow].channels[0].label, msg);
+       /* Form out message */
+       snprintf(detail, sizeof(detail),
+                "bank %u, cas %u, ras %u\n",
+                bank, cas, ras);
 
-       mci->ue_count++;
-       mci->csrows[csrow].ue_count++;
+       edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
+                            0, 0, syndrome,
+                            chan, rank, -1,
+                            msg, detail, NULL);
 }
 
 static void i5100_read_log(struct mem_ctl_info *mci, int chan,
@@ -835,10 +833,10 @@ static void __devinit i5100_init_interleaving(struct pci_dev *pdev,
 static void __devinit i5100_init_csrows(struct mem_ctl_info *mci)
 {
        int i;
-       unsigned long total_pages = 0UL;
        struct i5100_priv *priv = mci->pvt_info;
 
-       for (i = 0; i < mci->nr_csrows; i++) {
+       for (i = 0; i < mci->tot_dimms; i++) {
+               struct dimm_info *dimm;
                const unsigned long npages = i5100_npages(mci, i);
                const unsigned chan = i5100_csrow_to_chan(mci, i);
                const unsigned rank = i5100_csrow_to_rank(mci, i);
@@ -846,33 +844,23 @@ static void __devinit i5100_init_csrows(struct mem_ctl_info *mci)
                if (!npages)
                        continue;
 
-               /*
-                * FIXME: these two are totally bogus -- I don't see how to
-                * map them correctly to this structure...
-                */
-               mci->csrows[i].first_page = total_pages;
-               mci->csrows[i].last_page = total_pages + npages - 1;
-               mci->csrows[i].page_mask = 0UL;
-
-               mci->csrows[i].nr_pages = npages;
-               mci->csrows[i].grain = 32;
-               mci->csrows[i].csrow_idx = i;
-               mci->csrows[i].dtype =
-                       (priv->mtr[chan][rank].width == 4) ? DEV_X4 : DEV_X8;
-               mci->csrows[i].ue_count = 0;
-               mci->csrows[i].ce_count = 0;
-               mci->csrows[i].mtype = MEM_RDDR2;
-               mci->csrows[i].edac_mode = EDAC_SECDED;
-               mci->csrows[i].mci = mci;
-               mci->csrows[i].nr_channels = 1;
-               mci->csrows[i].channels[0].chan_idx = 0;
-               mci->csrows[i].channels[0].ce_count = 0;
-               mci->csrows[i].channels[0].csrow = mci->csrows + i;
-               snprintf(mci->csrows[i].channels[0].label,
-                        sizeof(mci->csrows[i].channels[0].label),
-                        "DIMM%u", i5100_rank_to_slot(mci, chan, rank));
-
-               total_pages += npages;
+               dimm = EDAC_DIMM_PTR(mci->layers, mci->dimms, mci->n_layers,
+                              chan, rank, 0);
+
+               dimm->nr_pages = npages;
+               if (npages) {
+                       dimm->grain = 32;
+                       dimm->dtype = (priv->mtr[chan][rank].width == 4) ?
+                                       DEV_X4 : DEV_X8;
+                       dimm->mtype = MEM_RDDR2;
+                       dimm->edac_mode = EDAC_SECDED;
+                       snprintf(dimm->label, sizeof(dimm->label),
+                               "DIMM%u",
+                               i5100_rank_to_slot(mci, chan, rank));
+               }
+
+               debugf2("dimm channel %d, rank %d, size %ld\n",
+                       chan, rank, (long)PAGES_TO_MiB(npages));
        }
 }
 
@@ -881,6 +869,7 @@ static int __devinit i5100_init_one(struct pci_dev *pdev,
 {
        int rc;
        struct mem_ctl_info *mci;
+       struct edac_mc_layer layers[2];
        struct i5100_priv *priv;
        struct pci_dev *ch0mm, *ch1mm;
        int ret = 0;
@@ -941,7 +930,14 @@ static int __devinit i5100_init_one(struct pci_dev *pdev,
                goto bail_ch1;
        }
 
-       mci = edac_mc_alloc(sizeof(*priv), ranksperch * 2, 1, 0);
+       layers[0].type = EDAC_MC_LAYER_CHANNEL;
+       layers[0].size = 2;
+       layers[0].is_virt_csrow = false;
+       layers[1].type = EDAC_MC_LAYER_SLOT;
+       layers[1].size = ranksperch;
+       layers[1].is_virt_csrow = true;
+       mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers,
+                           sizeof(*priv));
        if (!mci) {
                ret = -ENOMEM;
                goto bail_disable_ch1;
index 1869a1018fb5215b51e80ce53c274ae5c1190566..6640c29e1885a814d8f7e69318541d1afd666037 100644 (file)
  * Intel 5400 Chipset Memory Controller Hub (MCH) - Datasheet
  *     http://developer.intel.com/design/chipsets/datashts/313070.htm
  *
+ * This Memory Controller manages DDR2 FB-DIMMs. It has 2 branches, each with
+ * 2 channels operating in lockstep no-mirror mode. Each channel can have up to
+ * 4 dimm's, each with up to 8GB.
+ *
  */
 
 #include <linux/module.h>
        edac_mc_chipset_printk(mci, level, "i5400", fmt, ##arg)
 
 /* Limits for i5400 */
-#define NUM_MTRS_PER_BRANCH    4
+#define MAX_BRANCHES           2
 #define CHANNELS_PER_BRANCH    2
-#define MAX_DIMMS_PER_CHANNEL  NUM_MTRS_PER_BRANCH
-#define        MAX_CHANNELS            4
-/* max possible csrows per channel */
-#define MAX_CSROWS             (MAX_DIMMS_PER_CHANNEL)
+#define DIMMS_PER_CHANNEL      4
+#define        MAX_CHANNELS            (MAX_BRANCHES * CHANNELS_PER_BRANCH)
 
 /* Device 16,
  * Function 0: System Address
@@ -347,16 +349,16 @@ struct i5400_pvt {
 
        u16 mir0, mir1;
 
-       u16 b0_mtr[NUM_MTRS_PER_BRANCH];        /* Memory Technlogy Reg */
+       u16 b0_mtr[DIMMS_PER_CHANNEL];  /* Memory Technlogy Reg */
        u16 b0_ambpresent0;                     /* Branch 0, Channel 0 */
        u16 b0_ambpresent1;                     /* Brnach 0, Channel 1 */
 
-       u16 b1_mtr[NUM_MTRS_PER_BRANCH];        /* Memory Technlogy Reg */
+       u16 b1_mtr[DIMMS_PER_CHANNEL];  /* Memory Technlogy Reg */
        u16 b1_ambpresent0;                     /* Branch 1, Channel 8 */
        u16 b1_ambpresent1;                     /* Branch 1, Channel 1 */
 
        /* DIMM information matrix, allocating architecture maximums */
-       struct i5400_dimm_info dimm_info[MAX_CSROWS][MAX_CHANNELS];
+       struct i5400_dimm_info dimm_info[DIMMS_PER_CHANNEL][MAX_CHANNELS];
 
        /* Actual values for this controller */
        int maxch;                              /* Max channels */
@@ -532,13 +534,15 @@ static void i5400_proccess_non_recoverable_info(struct mem_ctl_info *mci,
        int ras, cas;
        int errnum;
        char *type = NULL;
+       enum hw_event_mc_err_type tp_event = HW_EVENT_ERR_UNCORRECTED;
 
        if (!allErrors)
                return;         /* if no error, return now */
 
-       if (allErrors &  ERROR_FAT_MASK)
+       if (allErrors &  ERROR_FAT_MASK) {
                type = "FATAL";
-       else if (allErrors & FERR_NF_UNCORRECTABLE)
+               tp_event = HW_EVENT_ERR_FATAL;
+       } else if (allErrors & FERR_NF_UNCORRECTABLE)
                type = "NON-FATAL uncorrected";
        else
                type = "NON-FATAL recoverable";
@@ -556,7 +560,7 @@ static void i5400_proccess_non_recoverable_info(struct mem_ctl_info *mci,
        ras = nrec_ras(info);
        cas = nrec_cas(info);
 
-       debugf0("\t\tCSROW= %d  Channels= %d,%d  (Branch= %d "
+       debugf0("\t\tDIMM= %d  Channels= %d,%d  (Branch= %d "
                "DRAM Bank= %d Buffer ID = %d rdwr= %s ras= %d cas= %d)\n",
                rank, channel, channel + 1, branch >> 1, bank,
                buf_id, rdwr_str(rdwr), ras, cas);
@@ -566,13 +570,13 @@ static void i5400_proccess_non_recoverable_info(struct mem_ctl_info *mci,
 
        /* Form out message */
        snprintf(msg, sizeof(msg),
-                "%s (Branch=%d DRAM-Bank=%d Buffer ID = %d RDWR=%s "
-                "RAS=%d CAS=%d %s Err=0x%lx (%s))",
-                type, branch >> 1, bank, buf_id, rdwr_str(rdwr), ras, cas,
-                type, allErrors, error_name[errnum]);
+                "Bank=%d Buffer ID = %d RAS=%d CAS=%d Err=0x%lx (%s)",
+                bank, buf_id, ras, cas, allErrors, error_name[errnum]);
 
-       /* Call the helper to output message */
-       edac_mc_handle_fbd_ue(mci, rank, channel, channel + 1, msg);
+       edac_mc_handle_error(tp_event, mci, 0, 0, 0,
+                            branch >> 1, -1, rank,
+                            rdwr ? "Write error" : "Read error",
+                            msg, NULL);
 }
 
 /*
@@ -630,7 +634,7 @@ static void i5400_process_nonfatal_error_info(struct mem_ctl_info *mci,
                /* Only 1 bit will be on */
                errnum = find_first_bit(&allErrors, ARRAY_SIZE(error_name));
 
-               debugf0("\t\tCSROW= %d Channel= %d  (Branch %d "
+               debugf0("\t\tDIMM= %d Channel= %d  (Branch %d "
                        "DRAM Bank= %d rdwr= %s ras= %d cas= %d)\n",
                        rank, channel, branch >> 1, bank,
                        rdwr_str(rdwr), ras, cas);
@@ -642,8 +646,10 @@ static void i5400_process_nonfatal_error_info(struct mem_ctl_info *mci,
                         branch >> 1, bank, rdwr_str(rdwr), ras, cas,
                         allErrors, error_name[errnum]);
 
-               /* Call the helper to output message */
-               edac_mc_handle_fbd_ce(mci, rank, channel, msg);
+               edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 0, 0, 0,
+                                    branch >> 1, channel % 2, rank,
+                                    rdwr ? "Write error" : "Read error",
+                                    msg, NULL);
 
                return;
        }
@@ -831,8 +837,8 @@ static int i5400_get_devices(struct mem_ctl_info *mci, int dev_idx)
 /*
  *     determine_amb_present
  *
- *             the information is contained in NUM_MTRS_PER_BRANCH different
- *             registers determining which of the NUM_MTRS_PER_BRANCH requires
+ *             the information is contained in DIMMS_PER_CHANNEL different
+ *             registers determining which of the DIMMS_PER_CHANNEL requires
  *              knowing which channel is in question
  *
  *     2 branches, each with 2 channels
@@ -861,11 +867,11 @@ static int determine_amb_present_reg(struct i5400_pvt *pvt, int channel)
 }
 
 /*
- * determine_mtr(pvt, csrow, channel)
+ * determine_mtr(pvt, dimm, channel)
  *
- * return the proper MTR register as determine by the csrow and desired channel
+ * return the proper MTR register as determine by the dimm and desired channel
  */
-static int determine_mtr(struct i5400_pvt *pvt, int csrow, int channel)
+static int determine_mtr(struct i5400_pvt *pvt, int dimm, int channel)
 {
        int mtr;
        int n;
@@ -873,11 +879,11 @@ static int determine_mtr(struct i5400_pvt *pvt, int csrow, int channel)
        /* There is one MTR for each slot pair of FB-DIMMs,
           Each slot pair may be at branch 0 or branch 1.
         */
-       n = csrow;
+       n = dimm;
 
-       if (n >= NUM_MTRS_PER_BRANCH) {
-               debugf0("ERROR: trying to access an invalid csrow: %d\n",
-                       csrow);
+       if (n >= DIMMS_PER_CHANNEL) {
+               debugf0("ERROR: trying to access an invalid dimm: %d\n",
+                       dimm);
                return 0;
        }
 
@@ -913,19 +919,19 @@ static void decode_mtr(int slot_row, u16 mtr)
        debugf2("\t\tNUMCOL: %s\n", numcol_toString[MTR_DIMM_COLS(mtr)]);
 }
 
-static void handle_channel(struct i5400_pvt *pvt, int csrow, int channel,
+static void handle_channel(struct i5400_pvt *pvt, int dimm, int channel,
                        struct i5400_dimm_info *dinfo)
 {
        int mtr;
        int amb_present_reg;
        int addrBits;
 
-       mtr = determine_mtr(pvt, csrow, channel);
+       mtr = determine_mtr(pvt, dimm, channel);
        if (MTR_DIMMS_PRESENT(mtr)) {
                amb_present_reg = determine_amb_present_reg(pvt, channel);
 
                /* Determine if there is a DIMM present in this DIMM slot */
-               if (amb_present_reg & (1 << csrow)) {
+               if (amb_present_reg & (1 << dimm)) {
                        /* Start with the number of bits for a Bank
                         * on the DRAM */
                        addrBits = MTR_DRAM_BANKS_ADDR_BITS(mtr);
@@ -954,10 +960,10 @@ static void handle_channel(struct i5400_pvt *pvt, int csrow, int channel,
 static void calculate_dimm_size(struct i5400_pvt *pvt)
 {
        struct i5400_dimm_info *dinfo;
-       int csrow, max_csrows;
+       int dimm, max_dimms;
        char *p, *mem_buffer;
        int space, n;
-       int channel;
+       int channel, branch;
 
        /* ================= Generate some debug output ================= */
        space = PAGE_SIZE;
@@ -968,32 +974,32 @@ static void calculate_dimm_size(struct i5400_pvt *pvt)
                return;
        }
 
-       /* Scan all the actual CSROWS
+       /* Scan all the actual DIMMS
         * and calculate the information for each DIMM
-        * Start with the highest csrow first, to display it first
-        * and work toward the 0th csrow
+        * Start with the highest dimm first, to display it first
+        * and work toward the 0th dimm
         */
-       max_csrows = pvt->maxdimmperch;
-       for (csrow = max_csrows - 1; csrow >= 0; csrow--) {
+       max_dimms = pvt->maxdimmperch;
+       for (dimm = max_dimms - 1; dimm >= 0; dimm--) {
 
-               /* on an odd csrow, first output a 'boundary' marker,
+               /* on an odd dimm, first output a 'boundary' marker,
                 * then reset the message buffer  */
-               if (csrow & 0x1) {
+               if (dimm & 0x1) {
                        n = snprintf(p, space, "---------------------------"
-                                       "--------------------------------");
+                                       "-------------------------------");
                        p += n;
                        space -= n;
                        debugf2("%s\n", mem_buffer);
                        p = mem_buffer;
                        space = PAGE_SIZE;
                }
-               n = snprintf(p, space, "csrow %2d    ", csrow);
+               n = snprintf(p, space, "dimm %2d    ", dimm);
                p += n;
                space -= n;
 
                for (channel = 0; channel < pvt->maxch; channel++) {
-                       dinfo = &pvt->dimm_info[csrow][channel];
-                       handle_channel(pvt, csrow, channel, dinfo);
+                       dinfo = &pvt->dimm_info[dimm][channel];
+                       handle_channel(pvt, dimm, channel, dinfo);
                        n = snprintf(p, space, "%4d MB   | ", dinfo->megabytes);
                        p += n;
                        space -= n;
@@ -1005,7 +1011,7 @@ static void calculate_dimm_size(struct i5400_pvt *pvt)
 
        /* Output the last bottom 'boundary' marker */
        n = snprintf(p, space, "---------------------------"
-                       "--------------------------------");
+                       "-------------------------------");
        p += n;
        space -= n;
        debugf2("%s\n", mem_buffer);
@@ -1013,7 +1019,7 @@ static void calculate_dimm_size(struct i5400_pvt *pvt)
        space = PAGE_SIZE;
 
        /* now output the 'channel' labels */
-       n = snprintf(p, space, "            ");
+       n = snprintf(p, space, "           ");
        p += n;
        space -= n;
        for (channel = 0; channel < pvt->maxch; channel++) {
@@ -1022,6 +1028,19 @@ static void calculate_dimm_size(struct i5400_pvt *pvt)
                space -= n;
        }
 
+       space -= n;
+       debugf2("%s\n", mem_buffer);
+       p = mem_buffer;
+       space = PAGE_SIZE;
+
+       n = snprintf(p, space, "           ");
+       p += n;
+       for (branch = 0; branch < MAX_BRANCHES; branch++) {
+               n = snprintf(p, space, "       branch %d       | ", branch);
+               p += n;
+               space -= n;
+       }
+
        /* output the last message and free buffer */
        debugf2("%s\n", mem_buffer);
        kfree(mem_buffer);
@@ -1080,7 +1099,7 @@ static void i5400_get_mc_regs(struct mem_ctl_info *mci)
        debugf2("MIR1: limit= 0x%x  WAY1= %u  WAY0= %x\n", limit, way1, way0);
 
        /* Get the set of MTR[0-3] regs by each branch */
-       for (slot_row = 0; slot_row < NUM_MTRS_PER_BRANCH; slot_row++) {
+       for (slot_row = 0; slot_row < DIMMS_PER_CHANNEL; slot_row++) {
                int where = MTR0 + (slot_row * sizeof(u16));
 
                /* Branch 0 set of MTR registers */
@@ -1105,7 +1124,7 @@ static void i5400_get_mc_regs(struct mem_ctl_info *mci)
        /* Read and dump branch 0's MTRs */
        debugf2("\nMemory Technology Registers:\n");
        debugf2("   Branch 0:\n");
-       for (slot_row = 0; slot_row < NUM_MTRS_PER_BRANCH; slot_row++)
+       for (slot_row = 0; slot_row < DIMMS_PER_CHANNEL; slot_row++)
                decode_mtr(slot_row, pvt->b0_mtr[slot_row]);
 
        pci_read_config_word(pvt->branch_0, AMBPRESENT_0,
@@ -1122,7 +1141,7 @@ static void i5400_get_mc_regs(struct mem_ctl_info *mci)
        } else {
                /* Read and dump  branch 1's MTRs */
                debugf2("   Branch 1:\n");
-               for (slot_row = 0; slot_row < NUM_MTRS_PER_BRANCH; slot_row++)
+               for (slot_row = 0; slot_row < DIMMS_PER_CHANNEL; slot_row++)
                        decode_mtr(slot_row, pvt->b1_mtr[slot_row]);
 
                pci_read_config_word(pvt->branch_1, AMBPRESENT_0,
@@ -1141,7 +1160,7 @@ static void i5400_get_mc_regs(struct mem_ctl_info *mci)
 }
 
 /*
- *     i5400_init_csrows       Initialize the 'csrows' table within
+ *     i5400_init_dimms        Initialize the 'dimms' table within
  *                             the mci control structure with the
  *                             addressing of memory.
  *
@@ -1149,64 +1168,68 @@ static void i5400_get_mc_regs(struct mem_ctl_info *mci)
  *             0       success
  *             1       no actual memory found on this MC
  */
-static int i5400_init_csrows(struct mem_ctl_info *mci)
+static int i5400_init_dimms(struct mem_ctl_info *mci)
 {
        struct i5400_pvt *pvt;
-       struct csrow_info *p_csrow;
-       int empty, channel_count;
-       int max_csrows;
+       struct dimm_info *dimm;
+       int ndimms, channel_count;
+       int max_dimms;
        int mtr;
-       int csrow_megs;
-       int channel;
-       int csrow;
+       int size_mb;
+       int  channel, slot;
 
        pvt = mci->pvt_info;
 
        channel_count = pvt->maxch;
-       max_csrows = pvt->maxdimmperch;
+       max_dimms = pvt->maxdimmperch;
 
-       empty = 1;              /* Assume NO memory */
+       ndimms = 0;
 
-       for (csrow = 0; csrow < max_csrows; csrow++) {
-               p_csrow = &mci->csrows[csrow];
-
-               p_csrow->csrow_idx = csrow;
-
-               /* use branch 0 for the basis */
-               mtr = determine_mtr(pvt, csrow, 0);
-
-               /* if no DIMMS on this row, continue */
-               if (!MTR_DIMMS_PRESENT(mtr))
-                       continue;
-
-               /* FAKE OUT VALUES, FIXME */
-               p_csrow->first_page = 0 + csrow * 20;
-               p_csrow->last_page = 9 + csrow * 20;
-               p_csrow->page_mask = 0xFFF;
-
-               p_csrow->grain = 8;
-
-               csrow_megs = 0;
-               for (channel = 0; channel < pvt->maxch; channel++)
-                       csrow_megs += pvt->dimm_info[csrow][channel].megabytes;
-
-               p_csrow->nr_pages = csrow_megs << 8;
-
-               /* Assume DDR2 for now */
-               p_csrow->mtype = MEM_FB_DDR2;
-
-               /* ask what device type on this row */
-               if (MTR_DRAM_WIDTH(mtr))
-                       p_csrow->dtype = DEV_X8;
-               else
-                       p_csrow->dtype = DEV_X4;
-
-               p_csrow->edac_mode = EDAC_S8ECD8ED;
-
-               empty = 0;
+       /*
+        * FIXME: remove  pvt->dimm_info[slot][channel] and use the 3
+        * layers here.
+        */
+       for (channel = 0; channel < mci->layers[0].size * mci->layers[1].size;
+            channel++) {
+               for (slot = 0; slot < mci->layers[2].size; slot++) {
+                       mtr = determine_mtr(pvt, slot, channel);
+
+                       /* if no DIMMS on this slot, continue */
+                       if (!MTR_DIMMS_PRESENT(mtr))
+                               continue;
+
+                       dimm = EDAC_DIMM_PTR(mci->layers, mci->dimms, mci->n_layers,
+                                      channel / 2, channel % 2, slot);
+
+                       size_mb =  pvt->dimm_info[slot][channel].megabytes;
+
+                       debugf2("%s: dimm%zd (branch %d channel %d slot %d): %d.%03d GB\n",
+                               __func__, dimm - mci->dimms,
+                               channel / 2, channel % 2, slot,
+                               size_mb / 1000, size_mb % 1000);
+
+                       dimm->nr_pages = size_mb << 8;
+                       dimm->grain = 8;
+                       dimm->dtype = MTR_DRAM_WIDTH(mtr) ? DEV_X8 : DEV_X4;
+                       dimm->mtype = MEM_FB_DDR2;
+                       /*
+                        * The eccc mechanism is SDDC (aka SECC), with
+                        * is similar to Chipkill.
+                        */
+                       dimm->edac_mode = MTR_DRAM_WIDTH(mtr) ?
+                                         EDAC_S8ECD8ED : EDAC_S4ECD4ED;
+                       ndimms++;
+               }
        }
 
-       return empty;
+       /*
+        * When just one memory is provided, it should be at location (0,0,0).
+        * With such single-DIMM mode, the SDCC algorithm degrades to SECDEC+.
+        */
+       if (ndimms == 1)
+               mci->dimms[0].edac_mode = EDAC_SECDED;
+
+       return (ndimms == 0);
 }
 
 /*
@@ -1242,9 +1265,7 @@ static int i5400_probe1(struct pci_dev *pdev, int dev_idx)
 {
        struct mem_ctl_info *mci;
        struct i5400_pvt *pvt;
-       int num_channels;
-       int num_dimms_per_channel;
-       int num_csrows;
+       struct edac_mc_layer layers[3];
 
        if (dev_idx >= ARRAY_SIZE(i5400_devs))
                return -EINVAL;
@@ -1258,23 +1279,21 @@ static int i5400_probe1(struct pci_dev *pdev, int dev_idx)
        if (PCI_FUNC(pdev->devfn) != 0)
                return -ENODEV;
 
-       /* As we don't have a motherboard identification routine to determine
-        * actual number of slots/dimms per channel, we thus utilize the
-        * resource as specified by the chipset. Thus, we might have
-        * have more DIMMs per channel than actually on the mobo, but this
-        * allows the driver to support up to the chipset max, without
-        * some fancy mobo determination.
+       /*
+        * allocate a new MC control structure
+        *
+        * This drivers uses the DIMM slot as "csrow" and the rest as "channel".
         */
-       num_dimms_per_channel = MAX_DIMMS_PER_CHANNEL;
-       num_channels = MAX_CHANNELS;
-       num_csrows = num_dimms_per_channel;
-
-       debugf0("MC: %s(): Number of - Channels= %d  DIMMS= %d  CSROWS= %d\n",
-               __func__, num_channels, num_dimms_per_channel, num_csrows);
-
-       /* allocate a new MC control structure */
-       mci = edac_mc_alloc(sizeof(*pvt), num_csrows, num_channels, 0);
-
+       layers[0].type = EDAC_MC_LAYER_BRANCH;
+       layers[0].size = MAX_BRANCHES;
+       layers[0].is_virt_csrow = false;
+       layers[1].type = EDAC_MC_LAYER_CHANNEL;
+       layers[1].size = CHANNELS_PER_BRANCH;
+       layers[1].is_virt_csrow = false;
+       layers[2].type = EDAC_MC_LAYER_SLOT;
+       layers[2].size = DIMMS_PER_CHANNEL;
+       layers[2].is_virt_csrow = true;
+       mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers, sizeof(*pvt));
        if (mci == NULL)
                return -ENOMEM;
 
@@ -1284,8 +1303,8 @@ static int i5400_probe1(struct pci_dev *pdev, int dev_idx)
 
        pvt = mci->pvt_info;
        pvt->system_address = pdev;     /* Record this device in our private */
-       pvt->maxch = num_channels;
-       pvt->maxdimmperch = num_dimms_per_channel;
+       pvt->maxch = MAX_CHANNELS;
+       pvt->maxdimmperch = DIMMS_PER_CHANNEL;
 
        /* 'get' the pci devices we want to reserve for our use */
        if (i5400_get_devices(mci, dev_idx))
@@ -1307,13 +1326,13 @@ static int i5400_probe1(struct pci_dev *pdev, int dev_idx)
        /* Set the function pointer to an actual operation function */
        mci->edac_check = i5400_check_error;
 
-       /* initialize the MC control structure 'csrows' table
+       /* initialize the MC control structure 'dimms' table
         * with the mapping and control information */
-       if (i5400_init_csrows(mci)) {
+       if (i5400_init_dimms(mci)) {
                debugf0("MC: Setting mci->edac_cap to EDAC_FLAG_NONE\n"
-                       "    because i5400_init_csrows() returned nonzero "
+                       "    because i5400_init_dimms() returned nonzero "
                        "value\n");
-               mci->edac_cap = EDAC_FLAG_NONE; /* no csrows found */
+               mci->edac_cap = EDAC_FLAG_NONE; /* no dimms found */
        } else {
                debugf1("MC: Enable error reporting now\n");
                i5400_enable_error_reporting(mci);
index 3bafa3bca14873d0a3d22b08650c92ec3fd5e572..97c22fd650eec1953dfba7b3c773c1ddf082d700 100644 (file)
@@ -464,17 +464,14 @@ static void i7300_process_fbd_error(struct mem_ctl_info *mci)
                                FERR_FAT_FBD, error_reg);
 
                snprintf(pvt->tmp_prt_buffer, PAGE_SIZE,
-                       "FATAL (Branch=%d DRAM-Bank=%d %s "
-                       "RAS=%d CAS=%d Err=0x%lx (%s))",
-                       branch, bank,
-                       is_wr ? "RDWR" : "RD",
-                       ras, cas,
-                       errors, specific);
-
-               /* Call the helper to output message */
-               edac_mc_handle_fbd_ue(mci, rank, branch << 1,
-                                     (branch << 1) + 1,
-                                     pvt->tmp_prt_buffer);
+                        "Bank=%d RAS=%d CAS=%d Err=0x%lx (%s))",
+                        bank, ras, cas, errors, specific);
+
+               edac_mc_handle_error(HW_EVENT_ERR_FATAL, mci, 0, 0, 0,
+                                    branch, -1, rank,
+                                    is_wr ? "Write error" : "Read error",
+                                    pvt->tmp_prt_buffer, NULL);
+
        }
 
        /* read in the 1st NON-FATAL error register */
@@ -513,23 +510,14 @@ static void i7300_process_fbd_error(struct mem_ctl_info *mci)
 
                /* Form out message */
                snprintf(pvt->tmp_prt_buffer, PAGE_SIZE,
-                       "Corrected error (Branch=%d, Channel %d), "
-                       " DRAM-Bank=%d %s "
-                       "RAS=%d CAS=%d, CE Err=0x%lx, Syndrome=0x%08x(%s))",
-                       branch, channel,
-                       bank,
-                       is_wr ? "RDWR" : "RD",
-                       ras, cas,
-                       errors, syndrome, specific);
-
-               /*
-                * Call the helper to output message
-                * NOTE: Errors are reported per-branch, and not per-channel
-                *       Currently, we don't know how to identify the right
-                *       channel.
-                */
-               edac_mc_handle_fbd_ce(mci, rank, channel,
-                                     pvt->tmp_prt_buffer);
+                        "DRAM-Bank=%d RAS=%d CAS=%d, Err=0x%lx (%s))",
+                        bank, ras, cas, errors, specific);
+
+               edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 0, 0,
+                                    syndrome,
+                                    branch >> 1, channel % 2, rank,
+                                    is_wr ? "Write error" : "Read error",
+                                    pvt->tmp_prt_buffer, NULL);
        }
        return;
 }
@@ -617,8 +605,7 @@ static void i7300_enable_error_reporting(struct mem_ctl_info *mci)
 static int decode_mtr(struct i7300_pvt *pvt,
                      int slot, int ch, int branch,
                      struct i7300_dimm_info *dinfo,
-                     struct csrow_info *p_csrow,
-                     u32 *nr_pages)
+                     struct dimm_info *dimm)
 {
        int mtr, ans, addrBits, channel;
 
@@ -650,7 +637,6 @@ static int decode_mtr(struct i7300_pvt *pvt,
        addrBits -= 3;  /* 8 bits per bytes */
 
        dinfo->megabytes = 1 << addrBits;
-       *nr_pages = dinfo->megabytes << 8;
 
        debugf2("\t\tWIDTH: x%d\n", MTR_DRAM_WIDTH(mtr));
 
@@ -663,11 +649,6 @@ static int decode_mtr(struct i7300_pvt *pvt,
        debugf2("\t\tNUMCOL: %s\n", numcol_toString[MTR_DIMM_COLS(mtr)]);
        debugf2("\t\tSIZE: %d MB\n", dinfo->megabytes);
 
-       p_csrow->grain = 8;
-       p_csrow->mtype = MEM_FB_DDR2;
-       p_csrow->csrow_idx = slot;
-       p_csrow->page_mask = 0;
-
        /*
         * The type of error detection actually depends of the
         * mode of operation. When it is just one single memory chip, at
@@ -677,15 +658,18 @@ static int decode_mtr(struct i7300_pvt *pvt,
         * See datasheet Sections 7.3.6 to 7.3.8
         */
 
+       dimm->nr_pages = MiB_TO_PAGES(dinfo->megabytes);
+       dimm->grain = 8;
+       dimm->mtype = MEM_FB_DDR2;
        if (IS_SINGLE_MODE(pvt->mc_settings_a)) {
-               p_csrow->edac_mode = EDAC_SECDED;
+               dimm->edac_mode = EDAC_SECDED;
                debugf2("\t\tECC code is 8-byte-over-32-byte SECDED+ code\n");
        } else {
                debugf2("\t\tECC code is on Lockstep mode\n");
                if (MTR_DRAM_WIDTH(mtr) == 8)
-                       p_csrow->edac_mode = EDAC_S8ECD8ED;
+                       dimm->edac_mode = EDAC_S8ECD8ED;
                else
-                       p_csrow->edac_mode = EDAC_S4ECD4ED;
+                       dimm->edac_mode = EDAC_S4ECD4ED;
        }
 
        /* ask what device type on this row */
@@ -694,9 +678,9 @@ static int decode_mtr(struct i7300_pvt *pvt,
                        IS_SCRBALGO_ENHANCED(pvt->mc_settings) ?
                                            "enhanced" : "normal");
 
-               p_csrow->dtype = DEV_X8;
+               dimm->dtype = DEV_X8;
        } else
-               p_csrow->dtype = DEV_X4;
+               dimm->dtype = DEV_X4;
 
        return mtr;
 }
@@ -774,11 +758,10 @@ static int i7300_init_csrows(struct mem_ctl_info *mci)
 {
        struct i7300_pvt *pvt;
        struct i7300_dimm_info *dinfo;
-       struct csrow_info *p_csrow;
        int rc = -ENODEV;
        int mtr;
        int ch, branch, slot, channel;
-       u32 last_page = 0, nr_pages;
+       struct dimm_info *dimm;
 
        pvt = mci->pvt_info;
 
@@ -809,25 +792,23 @@ static int i7300_init_csrows(struct mem_ctl_info *mci)
                        pci_read_config_word(pvt->pci_dev_2x_0_fbd_branch[branch],
                                        where,
                                        &pvt->mtr[slot][branch]);
-                       for (ch = 0; ch < MAX_BRANCHES; ch++) {
+                       for (ch = 0; ch < MAX_CH_PER_BRANCH; ch++) {
                                int channel = to_channel(ch, branch);
 
+                               dimm = EDAC_DIMM_PTR(mci->layers, mci->dimms,
+                                              mci->n_layers, branch, ch, slot);
+
                                dinfo = &pvt->dimm_info[slot][channel];
-                               p_csrow = &mci->csrows[slot];
 
                                mtr = decode_mtr(pvt, slot, ch, branch,
-                                                dinfo, p_csrow, &nr_pages);
+                                                dinfo, dimm);
+
                                /* if no DIMMS on this row, continue */
                                if (!MTR_DIMMS_PRESENT(mtr))
                                        continue;
 
-                               /* Update per_csrow memory count */
-                               p_csrow->nr_pages += nr_pages;
-                               p_csrow->first_page = last_page;
-                               last_page += nr_pages;
-                               p_csrow->last_page = last_page;
-
                                rc = 0;
+
                        }
                }
        }
@@ -1042,10 +1023,8 @@ static int __devinit i7300_init_one(struct pci_dev *pdev,
                                    const struct pci_device_id *id)
 {
        struct mem_ctl_info *mci;
+       struct edac_mc_layer layers[3];
        struct i7300_pvt *pvt;
-       int num_channels;
-       int num_dimms_per_channel;
-       int num_csrows;
        int rc;
 
        /* wake up device */
@@ -1062,23 +1041,17 @@ static int __devinit i7300_init_one(struct pci_dev *pdev,
        if (PCI_FUNC(pdev->devfn) != 0)
                return -ENODEV;
 
-       /* As we don't have a motherboard identification routine to determine
-        * actual number of slots/dimms per channel, we thus utilize the
-        * resource as specified by the chipset. Thus, we might have
-        * have more DIMMs per channel than actually on the mobo, but this
-        * allows the driver to support up to the chipset max, without
-        * some fancy mobo determination.
-        */
-       num_dimms_per_channel = MAX_SLOTS;
-       num_channels = MAX_CHANNELS;
-       num_csrows = MAX_SLOTS * MAX_CHANNELS;
-
-       debugf0("MC: %s(): Number of - Channels= %d  DIMMS= %d  CSROWS= %d\n",
-               __func__, num_channels, num_dimms_per_channel, num_csrows);
-
        /* allocate a new MC control structure */
-       mci = edac_mc_alloc(sizeof(*pvt), num_csrows, num_channels, 0);
-
+       layers[0].type = EDAC_MC_LAYER_BRANCH;
+       layers[0].size = MAX_BRANCHES;
+       layers[0].is_virt_csrow = false;
+       layers[1].type = EDAC_MC_LAYER_CHANNEL;
+       layers[1].size = MAX_CH_PER_BRANCH;
+       layers[1].is_virt_csrow = true;
+       layers[2].type = EDAC_MC_LAYER_SLOT;
+       layers[2].size = MAX_SLOTS;
+       layers[2].is_virt_csrow = true;
+       mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers, sizeof(*pvt));
        if (mci == NULL)
                return -ENOMEM;
 
index 7f1dfcc4e597f8e75971a6785b95ab73d56cefed..d27778f65a5dc3d8f5731cc4e6c1cff7cc6f4a1c 100644 (file)
@@ -221,7 +221,9 @@ struct i7core_inject {
 };
 
 struct i7core_channel {
-       u32             ranks;
+       bool            is_3dimms_present;
+       bool            is_single_4rank;
+       bool            has_4rank;
        u32             dimms;
 };
 
@@ -257,7 +259,6 @@ struct i7core_pvt {
        struct i7core_channel   channel[NUM_CHANS];
 
        int             ce_count_available;
-       int             csrow_map[NUM_CHANS][MAX_DIMMS];
 
                        /* ECC corrected errors counts per udimm */
        unsigned long   udimm_ce_count[MAX_DIMMS];
@@ -492,116 +493,15 @@ static void free_i7core_dev(struct i7core_dev *i7core_dev)
 /****************************************************************************
                        Memory check routines
  ****************************************************************************/
-static struct pci_dev *get_pdev_slot_func(u8 socket, unsigned slot,
-                                         unsigned func)
-{
-       struct i7core_dev *i7core_dev = get_i7core_dev(socket);
-       int i;
-
-       if (!i7core_dev)
-               return NULL;
-
-       for (i = 0; i < i7core_dev->n_devs; i++) {
-               if (!i7core_dev->pdev[i])
-                       continue;
-
-               if (PCI_SLOT(i7core_dev->pdev[i]->devfn) == slot &&
-                   PCI_FUNC(i7core_dev->pdev[i]->devfn) == func) {
-                       return i7core_dev->pdev[i];
-               }
-       }
-
-       return NULL;
-}
-
-/**
- * i7core_get_active_channels() - gets the number of channels and csrows
- * @socket:    Quick Path Interconnect socket
- * @channels:  Number of channels that will be returned
- * @csrows:    Number of csrows found
- *
- * Since EDAC core needs to know in advance the number of available channels
- * and csrows, in order to allocate memory for csrows/channels, it is needed
- * to run two similar steps. At the first step, implemented on this function,
- * it checks the number of csrows/channels present at one socket.
- * this is used in order to properly allocate the size of mci components.
- *
- * It should be noticed that none of the current available datasheets explain
- * or even mention how csrows are seen by the memory controller. So, we need
- * to add a fake description for csrows.
- * So, this driver is attributing one DIMM memory for one csrow.
- */
-static int i7core_get_active_channels(const u8 socket, unsigned *channels,
-                                     unsigned *csrows)
-{
-       struct pci_dev *pdev = NULL;
-       int i, j;
-       u32 status, control;
-
-       *channels = 0;
-       *csrows = 0;
-
-       pdev = get_pdev_slot_func(socket, 3, 0);
-       if (!pdev) {
-               i7core_printk(KERN_ERR, "Couldn't find socket %d fn 3.0!!!\n",
-                             socket);
-               return -ENODEV;
-       }
-
-       /* Device 3 function 0 reads */
-       pci_read_config_dword(pdev, MC_STATUS, &status);
-       pci_read_config_dword(pdev, MC_CONTROL, &control);
-
-       for (i = 0; i < NUM_CHANS; i++) {
-               u32 dimm_dod[3];
-               /* Check if the channel is active */
-               if (!(control & (1 << (8 + i))))
-                       continue;
-
-               /* Check if the channel is disabled */
-               if (status & (1 << i))
-                       continue;
-
-               pdev = get_pdev_slot_func(socket, i + 4, 1);
-               if (!pdev) {
-                       i7core_printk(KERN_ERR, "Couldn't find socket %d "
-                                               "fn %d.%d!!!\n",
-                                               socket, i + 4, 1);
-                       return -ENODEV;
-               }
-               /* Devices 4-6 function 1 */
-               pci_read_config_dword(pdev,
-                               MC_DOD_CH_DIMM0, &dimm_dod[0]);
-               pci_read_config_dword(pdev,
-                               MC_DOD_CH_DIMM1, &dimm_dod[1]);
-               pci_read_config_dword(pdev,
-                               MC_DOD_CH_DIMM2, &dimm_dod[2]);
 
-               (*channels)++;
-
-               for (j = 0; j < 3; j++) {
-                       if (!DIMM_PRESENT(dimm_dod[j]))
-                               continue;
-                       (*csrows)++;
-               }
-       }
-
-       debugf0("Number of active channels on socket %d: %d\n",
-               socket, *channels);
-
-       return 0;
-}
-
-static int get_dimm_config(const struct mem_ctl_info *mci)
+static int get_dimm_config(struct mem_ctl_info *mci)
 {
        struct i7core_pvt *pvt = mci->pvt_info;
-       struct csrow_info *csr;
        struct pci_dev *pdev;
        int i, j;
-       int csrow = 0;
-       unsigned long last_page = 0;
        enum edac_type mode;
        enum mem_type mtype;
+       struct dimm_info *dimm;
 
        /* Get data from the MC register, function 0 */
        pdev = pvt->pci_mcr[0];
@@ -657,21 +557,20 @@ static int get_dimm_config(const struct mem_ctl_info *mci)
                pci_read_config_dword(pvt->pci_ch[i][0],
                                MC_CHANNEL_DIMM_INIT_PARAMS, &data);
 
-               pvt->channel[i].ranks = (data & QUAD_RANK_PRESENT) ?
-                                               4 : 2;
+
+               if (data & THREE_DIMMS_PRESENT)
+                       pvt->channel[i].is_3dimms_present = true;
+
+               if (data & SINGLE_QUAD_RANK_PRESENT)
+                       pvt->channel[i].is_single_4rank = true;
+
+               if (data & QUAD_RANK_PRESENT)
+                       pvt->channel[i].has_4rank = true;
 
                if (data & REGISTERED_DIMM)
                        mtype = MEM_RDDR3;
                else
                        mtype = MEM_DDR3;
-#if 0
-               if (data & THREE_DIMMS_PRESENT)
-                       pvt->channel[i].dimms = 3;
-               else if (data & SINGLE_QUAD_RANK_PRESENT)
-                       pvt->channel[i].dimms = 1;
-               else
-                       pvt->channel[i].dimms = 2;
-#endif
 
                /* Devices 4-6 function 1 */
                pci_read_config_dword(pvt->pci_ch[i][1],
@@ -682,11 +581,13 @@ static int get_dimm_config(const struct mem_ctl_info *mci)
                                MC_DOD_CH_DIMM2, &dimm_dod[2]);
 
                debugf0("Ch%d phy rd%d, wr%d (0x%08x): "
-                       "%d ranks, %cDIMMs\n",
+                       "%s%s%s%cDIMMs\n",
                        i,
                        RDLCH(pvt->info.ch_map, i), WRLCH(pvt->info.ch_map, i),
                        data,
-                       pvt->channel[i].ranks,
+                       pvt->channel[i].is_3dimms_present ? "3DIMMS " : "",
+                       pvt->channel[i].is_3dimms_present ? "SINGLE_4R " : "",
+                       pvt->channel[i].has_4rank ? "HAS_4R " : "",
                        (data & REGISTERED_DIMM) ? 'R' : 'U');
 
                for (j = 0; j < 3; j++) {
@@ -696,6 +597,8 @@ static int get_dimm_config(const struct mem_ctl_info *mci)
                        if (!DIMM_PRESENT(dimm_dod[j]))
                                continue;
 
+                       dimm = EDAC_DIMM_PTR(mci->layers, mci->dimms, mci->n_layers,
+                                      i, j, 0);
                        banks = numbank(MC_DOD_NUMBANK(dimm_dod[j]));
                        ranks = numrank(MC_DOD_NUMRANK(dimm_dod[j]));
                        rows = numrow(MC_DOD_NUMROW(dimm_dod[j]));
@@ -704,8 +607,6 @@ static int get_dimm_config(const struct mem_ctl_info *mci)
                        /* DDR3 has 8 I/O banks */
                        size = (rows * cols * banks * ranks) >> (20 - 3);
 
-                       pvt->channel[i].dimms++;
-
                        debugf0("\tdimm %d %d Mb offset: %x, "
                                "bank: %d, rank: %d, row: %#x, col: %#x\n",
                                j, size,
@@ -714,44 +615,28 @@ static int get_dimm_config(const struct mem_ctl_info *mci)
 
                        npages = MiB_TO_PAGES(size);
 
-                       csr = &mci->csrows[csrow];
-                       csr->first_page = last_page + 1;
-                       last_page += npages;
-                       csr->last_page = last_page;
-                       csr->nr_pages = npages;
-
-                       csr->page_mask = 0;
-                       csr->grain = 8;
-                       csr->csrow_idx = csrow;
-                       csr->nr_channels = 1;
-
-                       csr->channels[0].chan_idx = i;
-                       csr->channels[0].ce_count = 0;
-
-                       pvt->csrow_map[i][j] = csrow;
+                       dimm->nr_pages = npages;
 
                        switch (banks) {
                        case 4:
-                               csr->dtype = DEV_X4;
+                               dimm->dtype = DEV_X4;
                                break;
                        case 8:
-                               csr->dtype = DEV_X8;
+                               dimm->dtype = DEV_X8;
                                break;
                        case 16:
-                               csr->dtype = DEV_X16;
+                               dimm->dtype = DEV_X16;
                                break;
                        default:
-                               csr->dtype = DEV_UNKNOWN;
+                               dimm->dtype = DEV_UNKNOWN;
                        }
 
-                       csr->edac_mode = mode;
-                       csr->mtype = mtype;
-                       snprintf(csr->channels[0].label,
-                                       sizeof(csr->channels[0].label),
-                                       "CPU#%uChannel#%u_DIMM#%u",
-                                       pvt->i7core_dev->socket, i, j);
-
-                       csrow++;
+                       snprintf(dimm->label, sizeof(dimm->label),
+                                "CPU#%uChannel#%u_DIMM#%u",
+                                pvt->i7core_dev->socket, i, j);
+                       dimm->grain = 8;
+                       dimm->edac_mode = mode;
+                       dimm->mtype = mtype;
                }
 
                pci_read_config_dword(pdev, MC_SAG_CH_0, &value[0]);
@@ -1567,22 +1452,16 @@ error:
 /****************************************************************************
                        Error check routines
  ****************************************************************************/
-static void i7core_rdimm_update_csrow(struct mem_ctl_info *mci,
+static void i7core_rdimm_update_errcount(struct mem_ctl_info *mci,
                                      const int chan,
                                      const int dimm,
                                      const int add)
 {
-       char *msg;
-       struct i7core_pvt *pvt = mci->pvt_info;
-       int row = pvt->csrow_map[chan][dimm], i;
+       int i;
 
        for (i = 0; i < add; i++) {
-               msg = kasprintf(GFP_KERNEL, "Corrected error "
-                               "(Socket=%d channel=%d dimm=%d)",
-                               pvt->i7core_dev->socket, chan, dimm);
-
-               edac_mc_handle_fbd_ce(mci, row, 0, msg);
-               kfree (msg);
+               edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 0, 0, 0,
+                                    chan, dimm, -1, "error", "", NULL);
        }
 }
 
@@ -1623,11 +1502,11 @@ static void i7core_rdimm_update_ce_count(struct mem_ctl_info *mci,
 
        /*updated the edac core */
        if (add0 != 0)
-               i7core_rdimm_update_csrow(mci, chan, 0, add0);
+               i7core_rdimm_update_errcount(mci, chan, 0, add0);
        if (add1 != 0)
-               i7core_rdimm_update_csrow(mci, chan, 1, add1);
+               i7core_rdimm_update_errcount(mci, chan, 1, add1);
        if (add2 != 0)
-               i7core_rdimm_update_csrow(mci, chan, 2, add2);
+               i7core_rdimm_update_errcount(mci, chan, 2, add2);
 
 }
 
@@ -1747,20 +1626,30 @@ static void i7core_mce_output_error(struct mem_ctl_info *mci,
                                    const struct mce *m)
 {
        struct i7core_pvt *pvt = mci->pvt_info;
-       char *type, *optype, *err, *msg;
+       char *type, *optype, *err, msg[80];
+       enum hw_event_mc_err_type tp_event;
        unsigned long error = m->status & 0x1ff0000l;
+       bool uncorrected_error = m->mcgstatus & 1ll << 61;
+       bool ripv = m->mcgstatus & 1;
        u32 optypenum = (m->status >> 4) & 0x07;
        u32 core_err_cnt = (m->status >> 38) & 0x7fff;
        u32 dimm = (m->misc >> 16) & 0x3;
        u32 channel = (m->misc >> 18) & 0x3;
        u32 syndrome = m->misc >> 32;
        u32 errnum = find_first_bit(&error, 32);
-       int csrow;
 
-       if (m->mcgstatus & 1)
-               type = "FATAL";
-       else
-               type = "NON_FATAL";
+       if (uncorrected_error) {
+               if (ripv) {
+                       type = "FATAL";
+                       tp_event = HW_EVENT_ERR_FATAL;
+               } else {
+                       type = "NON_FATAL";
+                       tp_event = HW_EVENT_ERR_UNCORRECTED;
+               }
+       } else {
+               type = "CORRECTED";
+               tp_event = HW_EVENT_ERR_CORRECTED;
+       }
 
        switch (optypenum) {
        case 0:
@@ -1815,27 +1704,20 @@ static void i7core_mce_output_error(struct mem_ctl_info *mci,
                err = "unknown";
        }
 
-       /* FIXME: should convert addr into bank and rank information */
-       msg = kasprintf(GFP_ATOMIC,
-               "%s (addr = 0x%08llx, cpu=%d, Dimm=%d, Channel=%d, "
-               "syndrome=0x%08x, count=%d, Err=%08llx:%08llx (%s: %s))\n",
-               type, (long long) m->addr, m->cpu, dimm, channel,
-               syndrome, core_err_cnt, (long long)m->status,
-               (long long)m->misc, optype, err);
-
-       debugf0("%s", msg);
-
-       csrow = pvt->csrow_map[channel][dimm];
+       snprintf(msg, sizeof(msg), "count=%d %s", core_err_cnt, optype);
 
-       /* Call the helper to output message */
-       if (m->mcgstatus & 1)
-               edac_mc_handle_fbd_ue(mci, csrow, 0,
-                               0 /* FIXME: should be channel here */, msg);
-       else if (!pvt->is_registered)
-               edac_mc_handle_fbd_ce(mci, csrow,
-                               0 /* FIXME: should be channel here */, msg);
-
-       kfree(msg);
+       /*
+        * Call the helper to output message
+        * FIXME: what to do if core_err_cnt > 1? Currently, it generates
+        * only one event
+        */
+       if (uncorrected_error || !pvt->is_registered)
+               edac_mc_handle_error(tp_event, mci,
+                                    m->addr >> PAGE_SHIFT,
+                                    m->addr & ~PAGE_MASK,
+                                    syndrome,
+                                    channel, dimm, -1,
+                                    err, msg, m);
 }
 
 /*
@@ -2252,15 +2134,19 @@ static int i7core_register_mci(struct i7core_dev *i7core_dev)
 {
        struct mem_ctl_info *mci;
        struct i7core_pvt *pvt;
-       int rc, channels, csrows;
-
-       /* Check the number of active and not disabled channels */
-       rc = i7core_get_active_channels(i7core_dev->socket, &channels, &csrows);
-       if (unlikely(rc < 0))
-               return rc;
+       int rc;
+       struct edac_mc_layer layers[2];
 
        /* allocate a new MC control structure */
-       mci = edac_mc_alloc(sizeof(*pvt), csrows, channels, i7core_dev->socket);
+
+       layers[0].type = EDAC_MC_LAYER_CHANNEL;
+       layers[0].size = NUM_CHANS;
+       layers[0].is_virt_csrow = false;
+       layers[1].type = EDAC_MC_LAYER_SLOT;
+       layers[1].size = MAX_DIMMS;
+       layers[1].is_virt_csrow = true;
+       mci = edac_mc_alloc(i7core_dev->socket, ARRAY_SIZE(layers), layers,
+                           sizeof(*pvt));
        if (unlikely(!mci))
                return -ENOMEM;
 
index 3bf2b2f490e7d98ad16374acf4c5e209b32de6cb..52072c28a8a652466f31ed8be165b591098143ca 100644 (file)
@@ -12,7 +12,7 @@
  * 440GX fix by Jason Uhlenkott <juhlenko@akamai.com>.
  *
  * Written with reference to 82443BX Host Bridge Datasheet:
- * http://download.intel.com/design/chipsets/datashts/29063301.pdf 
+ * http://download.intel.com/design/chipsets/datashts/29063301.pdf
  * references to this document given in [].
  *
  * This module doesn't support the 440LX, but it may be possible to
@@ -156,19 +156,19 @@ static int i82443bxgx_edacmc_process_error_info(struct mem_ctl_info *mci,
        if (info->eap & I82443BXGX_EAP_OFFSET_SBE) {
                error_found = 1;
                if (handle_errors)
-                       edac_mc_handle_ce(mci, page, pageoffset,
-                               /* 440BX/GX don't make syndrome information
-                                * available */
-                               0, edac_mc_find_csrow_by_page(mci, page), 0,
-                               mci->ctl_name);
+                       edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
+                                            page, pageoffset, 0,
+                                            edac_mc_find_csrow_by_page(mci, page),
+                                            0, -1, mci->ctl_name, "", NULL);
        }
 
        if (info->eap & I82443BXGX_EAP_OFFSET_MBE) {
                error_found = 1;
                if (handle_errors)
-                       edac_mc_handle_ue(mci, page, pageoffset,
-                                       edac_mc_find_csrow_by_page(mci, page),
-                                       mci->ctl_name);
+                       edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
+                                            page, pageoffset, 0,
+                                            edac_mc_find_csrow_by_page(mci, page),
+                                            0, -1, mci->ctl_name, "", NULL);
        }
 
        return error_found;
@@ -189,6 +189,7 @@ static void i82443bxgx_init_csrows(struct mem_ctl_info *mci,
                                enum mem_type mtype)
 {
        struct csrow_info *csrow;
+       struct dimm_info *dimm;
        int index;
        u8 drbar, dramc;
        u32 row_base, row_high_limit, row_high_limit_last;
@@ -197,6 +198,8 @@ static void i82443bxgx_init_csrows(struct mem_ctl_info *mci,
        row_high_limit_last = 0;
        for (index = 0; index < mci->nr_csrows; index++) {
                csrow = &mci->csrows[index];
+               dimm = csrow->channels[0].dimm;
+
                pci_read_config_byte(pdev, I82443BXGX_DRB + index, &drbar);
                debugf1("MC%d: %s: %s() Row=%d DRB = %#0x\n",
                        mci->mc_idx, __FILE__, __func__, index, drbar);
@@ -217,14 +220,14 @@ static void i82443bxgx_init_csrows(struct mem_ctl_info *mci,
                row_base = row_high_limit_last;
                csrow->first_page = row_base >> PAGE_SHIFT;
                csrow->last_page = (row_high_limit >> PAGE_SHIFT) - 1;
-               csrow->nr_pages = csrow->last_page - csrow->first_page + 1;
+               dimm->nr_pages = csrow->last_page - csrow->first_page + 1;
                /* EAP reports in 4kilobyte granularity [61] */
-               csrow->grain = 1 << 12;
-               csrow->mtype = mtype;
+               dimm->grain = 1 << 12;
+               dimm->mtype = mtype;
                /* I don't think 440BX can tell you device type? FIXME? */
-               csrow->dtype = DEV_UNKNOWN;
+               dimm->dtype = DEV_UNKNOWN;
                /* Mode is global to all rows on 440BX */
-               csrow->edac_mode = edac_mode;
+               dimm->edac_mode = edac_mode;
                row_high_limit_last = row_high_limit;
        }
 }
@@ -232,6 +235,7 @@ static void i82443bxgx_init_csrows(struct mem_ctl_info *mci,
 static int i82443bxgx_edacmc_probe1(struct pci_dev *pdev, int dev_idx)
 {
        struct mem_ctl_info *mci;
+       struct edac_mc_layer layers[2];
        u8 dramc;
        u32 nbxcfg, ecc_mode;
        enum mem_type mtype;
@@ -245,8 +249,13 @@ static int i82443bxgx_edacmc_probe1(struct pci_dev *pdev, int dev_idx)
        if (pci_read_config_dword(pdev, I82443BXGX_NBXCFG, &nbxcfg))
                return -EIO;
 
-       mci = edac_mc_alloc(0, I82443BXGX_NR_CSROWS, I82443BXGX_NR_CHANS, 0);
-
+       layers[0].type = EDAC_MC_LAYER_CHIP_SELECT;
+       layers[0].size = I82443BXGX_NR_CSROWS;
+       layers[0].is_virt_csrow = true;
+       layers[1].type = EDAC_MC_LAYER_CHANNEL;
+       layers[1].size = I82443BXGX_NR_CHANS;
+       layers[1].is_virt_csrow = false;
+       mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers, 0);
        if (mci == NULL)
                return -ENOMEM;
 
index c779092d18d1cf823350faf64890c2e0c94d9736..08045059d10bcc64a06318c9fcdd7826e7ea83fc 100644 (file)
@@ -99,6 +99,7 @@ static int i82860_process_error_info(struct mem_ctl_info *mci,
                                struct i82860_error_info *info,
                                int handle_errors)
 {
+       struct dimm_info *dimm;
        int row;
 
        if (!(info->errsts2 & 0x0003))
@@ -108,18 +109,25 @@ static int i82860_process_error_info(struct mem_ctl_info *mci,
                return 1;
 
        if ((info->errsts ^ info->errsts2) & 0x0003) {
-               edac_mc_handle_ce_no_info(mci, "UE overwrote CE");
+               edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 0, 0, 0,
+                                    -1, -1, -1, "UE overwrote CE", "", NULL);
                info->errsts = info->errsts2;
        }
 
        info->eap >>= PAGE_SHIFT;
        row = edac_mc_find_csrow_by_page(mci, info->eap);
+       dimm = mci->csrows[row].channels[0].dimm;
 
        if (info->errsts & 0x0002)
-               edac_mc_handle_ue(mci, info->eap, 0, row, "i82860 UE");
+               edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
+                                    info->eap, 0, 0,
+                                    dimm->location[0], dimm->location[1], -1,
+                                    "i82860 UE", "", NULL);
        else
-               edac_mc_handle_ce(mci, info->eap, 0, info->derrsyn, row, 0,
-                               "i82860 UE");
+               edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
+                                    info->eap, 0, info->derrsyn,
+                                    dimm->location[0], dimm->location[1], -1,
+                                    "i82860 CE", "", NULL);
 
        return 1;
 }
@@ -140,6 +148,7 @@ static void i82860_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev)
        u16 value;
        u32 cumul_size;
        struct csrow_info *csrow;
+       struct dimm_info *dimm;
        int index;
 
        pci_read_config_word(pdev, I82860_MCHCFG, &mchcfg_ddim);
@@ -153,6 +162,8 @@ static void i82860_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev)
         */
        for (index = 0; index < mci->nr_csrows; index++) {
                csrow = &mci->csrows[index];
+               dimm = csrow->channels[0].dimm;
+
                pci_read_config_word(pdev, I82860_GBA + index * 2, &value);
                cumul_size = (value & I82860_GBA_MASK) <<
                        (I82860_GBA_SHIFT - PAGE_SHIFT);
@@ -164,30 +175,38 @@ static void i82860_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev)
 
                csrow->first_page = last_cumul_size;
                csrow->last_page = cumul_size - 1;
-               csrow->nr_pages = cumul_size - last_cumul_size;
+               dimm->nr_pages = cumul_size - last_cumul_size;
                last_cumul_size = cumul_size;
-               csrow->grain = 1 << 12; /* I82860_EAP has 4KiB reolution */
-               csrow->mtype = MEM_RMBS;
-               csrow->dtype = DEV_UNKNOWN;
-               csrow->edac_mode = mchcfg_ddim ? EDAC_SECDED : EDAC_NONE;
+               dimm->grain = 1 << 12;  /* I82860_EAP has 4KiB reolution */
+               dimm->mtype = MEM_RMBS;
+               dimm->dtype = DEV_UNKNOWN;
+               dimm->edac_mode = mchcfg_ddim ? EDAC_SECDED : EDAC_NONE;
        }
 }
 
 static int i82860_probe1(struct pci_dev *pdev, int dev_idx)
 {
        struct mem_ctl_info *mci;
+       struct edac_mc_layer layers[2];
        struct i82860_error_info discard;
 
-       /* RDRAM has channels but these don't map onto the abstractions that
-          edac uses.
-          The device groups from the GRA registers seem to map reasonably
-          well onto the notion of a chip select row.
-          There are 16 GRA registers and since the name is associated with
-          the channel and the GRA registers map to physical devices so we are
-          going to make 1 channel for group.
+       /*
+        * RDRAM has channels but these don't map onto the csrow abstraction.
+        * According with the datasheet, there are 2 Rambus channels, supporting
+        * up to 16 direct RDRAM devices.
+        * The device groups from the GRA registers seem to map reasonably
+        * well onto the notion of a chip select row.
+        * There are 16 GRA registers and since the name is associated with
+        * the channel and the GRA registers map to physical devices so we are
+        * going to make 1 channel for group.
         */
-       mci = edac_mc_alloc(0, 16, 1, 0);
-
+       layers[0].type = EDAC_MC_LAYER_CHANNEL;
+       layers[0].size = 2;
+       layers[0].is_virt_csrow = true;
+       layers[1].type = EDAC_MC_LAYER_SLOT;
+       layers[1].size = 8;
+       layers[1].is_virt_csrow = true;
+       mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers, 0);
        if (!mci)
                return -ENOMEM;
 
index 10f15d85fb5eee63659c8ba5667826d25a5e2a66..b613e31c16e5de18f47a8a335690cfc4c487a752 100644 (file)
@@ -38,7 +38,8 @@
 #endif                         /* PCI_DEVICE_ID_INTEL_82875_6 */
 
 /* four csrows in dual channel, eight in single channel */
-#define I82875P_NR_CSROWS(nr_chans) (8/(nr_chans))
+#define I82875P_NR_DIMMS               8
+#define I82875P_NR_CSROWS(nr_chans)    (I82875P_NR_DIMMS / (nr_chans))
 
 /* Intel 82875p register addresses - device 0 function 0 - DRAM Controller */
 #define I82875P_EAP            0x58    /* Error Address Pointer (32b)
@@ -235,7 +236,9 @@ static int i82875p_process_error_info(struct mem_ctl_info *mci,
                return 1;
 
        if ((info->errsts ^ info->errsts2) & 0x0081) {
-               edac_mc_handle_ce_no_info(mci, "UE overwrote CE");
+               edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 0, 0, 0,
+                                    -1, -1, -1,
+                                    "UE overwrote CE", "", NULL);
                info->errsts = info->errsts2;
        }
 
@@ -243,11 +246,15 @@ static int i82875p_process_error_info(struct mem_ctl_info *mci,
        row = edac_mc_find_csrow_by_page(mci, info->eap);
 
        if (info->errsts & 0x0080)
-               edac_mc_handle_ue(mci, info->eap, 0, row, "i82875p UE");
+               edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
+                                    info->eap, 0, 0,
+                                    row, -1, -1,
+                                    "i82875p UE", "", NULL);
        else
-               edac_mc_handle_ce(mci, info->eap, 0, info->derrsyn, row,
-                               multi_chan ? (info->des & 0x1) : 0,
-                               "i82875p CE");
+               edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
+                                    info->eap, 0, info->derrsyn,
+                                    row, multi_chan ? (info->des & 0x1) : 0,
+                                    -1, "i82875p CE", "", NULL);
 
        return 1;
 }
@@ -342,11 +349,13 @@ static void i82875p_init_csrows(struct mem_ctl_info *mci,
                                void __iomem * ovrfl_window, u32 drc)
 {
        struct csrow_info *csrow;
+       struct dimm_info *dimm;
+       unsigned nr_chans = dual_channel_active(drc) + 1;
        unsigned long last_cumul_size;
        u8 value;
        u32 drc_ddim;           /* DRAM Data Integrity Mode 0=none,2=edac */
-       u32 cumul_size;
-       int index;
+       u32 cumul_size, nr_pages;
+       int index, j;
 
        drc_ddim = (drc >> 18) & 0x1;
        last_cumul_size = 0;
@@ -369,12 +378,18 @@ static void i82875p_init_csrows(struct mem_ctl_info *mci,
 
                csrow->first_page = last_cumul_size;
                csrow->last_page = cumul_size - 1;
-               csrow->nr_pages = cumul_size - last_cumul_size;
+               nr_pages = cumul_size - last_cumul_size;
                last_cumul_size = cumul_size;
-               csrow->grain = 1 << 12; /* I82875P_EAP has 4KiB reolution */
-               csrow->mtype = MEM_DDR;
-               csrow->dtype = DEV_UNKNOWN;
-               csrow->edac_mode = drc_ddim ? EDAC_SECDED : EDAC_NONE;
+
+               for (j = 0; j < nr_chans; j++) {
+                       dimm = csrow->channels[j].dimm;
+
+                       dimm->nr_pages = nr_pages / nr_chans;
+                       dimm->grain = 1 << 12;  /* I82875P_EAP has 4KiB reolution */
+                       dimm->mtype = MEM_DDR;
+                       dimm->dtype = DEV_UNKNOWN;
+                       dimm->edac_mode = drc_ddim ? EDAC_SECDED : EDAC_NONE;
+               }
        }
 }
 
@@ -382,6 +397,7 @@ static int i82875p_probe1(struct pci_dev *pdev, int dev_idx)
 {
        int rc = -ENODEV;
        struct mem_ctl_info *mci;
+       struct edac_mc_layer layers[2];
        struct i82875p_pvt *pvt;
        struct pci_dev *ovrfl_pdev;
        void __iomem *ovrfl_window;
@@ -397,9 +413,14 @@ static int i82875p_probe1(struct pci_dev *pdev, int dev_idx)
                return -ENODEV;
        drc = readl(ovrfl_window + I82875P_DRC);
        nr_chans = dual_channel_active(drc) + 1;
-       mci = edac_mc_alloc(sizeof(*pvt), I82875P_NR_CSROWS(nr_chans),
-                       nr_chans, 0);
 
+       layers[0].type = EDAC_MC_LAYER_CHIP_SELECT;
+       layers[0].size = I82875P_NR_CSROWS(nr_chans);
+       layers[0].is_virt_csrow = true;
+       layers[1].type = EDAC_MC_LAYER_CHANNEL;
+       layers[1].size = nr_chans;
+       layers[1].is_virt_csrow = false;
+       mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers, sizeof(*pvt));
        if (!mci) {
                rc = -ENOMEM;
                goto fail0;
index 0cd8368f88f8c3e634da1e09f2d2df43b67ef1f1..433332c7cdbabe3bf54fbce0e9622403af29869c 100644 (file)
@@ -29,7 +29,8 @@
 #define PCI_DEVICE_ID_INTEL_82975_0    0x277c
 #endif                         /* PCI_DEVICE_ID_INTEL_82975_0 */
 
-#define I82975X_NR_CSROWS(nr_chans)            (8/(nr_chans))
+#define I82975X_NR_DIMMS               8
+#define I82975X_NR_CSROWS(nr_chans)    (I82975X_NR_DIMMS / (nr_chans))
 
 /* Intel 82975X register addresses - device 0 function 0 - DRAM Controller */
 #define I82975X_EAP            0x58    /* Dram Error Address Pointer (32b)
@@ -287,7 +288,8 @@ static int i82975x_process_error_info(struct mem_ctl_info *mci,
                return 1;
 
        if ((info->errsts ^ info->errsts2) & 0x0003) {
-               edac_mc_handle_ce_no_info(mci, "UE overwrote CE");
+               edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 0, 0, 0,
+                                    -1, -1, -1, "UE overwrote CE", "", NULL);
                info->errsts = info->errsts2;
        }
 
@@ -309,13 +311,18 @@ static int i82975x_process_error_info(struct mem_ctl_info *mci,
        chan = (mci->csrows[row].nr_channels == 1) ? 0 : info->eap & 1;
        offst = info->eap
                        & ((1 << PAGE_SHIFT) -
-                               (1 << mci->csrows[row].grain));
+                          (1 << mci->csrows[row].channels[chan].dimm->grain));
 
        if (info->errsts & 0x0002)
-               edac_mc_handle_ue(mci, page, offst , row, "i82975x UE");
+               edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
+                                    page, offst, 0,
+                                    row, -1, -1,
+                                    "i82975x UE", "", NULL);
        else
-               edac_mc_handle_ce(mci, page, offst, info->derrsyn, row,
-                               chan, "i82975x CE");
+               edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
+                                    page, offst, info->derrsyn,
+                                    row, chan ? chan : 0, -1,
+                                    "i82975x CE", "", NULL);
 
        return 1;
 }
@@ -370,8 +377,10 @@ static void i82975x_init_csrows(struct mem_ctl_info *mci,
        struct csrow_info *csrow;
        unsigned long last_cumul_size;
        u8 value;
-       u32 cumul_size;
+       u32 cumul_size, nr_pages;
        int index, chan;
+       struct dimm_info *dimm;
+       enum dev_type dtype;
 
        last_cumul_size = 0;
 
@@ -400,28 +409,33 @@ static void i82975x_init_csrows(struct mem_ctl_info *mci,
                debugf3("%s(): (%d) cumul_size 0x%x\n", __func__, index,
                        cumul_size);
 
+               nr_pages = cumul_size - last_cumul_size;
+               if (!nr_pages)
+                       continue;
+
                /*
                 * Initialise dram labels
                 * index values:
                 *   [0-7] for single-channel; i.e. csrow->nr_channels = 1
                 *   [0-3] for dual-channel; i.e. csrow->nr_channels = 2
                 */
-               for (chan = 0; chan < csrow->nr_channels; chan++)
-                       strncpy(csrow->channels[chan].label,
+               dtype = i82975x_dram_type(mch_window, index);
+               for (chan = 0; chan < csrow->nr_channels; chan++) {
+                       dimm = mci->csrows[index].channels[chan].dimm;
+
+                       dimm->nr_pages = nr_pages / csrow->nr_channels;
+                       strncpy(csrow->channels[chan].dimm->label,
                                        labels[(index >> 1) + (chan * 2)],
                                        EDAC_MC_LABEL_LEN);
-
-               if (cumul_size == last_cumul_size)
-                       continue;       /* not populated */
+                       dimm->grain = 1 << 7;   /* 128Byte cache-line resolution */
+                       dimm->dtype = i82975x_dram_type(mch_window, index);
+                       dimm->mtype = MEM_DDR2; /* I82975x supports only DDR2 */
+                       dimm->edac_mode = EDAC_SECDED; /* only supported */
+               }
 
                csrow->first_page = last_cumul_size;
                csrow->last_page = cumul_size - 1;
-               csrow->nr_pages = cumul_size - last_cumul_size;
                last_cumul_size = cumul_size;
-               csrow->grain = 1 << 7;  /* 128Byte cache-line resolution */
-               csrow->mtype = MEM_DDR2; /* I82975x supports only DDR2 */
-               csrow->dtype = i82975x_dram_type(mch_window, index);
-               csrow->edac_mode = EDAC_SECDED; /* only supported */
        }
 }
 
@@ -463,6 +477,7 @@ static int i82975x_probe1(struct pci_dev *pdev, int dev_idx)
 {
        int rc = -ENODEV;
        struct mem_ctl_info *mci;
+       struct edac_mc_layer layers[2];
        struct i82975x_pvt *pvt;
        void __iomem *mch_window;
        u32 mchbar;
@@ -531,8 +546,13 @@ static int i82975x_probe1(struct pci_dev *pdev, int dev_idx)
        chans = dual_channel_active(mch_window) + 1;
 
        /* assuming only one controller, index thus is 0 */
-       mci = edac_mc_alloc(sizeof(*pvt), I82975X_NR_CSROWS(chans),
-                                       chans, 0);
+       layers[0].type = EDAC_MC_LAYER_CHIP_SELECT;
+       layers[0].size = I82975X_NR_DIMMS;
+       layers[0].is_virt_csrow = true;
+       layers[1].type = EDAC_MC_LAYER_CHANNEL;
+       layers[1].size = I82975X_NR_CSROWS(chans);
+       layers[1].is_virt_csrow = false;
+       mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers, sizeof(*pvt));
        if (!mci) {
                rc = -ENOMEM;
                goto fail1;
index c6074c5cd1ef49bd492d2e94793686a2710d694f..8c87a5e870577c06b0082ca7d3b19eb7a81a252b 100644 (file)
@@ -5,8 +5,6 @@
 
 #include <asm/mce.h>
 
-#define BIT_64(n)                      (U64_C(1) << (n))
-
 #define EC(x)                          ((x) & 0xffff)
 #define XEC(x, mask)                   (((x) >> 16) & mask)
 
index 73464a62adf74ae1483a16ad71847c03fc03169f..4c402353ba98d9aeceb5bbd8062eeec916f39d9d 100644 (file)
@@ -854,12 +854,16 @@ static void mpc85xx_mc_check(struct mem_ctl_info *mci)
                mpc85xx_mc_printk(mci, KERN_ERR, "PFN out of range!\n");
 
        if (err_detect & DDR_EDE_SBE)
-               edac_mc_handle_ce(mci, pfn, err_addr & ~PAGE_MASK,
-                                 syndrome, row_index, 0, mci->ctl_name);
+               edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
+                                    pfn, err_addr & ~PAGE_MASK, syndrome,
+                                    row_index, 0, -1,
+                                    mci->ctl_name, "", NULL);
 
        if (err_detect & DDR_EDE_MBE)
-               edac_mc_handle_ue(mci, pfn, err_addr & ~PAGE_MASK,
-                                 row_index, mci->ctl_name);
+               edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
+                                    pfn, err_addr & ~PAGE_MASK, syndrome,
+                                    row_index, 0, -1,
+                                    mci->ctl_name, "", NULL);
 
        out_be32(pdata->mc_vbase + MPC85XX_MC_ERR_DETECT, err_detect);
 }
@@ -883,6 +887,7 @@ static void __devinit mpc85xx_init_csrows(struct mem_ctl_info *mci)
 {
        struct mpc85xx_mc_pdata *pdata = mci->pvt_info;
        struct csrow_info *csrow;
+       struct dimm_info *dimm;
        u32 sdram_ctl;
        u32 sdtype;
        enum mem_type mtype;
@@ -929,6 +934,8 @@ static void __devinit mpc85xx_init_csrows(struct mem_ctl_info *mci)
                u32 end;
 
                csrow = &mci->csrows[index];
+               dimm = csrow->channels[0].dimm;
+
                cs_bnds = in_be32(pdata->mc_vbase + MPC85XX_MC_CS_BNDS_0 +
                                  (index * MPC85XX_MC_CS_BNDS_OFS));
 
@@ -944,19 +951,21 @@ static void __devinit mpc85xx_init_csrows(struct mem_ctl_info *mci)
 
                csrow->first_page = start;
                csrow->last_page = end;
-               csrow->nr_pages = end + 1 - start;
-               csrow->grain = 8;
-               csrow->mtype = mtype;
-               csrow->dtype = DEV_UNKNOWN;
+
+               dimm->nr_pages = end + 1 - start;
+               dimm->grain = 8;
+               dimm->mtype = mtype;
+               dimm->dtype = DEV_UNKNOWN;
                if (sdram_ctl & DSC_X32_EN)
-                       csrow->dtype = DEV_X32;
-               csrow->edac_mode = EDAC_SECDED;
+                       dimm->dtype = DEV_X32;
+               dimm->edac_mode = EDAC_SECDED;
        }
 }
 
 static int __devinit mpc85xx_mc_err_probe(struct platform_device *op)
 {
        struct mem_ctl_info *mci;
+       struct edac_mc_layer layers[2];
        struct mpc85xx_mc_pdata *pdata;
        struct resource r;
        u32 sdram_ctl;
@@ -965,7 +974,13 @@ static int __devinit mpc85xx_mc_err_probe(struct platform_device *op)
        if (!devres_open_group(&op->dev, mpc85xx_mc_err_probe, GFP_KERNEL))
                return -ENOMEM;
 
-       mci = edac_mc_alloc(sizeof(*pdata), 4, 1, edac_mc_idx);
+       layers[0].type = EDAC_MC_LAYER_CHIP_SELECT;
+       layers[0].size = 4;
+       layers[0].is_virt_csrow = true;
+       layers[1].type = EDAC_MC_LAYER_CHANNEL;
+       layers[1].size = 1;
+       layers[1].is_virt_csrow = false;
+       mci = edac_mc_alloc(edac_mc_idx, ARRAY_SIZE(layers), sizeof(*pdata));
        if (!mci) {
                devres_release_group(&op->dev, mpc85xx_mc_err_probe);
                return -ENOMEM;
index 7e5ff367705c66c25f8ad914772729a04cf585f4..b0bb5a3d2527698c1f4659997fc6950526e5e215 100644 (file)
@@ -611,12 +611,17 @@ static void mv64x60_mc_check(struct mem_ctl_info *mci)
 
        /* first bit clear in ECC Err Reg, 1 bit error, correctable by HW */
        if (!(reg & 0x1))
-               edac_mc_handle_ce(mci, err_addr >> PAGE_SHIFT,
-                                 err_addr & PAGE_MASK, syndrome, 0, 0,
-                                 mci->ctl_name);
+               edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
+                                    err_addr >> PAGE_SHIFT,
+                                    err_addr & PAGE_MASK, syndrome,
+                                    0, 0, -1,
+                                    mci->ctl_name, "", NULL);
        else    /* 2 bit error, UE */
-               edac_mc_handle_ue(mci, err_addr >> PAGE_SHIFT,
-                                 err_addr & PAGE_MASK, 0, mci->ctl_name);
+               edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
+                                    err_addr >> PAGE_SHIFT,
+                                    err_addr & PAGE_MASK, 0,
+                                    0, 0, -1,
+                                    mci->ctl_name, "", NULL);
 
        /* clear the error */
        out_le32(pdata->mc_vbase + MV64X60_SDRAM_ERR_ADDR, 0);
@@ -656,6 +661,8 @@ static void mv64x60_init_csrows(struct mem_ctl_info *mci,
                                struct mv64x60_mc_pdata *pdata)
 {
        struct csrow_info *csrow;
+       struct dimm_info *dimm;
+
        u32 devtype;
        u32 ctl;
 
@@ -664,35 +671,36 @@ static void mv64x60_init_csrows(struct mem_ctl_info *mci,
        ctl = in_le32(pdata->mc_vbase + MV64X60_SDRAM_CONFIG);
 
        csrow = &mci->csrows[0];
-       csrow->first_page = 0;
-       csrow->nr_pages = pdata->total_mem >> PAGE_SHIFT;
-       csrow->last_page = csrow->first_page + csrow->nr_pages - 1;
-       csrow->grain = 8;
+       dimm = csrow->channels[0].dimm;
+
+       dimm->nr_pages = pdata->total_mem >> PAGE_SHIFT;
+       dimm->grain = 8;
 
-       csrow->mtype = (ctl & MV64X60_SDRAM_REGISTERED) ? MEM_RDDR : MEM_DDR;
+       dimm->mtype = (ctl & MV64X60_SDRAM_REGISTERED) ? MEM_RDDR : MEM_DDR;
 
        devtype = (ctl >> 20) & 0x3;
        switch (devtype) {
        case 0x0:
-               csrow->dtype = DEV_X32;
+               dimm->dtype = DEV_X32;
                break;
        case 0x2:               /* could be X8 too, but no way to tell */
-               csrow->dtype = DEV_X16;
+               dimm->dtype = DEV_X16;
                break;
        case 0x3:
-               csrow->dtype = DEV_X4;
+               dimm->dtype = DEV_X4;
                break;
        default:
-               csrow->dtype = DEV_UNKNOWN;
+               dimm->dtype = DEV_UNKNOWN;
                break;
        }
 
-       csrow->edac_mode = EDAC_SECDED;
+       dimm->edac_mode = EDAC_SECDED;
 }
 
 static int __devinit mv64x60_mc_err_probe(struct platform_device *pdev)
 {
        struct mem_ctl_info *mci;
+       struct edac_mc_layer layers[2];
        struct mv64x60_mc_pdata *pdata;
        struct resource *r;
        u32 ctl;
@@ -701,7 +709,14 @@ static int __devinit mv64x60_mc_err_probe(struct platform_device *pdev)
        if (!devres_open_group(&pdev->dev, mv64x60_mc_err_probe, GFP_KERNEL))
                return -ENOMEM;
 
-       mci = edac_mc_alloc(sizeof(struct mv64x60_mc_pdata), 1, 1, edac_mc_idx);
+       layers[0].type = EDAC_MC_LAYER_CHIP_SELECT;
+       layers[0].size = 1;
+       layers[0].is_virt_csrow = true;
+       layers[1].type = EDAC_MC_LAYER_CHANNEL;
+       layers[1].size = 1;
+       layers[1].is_virt_csrow = false;
+       mci = edac_mc_alloc(edac_mc_idx, ARRAY_SIZE(layers), layers,
+                           sizeof(struct mv64x60_mc_pdata));
        if (!mci) {
                printk(KERN_ERR "%s: No memory for CPU err\n", __func__);
                devres_release_group(&pdev->dev, mv64x60_mc_err_probe);
index 7f71ee43674486fe3051013729b7b287225f4619..b095a906a994bc7b092362f21ed9f31eecf8d288 100644 (file)
@@ -110,15 +110,16 @@ static void pasemi_edac_process_error_info(struct mem_ctl_info *mci, u32 errsta)
        /* uncorrectable/multi-bit errors */
        if (errsta & (MCDEBUG_ERRSTA_MBE_STATUS |
                      MCDEBUG_ERRSTA_RFL_STATUS)) {
-               edac_mc_handle_ue(mci, mci->csrows[cs].first_page, 0,
-                                 cs, mci->ctl_name);
+               edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
+                                    mci->csrows[cs].first_page, 0, 0,
+                                    cs, 0, -1, mci->ctl_name, "", NULL);
        }
 
        /* correctable/single-bit errors */
-       if (errsta & MCDEBUG_ERRSTA_SBE_STATUS) {
-               edac_mc_handle_ce(mci, mci->csrows[cs].first_page, 0,
-                                 0, cs, 0, mci->ctl_name);
-       }
+       if (errsta & MCDEBUG_ERRSTA_SBE_STATUS)
+               edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
+                                    mci->csrows[cs].first_page, 0, 0,
+                                    cs, 0, -1, mci->ctl_name, "", NULL);
 }
 
 static void pasemi_edac_check(struct mem_ctl_info *mci)
@@ -135,11 +136,13 @@ static int pasemi_edac_init_csrows(struct mem_ctl_info *mci,
                                   enum edac_type edac_mode)
 {
        struct csrow_info *csrow;
+       struct dimm_info *dimm;
        u32 rankcfg;
        int index;
 
        for (index = 0; index < mci->nr_csrows; index++) {
                csrow = &mci->csrows[index];
+               dimm = csrow->channels[0].dimm;
 
                pci_read_config_dword(pdev,
                                      MCDRAM_RANKCFG + (index * 12),
@@ -151,20 +154,20 @@ static int pasemi_edac_init_csrows(struct mem_ctl_info *mci,
                switch ((rankcfg & MCDRAM_RANKCFG_TYPE_SIZE_M) >>
                        MCDRAM_RANKCFG_TYPE_SIZE_S) {
                case 0:
-                       csrow->nr_pages = 128 << (20 - PAGE_SHIFT);
+                       dimm->nr_pages = 128 << (20 - PAGE_SHIFT);
                        break;
                case 1:
-                       csrow->nr_pages = 256 << (20 - PAGE_SHIFT);
+                       dimm->nr_pages = 256 << (20 - PAGE_SHIFT);
                        break;
                case 2:
                case 3:
-                       csrow->nr_pages = 512 << (20 - PAGE_SHIFT);
+                       dimm->nr_pages = 512 << (20 - PAGE_SHIFT);
                        break;
                case 4:
-                       csrow->nr_pages = 1024 << (20 - PAGE_SHIFT);
+                       dimm->nr_pages = 1024 << (20 - PAGE_SHIFT);
                        break;
                case 5:
-                       csrow->nr_pages = 2048 << (20 - PAGE_SHIFT);
+                       dimm->nr_pages = 2048 << (20 - PAGE_SHIFT);
                        break;
                default:
                        edac_mc_printk(mci, KERN_ERR,
@@ -174,13 +177,13 @@ static int pasemi_edac_init_csrows(struct mem_ctl_info *mci,
                }
 
                csrow->first_page = last_page_in_mmc;
-               csrow->last_page = csrow->first_page + csrow->nr_pages - 1;
-               last_page_in_mmc += csrow->nr_pages;
+               csrow->last_page = csrow->first_page + dimm->nr_pages - 1;
+               last_page_in_mmc += dimm->nr_pages;
                csrow->page_mask = 0;
-               csrow->grain = PASEMI_EDAC_ERROR_GRAIN;
-               csrow->mtype = MEM_DDR;
-               csrow->dtype = DEV_UNKNOWN;
-               csrow->edac_mode = edac_mode;
+               dimm->grain = PASEMI_EDAC_ERROR_GRAIN;
+               dimm->mtype = MEM_DDR;
+               dimm->dtype = DEV_UNKNOWN;
+               dimm->edac_mode = edac_mode;
        }
        return 0;
 }
@@ -189,6 +192,7 @@ static int __devinit pasemi_edac_probe(struct pci_dev *pdev,
                const struct pci_device_id *ent)
 {
        struct mem_ctl_info *mci = NULL;
+       struct edac_mc_layer layers[2];
        u32 errctl1, errcor, scrub, mcen;
 
        pci_read_config_dword(pdev, MCCFG_MCEN, &mcen);
@@ -205,9 +209,14 @@ static int __devinit pasemi_edac_probe(struct pci_dev *pdev,
                MCDEBUG_ERRCTL1_RFL_LOG_EN;
        pci_write_config_dword(pdev, MCDEBUG_ERRCTL1, errctl1);
 
-       mci = edac_mc_alloc(0, PASEMI_EDAC_NR_CSROWS, PASEMI_EDAC_NR_CHANS,
-                               system_mmc_id++);
-
+       layers[0].type = EDAC_MC_LAYER_CHIP_SELECT;
+       layers[0].size = PASEMI_EDAC_NR_CSROWS;
+       layers[0].is_virt_csrow = true;
+       layers[1].type = EDAC_MC_LAYER_CHANNEL;
+       layers[1].size = PASEMI_EDAC_NR_CHANS;
+       layers[1].is_virt_csrow = false;
+       mci = edac_mc_alloc(system_mmc_id++, ARRAY_SIZE(layers), layers,
+                           0);
        if (mci == NULL)
                return -ENOMEM;
 
index d427c69bb8b1ebf811b4aeadcf06ac532b3be4b1..f3f9fed06ad7d34ec8e3607f8141da6256e88e49 100644 (file)
@@ -727,7 +727,10 @@ ppc4xx_edac_handle_ce(struct mem_ctl_info *mci,
 
        for (row = 0; row < mci->nr_csrows; row++)
                if (ppc4xx_edac_check_bank_error(status, row))
-                       edac_mc_handle_ce_no_info(mci, message);
+                       edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
+                                            0, 0, 0,
+                                            row, 0, -1,
+                                            message, "", NULL);
 }
 
 /**
@@ -755,7 +758,10 @@ ppc4xx_edac_handle_ue(struct mem_ctl_info *mci,
 
        for (row = 0; row < mci->nr_csrows; row++)
                if (ppc4xx_edac_check_bank_error(status, row))
-                       edac_mc_handle_ue(mci, page, offset, row, message);
+                       edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
+                                            page, offset, 0,
+                                            row, 0, -1,
+                                            message, "", NULL);
 }
 
 /**
@@ -895,9 +901,8 @@ ppc4xx_edac_init_csrows(struct mem_ctl_info *mci, u32 mcopt1)
        enum mem_type mtype;
        enum dev_type dtype;
        enum edac_type edac_mode;
-       int row;
-       u32 mbxcf, size;
-       static u32 ppc4xx_last_page;
+       int row, j;
+       u32 mbxcf, size, nr_pages;
 
        /* Establish the memory type and width */
 
@@ -948,7 +953,7 @@ ppc4xx_edac_init_csrows(struct mem_ctl_info *mci, u32 mcopt1)
                case SDRAM_MBCF_SZ_2GB:
                case SDRAM_MBCF_SZ_4GB:
                case SDRAM_MBCF_SZ_8GB:
-                       csi->nr_pages = SDRAM_MBCF_SZ_TO_PAGES(size);
+                       nr_pages = SDRAM_MBCF_SZ_TO_PAGES(size);
                        break;
                default:
                        ppc4xx_edac_mc_printk(KERN_ERR, mci,
@@ -959,10 +964,6 @@ ppc4xx_edac_init_csrows(struct mem_ctl_info *mci, u32 mcopt1)
                        goto done;
                }
 
-               csi->first_page = ppc4xx_last_page;
-               csi->last_page  = csi->first_page + csi->nr_pages - 1;
-               csi->page_mask  = 0;
-
                /*
                 * It's unclear exactly what grain should be set to
                 * here. The SDRAM_ECCES register allows resolution of
@@ -975,15 +976,17 @@ ppc4xx_edac_init_csrows(struct mem_ctl_info *mci, u32 mcopt1)
                 * possible values would be the PLB width (16), the
                 * page size (PAGE_SIZE) or the memory width (2 or 4).
                 */
+               for (j = 0; j < csi->nr_channels; j++) {
+                       struct dimm_info *dimm = csi->channels[j].dimm;
 
-               csi->grain      = 1;
-
-               csi->mtype      = mtype;
-               csi->dtype      = dtype;
+                       dimm->nr_pages  = nr_pages / csi->nr_channels;
+                       dimm->grain     = 1;
 
-               csi->edac_mode  = edac_mode;
+                       dimm->mtype     = mtype;
+                       dimm->dtype     = dtype;
 
-               ppc4xx_last_page += csi->nr_pages;
+                       dimm->edac_mode = edac_mode;
+               }
        }
 
  done:
@@ -1236,6 +1239,7 @@ static int __devinit ppc4xx_edac_probe(struct platform_device *op)
        dcr_host_t dcr_host;
        const struct device_node *np = op->dev.of_node;
        struct mem_ctl_info *mci = NULL;
+       struct edac_mc_layer layers[2];
        static int ppc4xx_edac_instance;
 
        /*
@@ -1281,12 +1285,14 @@ static int __devinit ppc4xx_edac_probe(struct platform_device *op)
         * controller instance and perform the appropriate
         * initialization.
         */
-
-       mci = edac_mc_alloc(sizeof(struct ppc4xx_edac_pdata),
-                           ppc4xx_edac_nr_csrows,
-                           ppc4xx_edac_nr_chans,
-                           ppc4xx_edac_instance);
-
+       layers[0].type = EDAC_MC_LAYER_CHIP_SELECT;
+       layers[0].size = ppc4xx_edac_nr_csrows;
+       layers[0].is_virt_csrow = true;
+       layers[1].type = EDAC_MC_LAYER_CHANNEL;
+       layers[1].size = ppc4xx_edac_nr_chans;
+       layers[1].is_virt_csrow = false;
+       mci = edac_mc_alloc(ppc4xx_edac_instance, ARRAY_SIZE(layers), layers,
+                           sizeof(struct ppc4xx_edac_pdata));
        if (mci == NULL) {
                ppc4xx_edac_printk(KERN_ERR, "%s: "
                                   "Failed to allocate EDAC MC instance!\n",
index 6d908ad72d6458c2dce43983672a92f174e69c5c..e1cacd164f316d3821e750f106a82540e536d794 100644 (file)
@@ -179,10 +179,11 @@ static int r82600_process_error_info(struct mem_ctl_info *mci,
                error_found = 1;
 
                if (handle_errors)
-                       edac_mc_handle_ce(mci, page, 0, /* not avail */
-                                       syndrome,
-                                       edac_mc_find_csrow_by_page(mci, page),
-                                       0, mci->ctl_name);
+                       edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
+                                            page, 0, syndrome,
+                                            edac_mc_find_csrow_by_page(mci, page),
+                                            0, -1,
+                                            mci->ctl_name, "", NULL);
        }
 
        if (info->eapr & BIT(1)) {      /* UE? */
@@ -190,9 +191,11 @@ static int r82600_process_error_info(struct mem_ctl_info *mci,
 
                if (handle_errors)
                        /* 82600 doesn't give enough info */
-                       edac_mc_handle_ue(mci, page, 0,
-                                       edac_mc_find_csrow_by_page(mci, page),
-                                       mci->ctl_name);
+                       edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
+                                            page, 0, 0,
+                                            edac_mc_find_csrow_by_page(mci, page),
+                                            0, -1,
+                                            mci->ctl_name, "", NULL);
        }
 
        return error_found;
@@ -216,6 +219,7 @@ static void r82600_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev,
                        u8 dramcr)
 {
        struct csrow_info *csrow;
+       struct dimm_info *dimm;
        int index;
        u8 drbar;               /* SDRAM Row Boundary Address Register */
        u32 row_high_limit, row_high_limit_last;
@@ -227,6 +231,7 @@ static void r82600_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev,
 
        for (index = 0; index < mci->nr_csrows; index++) {
                csrow = &mci->csrows[index];
+               dimm = csrow->channels[0].dimm;
 
                /* find the DRAM Chip Select Base address and mask */
                pci_read_config_byte(pdev, R82600_DRBA + index, &drbar);
@@ -247,16 +252,17 @@ static void r82600_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev,
 
                csrow->first_page = row_base >> PAGE_SHIFT;
                csrow->last_page = (row_high_limit >> PAGE_SHIFT) - 1;
-               csrow->nr_pages = csrow->last_page - csrow->first_page + 1;
+
+               dimm->nr_pages = csrow->last_page - csrow->first_page + 1;
                /* Error address is top 19 bits - so granularity is      *
                 * 14 bits                                               */
-               csrow->grain = 1 << 14;
-               csrow->mtype = reg_sdram ? MEM_RDDR : MEM_DDR;
+               dimm->grain = 1 << 14;
+               dimm->mtype = reg_sdram ? MEM_RDDR : MEM_DDR;
                /* FIXME - check that this is unknowable with this chipset */
-               csrow->dtype = DEV_UNKNOWN;
+               dimm->dtype = DEV_UNKNOWN;
 
                /* Mode is global on 82600 */
-               csrow->edac_mode = ecc_on ? EDAC_SECDED : EDAC_NONE;
+               dimm->edac_mode = ecc_on ? EDAC_SECDED : EDAC_NONE;
                row_high_limit_last = row_high_limit;
        }
 }
@@ -264,6 +270,7 @@ static void r82600_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev,
 static int r82600_probe1(struct pci_dev *pdev, int dev_idx)
 {
        struct mem_ctl_info *mci;
+       struct edac_mc_layer layers[2];
        u8 dramcr;
        u32 eapr;
        u32 scrub_disabled;
@@ -278,8 +285,13 @@ static int r82600_probe1(struct pci_dev *pdev, int dev_idx)
        debugf2("%s(): sdram refresh rate = %#0x\n", __func__,
                sdram_refresh_rate);
        debugf2("%s(): DRAMC register = %#0x\n", __func__, dramcr);
-       mci = edac_mc_alloc(0, R82600_NR_CSROWS, R82600_NR_CHANS, 0);
-
+       layers[0].type = EDAC_MC_LAYER_CHIP_SELECT;
+       layers[0].size = R82600_NR_CSROWS;
+       layers[0].is_virt_csrow = true;
+       layers[1].type = EDAC_MC_LAYER_CHANNEL;
+       layers[1].size = R82600_NR_CHANS;
+       layers[1].is_virt_csrow = false;
+       mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers, 0);
        if (mci == NULL)
                return -ENOMEM;
 
index 123204f8e23b947a9f7035403f6961f9d988ec22..4adaf4b7da993c3d6b1f25d430dbdf1aa3e7a875 100644 (file)
@@ -314,8 +314,6 @@ struct sbridge_pvt {
        struct sbridge_info     info;
        struct sbridge_channel  channel[NUM_CHANNELS];
 
-       int                     csrow_map[NUM_CHANNELS][MAX_DIMMS];
-
        /* Memory type detection */
        bool                    is_mirrored, is_lockstep, is_close_pg;
 
@@ -487,29 +485,14 @@ static struct pci_dev *get_pdev_slot_func(u8 bus, unsigned slot,
 }
 
 /**
- * sbridge_get_active_channels() - gets the number of channels and csrows
+ * check_if_ecc_is_active() - Checks if ECC is active
  * bus:                Device bus
- * @channels:  Number of channels that will be returned
- * @csrows:    Number of csrows found
- *
- * Since EDAC core needs to know in advance the number of available channels
- * and csrows, in order to allocate memory for csrows/channels, it is needed
- * to run two similar steps. At the first step, implemented on this function,
- * it checks the number of csrows/channels present at one socket, identified
- * by the associated PCI bus.
- * this is used in order to properly allocate the size of mci components.
- * Note: one csrow is one dimm.
  */
-static int sbridge_get_active_channels(const u8 bus, unsigned *channels,
-                                     unsigned *csrows)
+static int check_if_ecc_is_active(const u8 bus)
 {
        struct pci_dev *pdev = NULL;
-       int i, j;
        u32 mcmtr;
 
-       *channels = 0;
-       *csrows = 0;
-
        pdev = get_pdev_slot_func(bus, 15, 0);
        if (!pdev) {
                sbridge_printk(KERN_ERR, "Couldn't find PCI device "
@@ -523,41 +506,14 @@ static int sbridge_get_active_channels(const u8 bus, unsigned *channels,
                sbridge_printk(KERN_ERR, "ECC is disabled. Aborting\n");
                return -ENODEV;
        }
-
-       for (i = 0; i < NUM_CHANNELS; i++) {
-               u32 mtr;
-
-               /* Device 15 functions 2 - 5  */
-               pdev = get_pdev_slot_func(bus, 15, 2 + i);
-               if (!pdev) {
-                       sbridge_printk(KERN_ERR, "Couldn't find PCI device "
-                                                "%2x.%02d.%d!!!\n",
-                                                bus, 15, 2 + i);
-                       return -ENODEV;
-               }
-               (*channels)++;
-
-               for (j = 0; j < ARRAY_SIZE(mtr_regs); j++) {
-                       pci_read_config_dword(pdev, mtr_regs[j], &mtr);
-                       debugf1("Bus#%02x channel #%d  MTR%d = %x\n", bus, i, j, mtr);
-                       if (IS_DIMM_PRESENT(mtr))
-                               (*csrows)++;
-               }
-       }
-
-       debugf0("Number of active channels: %d, number of active dimms: %d\n",
-               *channels, *csrows);
-
        return 0;
 }
 
-static int get_dimm_config(const struct mem_ctl_info *mci)
+static int get_dimm_config(struct mem_ctl_info *mci)
 {
        struct sbridge_pvt *pvt = mci->pvt_info;
-       struct csrow_info *csr;
+       struct dimm_info *dimm;
        int i, j, banks, ranks, rows, cols, size, npages;
-       int csrow = 0;
-       unsigned long last_page = 0;
        u32 reg;
        enum edac_type mode;
        enum mem_type mtype;
@@ -616,6 +572,8 @@ static int get_dimm_config(const struct mem_ctl_info *mci)
                u32 mtr;
 
                for (j = 0; j < ARRAY_SIZE(mtr_regs); j++) {
+                       dimm = EDAC_DIMM_PTR(mci->layers, mci->dimms, mci->n_layers,
+                                      i, j, 0);
                        pci_read_config_dword(pvt->pci_tad[i],
                                              mtr_regs[j], &mtr);
                        debugf4("Channel #%d  MTR%d = %x\n", i, j, mtr);
@@ -634,29 +592,15 @@ static int get_dimm_config(const struct mem_ctl_info *mci)
                                        pvt->sbridge_dev->mc, i, j,
                                        size, npages,
                                        banks, ranks, rows, cols);
-                               csr = &mci->csrows[csrow];
-
-                               csr->first_page = last_page;
-                               csr->last_page = last_page + npages - 1;
-                               csr->page_mask = 0UL;   /* Unused */
-                               csr->nr_pages = npages;
-                               csr->grain = 32;
-                               csr->csrow_idx = csrow;
-                               csr->dtype = (banks == 8) ? DEV_X8 : DEV_X4;
-                               csr->ce_count = 0;
-                               csr->ue_count = 0;
-                               csr->mtype = mtype;
-                               csr->edac_mode = mode;
-                               csr->nr_channels = 1;
-                               csr->channels[0].chan_idx = i;
-                               csr->channels[0].ce_count = 0;
-                               pvt->csrow_map[i][j] = csrow;
-                               snprintf(csr->channels[0].label,
-                                        sizeof(csr->channels[0].label),
+
+                               dimm->nr_pages = npages;
+                               dimm->grain = 32;
+                               dimm->dtype = (banks == 8) ? DEV_X8 : DEV_X4;
+                               dimm->mtype = mtype;
+                               dimm->edac_mode = mode;
+                               snprintf(dimm->label, sizeof(dimm->label),
                                         "CPU_SrcID#%u_Channel#%u_DIMM#%u",
                                         pvt->sbridge_dev->source_id, i, j);
-                               last_page += npages;
-                               csrow++;
                        }
                }
        }
@@ -844,11 +788,10 @@ static int get_memory_error_data(struct mem_ctl_info *mci,
                                 u8 *socket,
                                 long *channel_mask,
                                 u8 *rank,
-                                char *area_type)
+                                char **area_type, char *msg)
 {
        struct mem_ctl_info     *new_mci;
        struct sbridge_pvt *pvt = mci->pvt_info;
-       char                    msg[256];
        int                     n_rir, n_sads, n_tads, sad_way, sck_xch;
        int                     sad_interl, idx, base_ch;
        int                     interleave_mode;
@@ -870,12 +813,10 @@ static int get_memory_error_data(struct mem_ctl_info *mci,
         */
        if ((addr > (u64) pvt->tolm) && (addr < (1LL << 32))) {
                sprintf(msg, "Error at TOLM area, on addr 0x%08Lx", addr);
-               edac_mc_handle_ce_no_info(mci, msg);
                return -EINVAL;
        }
        if (addr >= (u64)pvt->tohm) {
                sprintf(msg, "Error at MMIOH area, on addr 0x%016Lx", addr);
-               edac_mc_handle_ce_no_info(mci, msg);
                return -EINVAL;
        }
 
@@ -892,7 +833,6 @@ static int get_memory_error_data(struct mem_ctl_info *mci,
                limit = SAD_LIMIT(reg);
                if (limit <= prv) {
                        sprintf(msg, "Can't discover the memory socket");
-                       edac_mc_handle_ce_no_info(mci, msg);
                        return -EINVAL;
                }
                if  (addr <= limit)
@@ -901,10 +841,9 @@ static int get_memory_error_data(struct mem_ctl_info *mci,
        }
        if (n_sads == MAX_SAD) {
                sprintf(msg, "Can't discover the memory socket");
-               edac_mc_handle_ce_no_info(mci, msg);
                return -EINVAL;
        }
-       area_type = get_dram_attr(reg);
+       *area_type = get_dram_attr(reg);
        interleave_mode = INTERLEAVE_MODE(reg);
 
        pci_read_config_dword(pvt->pci_sad0, interleave_list[n_sads],
@@ -942,7 +881,6 @@ static int get_memory_error_data(struct mem_ctl_info *mci,
                break;
        default:
                sprintf(msg, "Can't discover socket interleave");
-               edac_mc_handle_ce_no_info(mci, msg);
                return -EINVAL;
        }
        *socket = sad_interleave[idx];
@@ -957,7 +895,6 @@ static int get_memory_error_data(struct mem_ctl_info *mci,
        if (!new_mci) {
                sprintf(msg, "Struct for socket #%u wasn't initialized",
                        *socket);
-               edac_mc_handle_ce_no_info(mci, msg);
                return -EINVAL;
        }
        mci = new_mci;
@@ -973,7 +910,6 @@ static int get_memory_error_data(struct mem_ctl_info *mci,
                limit = TAD_LIMIT(reg);
                if (limit <= prv) {
                        sprintf(msg, "Can't discover the memory channel");
-                       edac_mc_handle_ce_no_info(mci, msg);
                        return -EINVAL;
                }
                if  (addr <= limit)
@@ -1013,7 +949,6 @@ static int get_memory_error_data(struct mem_ctl_info *mci,
                break;
        default:
                sprintf(msg, "Can't discover the TAD target");
-               edac_mc_handle_ce_no_info(mci, msg);
                return -EINVAL;
        }
        *channel_mask = 1 << base_ch;
@@ -1027,7 +962,6 @@ static int get_memory_error_data(struct mem_ctl_info *mci,
                        break;
                default:
                        sprintf(msg, "Invalid mirror set. Can't decode addr");
-                       edac_mc_handle_ce_no_info(mci, msg);
                        return -EINVAL;
                }
        } else
@@ -1055,7 +989,6 @@ static int get_memory_error_data(struct mem_ctl_info *mci,
        if (offset > addr) {
                sprintf(msg, "Can't calculate ch addr: TAD offset 0x%08Lx is too high for addr 0x%08Lx!",
                        offset, addr);
-               edac_mc_handle_ce_no_info(mci, msg);
                return -EINVAL;
        }
        addr -= offset;
@@ -1095,7 +1028,6 @@ static int get_memory_error_data(struct mem_ctl_info *mci,
        if (n_rir == MAX_RIR_RANGES) {
                sprintf(msg, "Can't discover the memory rank for ch addr 0x%08Lx",
                        ch_addr);
-               edac_mc_handle_ce_no_info(mci, msg);
                return -EINVAL;
        }
        rir_way = RIR_WAY(reg);
@@ -1409,7 +1341,8 @@ static void sbridge_mce_output_error(struct mem_ctl_info *mci,
 {
        struct mem_ctl_info *new_mci;
        struct sbridge_pvt *pvt = mci->pvt_info;
-       char *type, *optype, *msg, *recoverable_msg;
+       enum hw_event_mc_err_type tp_event;
+       char *type, *optype, msg[256];
        bool ripv = GET_BITFIELD(m->mcgstatus, 0, 0);
        bool overflow = GET_BITFIELD(m->status, 62, 62);
        bool uncorrected_error = GET_BITFIELD(m->status, 61, 61);
@@ -1421,13 +1354,21 @@ static void sbridge_mce_output_error(struct mem_ctl_info *mci,
        u32 optypenum = GET_BITFIELD(m->status, 4, 6);
        long channel_mask, first_channel;
        u8  rank, socket;
-       int csrow, rc, dimm;
-       char *area_type = "Unknown";
-
-       if (ripv)
-               type = "NON_FATAL";
-       else
-               type = "FATAL";
+       int rc, dimm;
+       char *area_type = NULL;
+
+       if (uncorrected_error) {
+               if (ripv) {
+                       type = "FATAL";
+                       tp_event = HW_EVENT_ERR_FATAL;
+               } else {
+                       type = "NON_FATAL";
+                       tp_event = HW_EVENT_ERR_UNCORRECTED;
+               }
+       } else {
+               type = "CORRECTED";
+               tp_event = HW_EVENT_ERR_CORRECTED;
+       }
 
        /*
         * According with Table 15-9 of the Intel Architecture spec vol 3A,
@@ -1445,19 +1386,19 @@ static void sbridge_mce_output_error(struct mem_ctl_info *mci,
        } else {
                switch (optypenum) {
                case 0:
-                       optype = "generic undef request";
+                       optype = "generic undef request error";
                        break;
                case 1:
-                       optype = "memory read";
+                       optype = "memory read error";
                        break;
                case 2:
-                       optype = "memory write";
+                       optype = "memory write error";
                        break;
                case 3:
-                       optype = "addr/cmd";
+                       optype = "addr/cmd error";
                        break;
                case 4:
-                       optype = "memory scrubbing";
+                       optype = "memory scrubbing error";
                        break;
                default:
                        optype = "reserved";
@@ -1466,13 +1407,13 @@ static void sbridge_mce_output_error(struct mem_ctl_info *mci,
        }
 
        rc = get_memory_error_data(mci, m->addr, &socket,
-                                  &channel_mask, &rank, area_type);
+                                  &channel_mask, &rank, &area_type, msg);
        if (rc < 0)
-               return;
+               goto err_parsing;
        new_mci = get_mci_for_node_id(socket);
        if (!new_mci) {
-               edac_mc_handle_ce_no_info(mci, "Error: socket got corrupted!");
-               return;
+               strcpy(msg, "Error: socket got corrupted!");
+               goto err_parsing;
        }
        mci = new_mci;
        pvt = mci->pvt_info;
@@ -1486,45 +1427,39 @@ static void sbridge_mce_output_error(struct mem_ctl_info *mci,
        else
                dimm = 2;
 
-       csrow = pvt->csrow_map[first_channel][dimm];
-
-       if (uncorrected_error && recoverable)
-               recoverable_msg = " recoverable";
-       else
-               recoverable_msg = "";
 
        /*
-        * FIXME: What should we do with "channel" information on mcelog?
-        * Probably, we can just discard it, as the channel information
-        * comes from the get_memory_error_data() address decoding
+        * FIXME: On some memory configurations (mirror, lockstep), the
+        * Memory Controller can't point the error to a single DIMM. The
+        * EDAC core should be handling the channel mask, in order to point
+        * to the group of dimm's where the error may be happening.
         */
-       msg = kasprintf(GFP_ATOMIC,
-                       "%d %s error(s): %s on %s area %s%s: cpu=%d Err=%04x:%04x (ch=%d), "
-                       "addr = 0x%08llx => socket=%d, Channel=%ld(mask=%ld), rank=%d\n",
-                       core_err_cnt,
-                       area_type,
-                       optype,
-                       type,
-                       recoverable_msg,
-                       overflow ? "OVERFLOW" : "",
-                       m->cpu,
-                       mscod, errcode,
-                       channel,                /* 1111b means not specified */
-                       (long long) m->addr,
-                       socket,
-                       first_channel,          /* This is the real channel on SB */
-                       channel_mask,
-                       rank);
+       snprintf(msg, sizeof(msg),
+                "count:%d%s%s area:%s err_code:%04x:%04x socket:%d channel_mask:%ld rank:%d",
+                core_err_cnt,
+                overflow ? " OVERFLOW" : "",
+                (uncorrected_error && recoverable) ? " recoverable" : "",
+                area_type,
+                mscod, errcode,
+                socket,
+                channel_mask,
+                rank);
 
        debugf0("%s", msg);
 
+       /* FIXME: need support for channel mask */
+
        /* Call the helper to output message */
-       if (uncorrected_error)
-               edac_mc_handle_fbd_ue(mci, csrow, 0, 0, msg);
-       else
-               edac_mc_handle_fbd_ce(mci, csrow, 0, msg);
+       edac_mc_handle_error(tp_event, mci,
+                            m->addr >> PAGE_SHIFT, m->addr & ~PAGE_MASK, 0,
+                            channel, dimm, -1,
+                            optype, msg, m);
+       return;
+err_parsing:
+       edac_mc_handle_error(tp_event, mci, 0, 0, 0,
+                            -1, -1, -1,
+                            msg, "", m);
 
-       kfree(msg);
 }
 
 /*
@@ -1683,16 +1618,25 @@ static void sbridge_unregister_mci(struct sbridge_dev *sbridge_dev)
 static int sbridge_register_mci(struct sbridge_dev *sbridge_dev)
 {
        struct mem_ctl_info *mci;
+       struct edac_mc_layer layers[2];
        struct sbridge_pvt *pvt;
-       int rc, channels, csrows;
+       int rc;
 
        /* Check the number of active and not disabled channels */
-       rc = sbridge_get_active_channels(sbridge_dev->bus, &channels, &csrows);
+       rc = check_if_ecc_is_active(sbridge_dev->bus);
        if (unlikely(rc < 0))
                return rc;
 
        /* allocate a new MC control structure */
-       mci = edac_mc_alloc(sizeof(*pvt), csrows, channels, sbridge_dev->mc);
+       layers[0].type = EDAC_MC_LAYER_CHANNEL;
+       layers[0].size = NUM_CHANNELS;
+       layers[0].is_virt_csrow = false;
+       layers[1].type = EDAC_MC_LAYER_SLOT;
+       layers[1].size = MAX_DIMMS;
+       layers[1].is_virt_csrow = true;
+       mci = edac_mc_alloc(sbridge_dev->mc, ARRAY_SIZE(layers), layers,
+                           sizeof(*pvt));
+
        if (unlikely(!mci))
                return -ENOMEM;
 
index e99d00976189344193e974cc9b972512a8f7359e..7bb4614730db846445909d460a5b5a0953d8ab50 100644 (file)
@@ -71,7 +71,10 @@ static void tile_edac_check(struct mem_ctl_info *mci)
        if (mem_error.sbe_count != priv->ce_count) {
                dev_dbg(mci->dev, "ECC CE err on node %d\n", priv->node);
                priv->ce_count = mem_error.sbe_count;
-               edac_mc_handle_ce(mci, 0, 0, 0, 0, 0, mci->ctl_name);
+               edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
+                                    0, 0, 0,
+                                    0, 0, -1,
+                                    mci->ctl_name, "", NULL);
        }
 }
 
@@ -84,6 +87,7 @@ static int __devinit tile_edac_init_csrows(struct mem_ctl_info *mci)
        struct csrow_info       *csrow = &mci->csrows[0];
        struct tile_edac_priv   *priv = mci->pvt_info;
        struct mshim_mem_info   mem_info;
+       struct dimm_info *dimm = csrow->channels[0].dimm;
 
        if (hv_dev_pread(priv->hv_devhdl, 0, (HV_VirtAddr)&mem_info,
                sizeof(struct mshim_mem_info), MSHIM_MEM_INFO_OFF) !=
@@ -93,27 +97,25 @@ static int __devinit tile_edac_init_csrows(struct mem_ctl_info *mci)
        }
 
        if (mem_info.mem_ecc)
-               csrow->edac_mode = EDAC_SECDED;
+               dimm->edac_mode = EDAC_SECDED;
        else
-               csrow->edac_mode = EDAC_NONE;
+               dimm->edac_mode = EDAC_NONE;
        switch (mem_info.mem_type) {
        case DDR2:
-               csrow->mtype = MEM_DDR2;
+               dimm->mtype = MEM_DDR2;
                break;
 
        case DDR3:
-               csrow->mtype = MEM_DDR3;
+               dimm->mtype = MEM_DDR3;
                break;
 
        default:
                return -1;
        }
 
-       csrow->first_page = 0;
-       csrow->nr_pages = mem_info.mem_size >> PAGE_SHIFT;
-       csrow->last_page = csrow->first_page + csrow->nr_pages - 1;
-       csrow->grain = TILE_EDAC_ERROR_GRAIN;
-       csrow->dtype = DEV_UNKNOWN;
+       dimm->nr_pages = mem_info.mem_size >> PAGE_SHIFT;
+       dimm->grain = TILE_EDAC_ERROR_GRAIN;
+       dimm->dtype = DEV_UNKNOWN;
 
        return 0;
 }
@@ -123,6 +125,7 @@ static int __devinit tile_edac_mc_probe(struct platform_device *pdev)
        char                    hv_file[32];
        int                     hv_devhdl;
        struct mem_ctl_info     *mci;
+       struct edac_mc_layer    layers[2];
        struct tile_edac_priv   *priv;
        int                     rc;
 
@@ -132,8 +135,14 @@ static int __devinit tile_edac_mc_probe(struct platform_device *pdev)
                return -EINVAL;
 
        /* A TILE MC has a single channel and one chip-select row. */
-       mci = edac_mc_alloc(sizeof(struct tile_edac_priv),
-               TILE_EDAC_NR_CSROWS, TILE_EDAC_NR_CHANS, pdev->id);
+       layers[0].type = EDAC_MC_LAYER_CHIP_SELECT;
+       layers[0].size = TILE_EDAC_NR_CSROWS;
+       layers[0].is_virt_csrow = true;
+       layers[1].type = EDAC_MC_LAYER_CHANNEL;
+       layers[1].size = TILE_EDAC_NR_CHANS;
+       layers[1].is_virt_csrow = false;
+       mci = edac_mc_alloc(pdev->id, ARRAY_SIZE(layers), layers,
+                           sizeof(struct tile_edac_priv));
        if (mci == NULL)
                return -ENOMEM;
        priv = mci->pvt_info;
index a438297389e5d2919476e3a41a61751e8dd38dc3..1ac7962d63eadcd5ba8ddd17ae58b96b9a062e2e 100644 (file)
@@ -215,19 +215,26 @@ static void x38_process_error_info(struct mem_ctl_info *mci,
                return;
 
        if ((info->errsts ^ info->errsts2) & X38_ERRSTS_BITS) {
-               edac_mc_handle_ce_no_info(mci, "UE overwrote CE");
+               edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 0, 0, 0,
+                                    -1, -1, -1,
+                                    "UE overwrote CE", "", NULL);
                info->errsts = info->errsts2;
        }
 
        for (channel = 0; channel < x38_channel_num; channel++) {
                log = info->eccerrlog[channel];
                if (log & X38_ECCERRLOG_UE) {
-                       edac_mc_handle_ue(mci, 0, 0,
-                               eccerrlog_row(channel, log), "x38 UE");
+                       edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
+                                            0, 0, 0,
+                                            eccerrlog_row(channel, log),
+                                            -1, -1,
+                                            "x38 UE", "", NULL);
                } else if (log & X38_ECCERRLOG_CE) {
-                       edac_mc_handle_ce(mci, 0, 0,
-                               eccerrlog_syndrome(log),
-                               eccerrlog_row(channel, log), 0, "x38 CE");
+                       edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
+                                            0, 0, eccerrlog_syndrome(log),
+                                            eccerrlog_row(channel, log),
+                                            -1, -1,
+                                            "x38 CE", "", NULL);
                }
        }
 }
@@ -317,9 +324,9 @@ static unsigned long drb_to_nr_pages(
 static int x38_probe1(struct pci_dev *pdev, int dev_idx)
 {
        int rc;
-       int i;
+       int i, j;
        struct mem_ctl_info *mci = NULL;
-       unsigned long last_page;
+       struct edac_mc_layer layers[2];
        u16 drbs[X38_CHANNELS][X38_RANKS_PER_CHANNEL];
        bool stacked;
        void __iomem *window;
@@ -335,7 +342,13 @@ static int x38_probe1(struct pci_dev *pdev, int dev_idx)
        how_many_channel(pdev);
 
        /* FIXME: unconventional pvt_info usage */
-       mci = edac_mc_alloc(0, X38_RANKS, x38_channel_num, 0);
+       layers[0].type = EDAC_MC_LAYER_CHIP_SELECT;
+       layers[0].size = X38_RANKS;
+       layers[0].is_virt_csrow = true;
+       layers[1].type = EDAC_MC_LAYER_CHANNEL;
+       layers[1].size = x38_channel_num;
+       layers[1].is_virt_csrow = false;
+       mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers, 0);
        if (!mci)
                return -ENOMEM;
 
@@ -363,7 +376,6 @@ static int x38_probe1(struct pci_dev *pdev, int dev_idx)
         * cumulative; the last one will contain the total memory
         * contained in all ranks.
         */
-       last_page = -1UL;
        for (i = 0; i < mci->nr_csrows; i++) {
                unsigned long nr_pages;
                struct csrow_info *csrow = &mci->csrows[i];
@@ -372,20 +384,18 @@ static int x38_probe1(struct pci_dev *pdev, int dev_idx)
                        i / X38_RANKS_PER_CHANNEL,
                        i % X38_RANKS_PER_CHANNEL);
 
-               if (nr_pages == 0) {
-                       csrow->mtype = MEM_EMPTY;
+               if (nr_pages == 0)
                        continue;
-               }
 
-               csrow->first_page = last_page + 1;
-               last_page += nr_pages;
-               csrow->last_page = last_page;
-               csrow->nr_pages = nr_pages;
+               for (j = 0; j < x38_channel_num; j++) {
+                       struct dimm_info *dimm = csrow->channels[j].dimm;
 
-               csrow->grain = nr_pages << PAGE_SHIFT;
-               csrow->mtype = MEM_DDR2;
-               csrow->dtype = DEV_UNKNOWN;
-               csrow->edac_mode = EDAC_UNKNOWN;
+                       dimm->nr_pages = nr_pages / x38_channel_num;
+                       dimm->grain = nr_pages << PAGE_SHIFT;
+                       dimm->mtype = MEM_DDR2;
+                       dimm->dtype = DEV_UNKNOWN;
+                       dimm->edac_mode = EDAC_UNKNOWN;
+               }
        }
 
        x38_clear_error_info(mci);
index aa3642cb820989e77d89799c11551463a36416a6..c4067d0141f7c083ea9de58c72dc0ea3122ca300 100644 (file)
@@ -114,6 +114,14 @@ config GPIO_EP93XX
        depends on ARCH_EP93XX
        select GPIO_GENERIC
 
+config GPIO_MM_LANTIQ
+       bool "Lantiq Memory mapped GPIOs"
+       depends on LANTIQ && SOC_XWAY
+       help
+         This enables support for memory mapped GPIOs on the External Bus Unit
+         (EBU) found on Lantiq SoCs. The gpios are output only as they are
+         created by attaching a 16bit latch to the bus.
+
 config GPIO_MPC5200
        def_bool y
        depends on PPC_MPC52xx
@@ -167,6 +175,14 @@ config GPIO_PXA
        help
          Say yes here to support the PXA GPIO device
 
+config GPIO_STA2X11
+       bool "STA2x11/ConneXt GPIO support"
+       depends on MFD_STA2X11
+       select GENERIC_IRQ_CHIP
+       help
+         Say yes here to support the STA2x11/ConneXt GPIO device.
+         The GPIO module has 128 GPIO pins with alternate functions.
+
 config GPIO_XILINX
        bool "Xilinx GPIO support"
        depends on PPC_OF || MICROBLAZE
@@ -180,13 +196,13 @@ config GPIO_VR41XX
          Say yes here to support the NEC VR4100 series General-purpose I/O Uint
 
 config GPIO_SCH
-       tristate "Intel SCH/TunnelCreek GPIO"
+       tristate "Intel SCH/TunnelCreek/Centerton GPIO"
        depends on PCI && X86
        select MFD_CORE
        select LPC_SCH
        help
-         Say yes here to support GPIO interface on Intel Poulsbo SCH
-         or Intel Tunnel Creek processor.
+         Say yes here to support GPIO interface on Intel Poulsbo SCH,
+         Intel Tunnel Creek processor or Intel Centerton processor.
          The Intel SCH contains a total of 14 GPIO pins. Ten GPIOs are
          powered by the core power rail and are turned off during sleep
          modes (S3 and higher). The remaining four GPIOs are powered by
@@ -195,6 +211,22 @@ config GPIO_SCH
          system from the Suspend-to-RAM state.
          The Intel Tunnel Creek processor has 5 GPIOs powered by the
          core power rail and 9 from suspend power supply.
+         The Intel Centerton processor has a total of 30 GPIO pins.
+         Twenty-one are powered by the core power rail and 9 from the
+         suspend power supply.
+
+config GPIO_ICH
+       tristate "Intel ICH GPIO"
+       depends on PCI && X86
+       select MFD_CORE
+       select LPC_ICH
+       help
+         Say yes here to support the GPIO functionality of a number of Intel
+         ICH-based chipsets.  Currently supported devices: ICH6, ICH7, ICH8
+         ICH9, ICH10, Series 5/3400 (eg Ibex Peak), Series 6/C200 (eg
+         Cougar Point), NM10 (Tiger Point), and 3100 (Whitmore Lake).
+
+         If unsure, say N.
 
 config GPIO_VX855
        tristate "VIA VX855/VX875 GPIO"
@@ -334,6 +366,16 @@ config GPIO_STMPE
          This enables support for the GPIOs found on the STMPE I/O
          Expanders.
 
+config GPIO_STP_XWAY
+       bool "XWAY STP GPIOs"
+       depends on SOC_XWAY
+       help
+         This enables support for the Serial To Parallel (STP) unit found on
+         XWAY SoC. The STP allows the SoC to drive a shift registers cascade,
+         that can be up to 24 bit. This peripheral is aimed at driving leds.
+         Some of the gpios/leds can be auto updated by the soc with dsl and
+         phy status.
+
 config GPIO_TC3589X
        bool "TC3589X GPIOs"
        depends on MFD_TC3589X
index 07a79e245407ea23d9210aecdb2af24481cfd8b5..0f55662002c357c4d61c673b81655e4c74844742 100644 (file)
@@ -19,6 +19,7 @@ obj-$(CONFIG_ARCH_DAVINCI)    += gpio-davinci.o
 obj-$(CONFIG_GPIO_EM)          += gpio-em.o
 obj-$(CONFIG_GPIO_EP93XX)      += gpio-ep93xx.o
 obj-$(CONFIG_GPIO_GE_FPGA)     += gpio-ge.o
+obj-$(CONFIG_GPIO_ICH)         += gpio-ich.o
 obj-$(CONFIG_GPIO_IT8761E)     += gpio-it8761e.o
 obj-$(CONFIG_GPIO_JANZ_TTL)    += gpio-janz-ttl.o
 obj-$(CONFIG_ARCH_KS8695)      += gpio-ks8695.o
@@ -32,6 +33,7 @@ obj-$(CONFIG_GPIO_MC33880)    += gpio-mc33880.o
 obj-$(CONFIG_GPIO_MC9S08DZ60)  += gpio-mc9s08dz60.o
 obj-$(CONFIG_GPIO_MCP23S08)    += gpio-mcp23s08.o
 obj-$(CONFIG_GPIO_ML_IOH)      += gpio-ml-ioh.o
+obj-$(CONFIG_GPIO_MM_LANTIQ)   += gpio-mm-lantiq.o
 obj-$(CONFIG_GPIO_MPC5200)     += gpio-mpc5200.o
 obj-$(CONFIG_GPIO_MPC8XXX)     += gpio-mpc8xxx.o
 obj-$(CONFIG_GPIO_MSIC)                += gpio-msic.o
@@ -51,7 +53,9 @@ obj-$(CONFIG_PLAT_SAMSUNG)    += gpio-samsung.o
 obj-$(CONFIG_ARCH_SA1100)      += gpio-sa1100.o
 obj-$(CONFIG_GPIO_SCH)         += gpio-sch.o
 obj-$(CONFIG_GPIO_SODAVILLE)   += gpio-sodaville.o
+obj-$(CONFIG_GPIO_STA2X11)     += gpio-sta2x11.o
 obj-$(CONFIG_GPIO_STMPE)       += gpio-stmpe.o
+obj-$(CONFIG_GPIO_STP_XWAY)    += gpio-stp-xway.o
 obj-$(CONFIG_GPIO_SX150X)      += gpio-sx150x.o
 obj-$(CONFIG_GPIO_TC3589X)     += gpio-tc3589x.o
 obj-$(CONFIG_ARCH_TEGRA)       += gpio-tegra.o
diff --git a/drivers/gpio/gpio-ich.c b/drivers/gpio/gpio-ich.c
new file mode 100644 (file)
index 0000000..b7c0651
--- /dev/null
@@ -0,0 +1,419 @@
+/*
+ * Intel ICH6-10, Series 5 and 6 GPIO driver
+ *
+ * Copyright (C) 2010 Extreme Engineering Solutions.
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/gpio.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/lpc_ich.h>
+
+#define DRV_NAME "gpio_ich"
+
+/*
+ * GPIO register offsets in GPIO I/O space.
+ * Each chunk of 32 GPIOs is manipulated via its own USE_SELx, IO_SELx, and
+ * LVLx registers.  Logic in the read/write functions takes a register and
+ * an absolute bit number and determines the proper register offset and bit
+ * number in that register.  For example, to read the value of GPIO bit 50
+ * the code would access offset ichx_regs[2(=GPIO_LVL)][1(=50/32)],
+ * bit 18 (50%32).
+ */
+enum GPIO_REG {
+       GPIO_USE_SEL = 0,
+       GPIO_IO_SEL,
+       GPIO_LVL,
+};
+
+static const u8 ichx_regs[3][3] = {
+       {0x00, 0x30, 0x40},     /* USE_SEL[1-3] offsets */
+       {0x04, 0x34, 0x44},     /* IO_SEL[1-3] offsets */
+       {0x0c, 0x38, 0x48},     /* LVL[1-3] offsets */
+};
+
+#define ICHX_WRITE(val, reg, base_res) outl(val, (reg) + (base_res)->start)
+#define ICHX_READ(reg, base_res)       inl((reg) + (base_res)->start)
+
+struct ichx_desc {
+       /* Max GPIO pins the chipset can have */
+       uint ngpio;
+
+       /* Whether the chipset has GPIO in GPE0_STS in the PM IO region */
+       bool uses_gpe0;
+
+       /* USE_SEL is bogus on some chipsets, eg 3100 */
+       u32 use_sel_ignore[3];
+
+       /* Some chipsets have quirks, let these use their own request/get */
+       int (*request)(struct gpio_chip *chip, unsigned offset);
+       int (*get)(struct gpio_chip *chip, unsigned offset);
+};
+
+static struct {
+       spinlock_t lock;
+       struct platform_device *dev;
+       struct gpio_chip chip;
+       struct resource *gpio_base;     /* GPIO IO base */
+       struct resource *pm_base;       /* Power Mangagment IO base */
+       struct ichx_desc *desc; /* Pointer to chipset-specific description */
+       u32 orig_gpio_ctrl;     /* Orig CTRL value, used to restore on exit */
+} ichx_priv;
+
+static int modparam_gpiobase = -1;     /* dynamic */
+module_param_named(gpiobase, modparam_gpiobase, int, 0444);
+MODULE_PARM_DESC(gpiobase, "The GPIO number base. -1 means dynamic, "
+                          "which is the default.");
+
+static int ichx_write_bit(int reg, unsigned nr, int val, int verify)
+{
+       unsigned long flags;
+       u32 data, tmp;
+       int reg_nr = nr / 32;
+       int bit = nr & 0x1f;
+       int ret = 0;
+
+       spin_lock_irqsave(&ichx_priv.lock, flags);
+
+       data = ICHX_READ(ichx_regs[reg][reg_nr], ichx_priv.gpio_base);
+       if (val)
+               data |= 1 << bit;
+       else
+               data &= ~(1 << bit);
+       ICHX_WRITE(data, ichx_regs[reg][reg_nr], ichx_priv.gpio_base);
+       tmp = ICHX_READ(ichx_regs[reg][reg_nr], ichx_priv.gpio_base);
+       if (verify && data != tmp)
+               ret = -EPERM;
+
+       spin_unlock_irqrestore(&ichx_priv.lock, flags);
+
+       return ret;
+}
+
+static int ichx_read_bit(int reg, unsigned nr)
+{
+       unsigned long flags;
+       u32 data;
+       int reg_nr = nr / 32;
+       int bit = nr & 0x1f;
+
+       spin_lock_irqsave(&ichx_priv.lock, flags);
+
+       data = ICHX_READ(ichx_regs[reg][reg_nr], ichx_priv.gpio_base);
+
+       spin_unlock_irqrestore(&ichx_priv.lock, flags);
+
+       return data & (1 << bit) ? 1 : 0;
+}
+
+static int ichx_gpio_direction_input(struct gpio_chip *gpio, unsigned nr)
+{
+       /*
+        * Try setting pin as an input and verify it worked since many pins
+        * are output-only.
+        */
+       if (ichx_write_bit(GPIO_IO_SEL, nr, 1, 1))
+               return -EINVAL;
+
+       return 0;
+}
+
+static int ichx_gpio_direction_output(struct gpio_chip *gpio, unsigned nr,
+                                       int val)
+{
+       /* Set GPIO output value. */
+       ichx_write_bit(GPIO_LVL, nr, val, 0);
+
+       /*
+        * Try setting pin as an output and verify it worked since many pins
+        * are input-only.
+        */
+       if (ichx_write_bit(GPIO_IO_SEL, nr, 0, 1))
+               return -EINVAL;
+
+       return 0;
+}
+
+static int ichx_gpio_get(struct gpio_chip *chip, unsigned nr)
+{
+       return ichx_read_bit(GPIO_LVL, nr);
+}
+
+static int ich6_gpio_get(struct gpio_chip *chip, unsigned nr)
+{
+       unsigned long flags;
+       u32 data;
+
+       /*
+        * GPI 0 - 15 need to be read from the power management registers on
+        * a ICH6/3100 bridge.
+        */
+       if (nr < 16) {
+               if (!ichx_priv.pm_base)
+                       return -ENXIO;
+
+               spin_lock_irqsave(&ichx_priv.lock, flags);
+
+               /* GPI 0 - 15 are latched, write 1 to clear*/
+               ICHX_WRITE(1 << (16 + nr), 0, ichx_priv.pm_base);
+               data = ICHX_READ(0, ichx_priv.pm_base);
+
+               spin_unlock_irqrestore(&ichx_priv.lock, flags);
+
+               return (data >> 16) & (1 << nr) ? 1 : 0;
+       } else {
+               return ichx_gpio_get(chip, nr);
+       }
+}
+
+static int ichx_gpio_request(struct gpio_chip *chip, unsigned nr)
+{
+       /*
+        * Note we assume the BIOS properly set a bridge's USE value.  Some
+        * chips (eg Intel 3100) have bogus USE values though, so first see if
+        * the chipset's USE value can be trusted for this specific bit.
+        * If it can't be trusted, assume that the pin can be used as a GPIO.
+        */
+       if (ichx_priv.desc->use_sel_ignore[nr / 32] & (1 << (nr & 0x1f)))
+               return 1;
+
+       return ichx_read_bit(GPIO_USE_SEL, nr) ? 0 : -ENODEV;
+}
+
+static int ich6_gpio_request(struct gpio_chip *chip, unsigned nr)
+{
+       /*
+        * Fixups for bits 16 and 17 are necessary on the Intel ICH6/3100
+        * bridge as they are controlled by USE register bits 0 and 1.  See
+        * "Table 704 GPIO_USE_SEL1 register" in the i3100 datasheet for
+        * additional info.
+        */
+       if (nr == 16 || nr == 17)
+               nr -= 16;
+
+       return ichx_gpio_request(chip, nr);
+}
+
+static void ichx_gpio_set(struct gpio_chip *chip, unsigned nr, int val)
+{
+       ichx_write_bit(GPIO_LVL, nr, val, 0);
+}
+
+static void __devinit ichx_gpiolib_setup(struct gpio_chip *chip)
+{
+       chip->owner = THIS_MODULE;
+       chip->label = DRV_NAME;
+       chip->dev = &ichx_priv.dev->dev;
+
+       /* Allow chip-specific overrides of request()/get() */
+       chip->request = ichx_priv.desc->request ?
+               ichx_priv.desc->request : ichx_gpio_request;
+       chip->get = ichx_priv.desc->get ?
+               ichx_priv.desc->get : ichx_gpio_get;
+
+       chip->set = ichx_gpio_set;
+       chip->direction_input = ichx_gpio_direction_input;
+       chip->direction_output = ichx_gpio_direction_output;
+       chip->base = modparam_gpiobase;
+       chip->ngpio = ichx_priv.desc->ngpio;
+       chip->can_sleep = 0;
+       chip->dbg_show = NULL;
+}
+
+/* ICH6-based, 631xesb-based */
+static struct ichx_desc ich6_desc = {
+       /* Bridges using the ICH6 controller need fixups for GPIO 0 - 17 */
+       .request = ich6_gpio_request,
+       .get = ich6_gpio_get,
+
+       /* GPIO 0-15 are read in the GPE0_STS PM register */
+       .uses_gpe0 = true,
+
+       .ngpio = 50,
+};
+
+/* Intel 3100 */
+static struct ichx_desc i3100_desc = {
+       /*
+        * Bits 16,17, 20 of USE_SEL and bit 16 of USE_SEL2 always read 0 on
+        * the Intel 3100.  See "Table 712. GPIO Summary Table" of 3100
+        * Datasheet for more info.
+        */
+       .use_sel_ignore = {0x00130000, 0x00010000, 0x0},
+
+       /* The 3100 needs fixups for GPIO 0 - 17 */
+       .request = ich6_gpio_request,
+       .get = ich6_gpio_get,
+
+       /* GPIO 0-15 are read in the GPE0_STS PM register */
+       .uses_gpe0 = true,
+
+       .ngpio = 50,
+};
+
+/* ICH7 and ICH8-based */
+static struct ichx_desc ich7_desc = {
+       .ngpio = 50,
+};
+
+/* ICH9-based */
+static struct ichx_desc ich9_desc = {
+       .ngpio = 61,
+};
+
+/* ICH10-based - Consumer/corporate versions have different amount of GPIO */
+static struct ichx_desc ich10_cons_desc = {
+       .ngpio = 61,
+};
+static struct ichx_desc ich10_corp_desc = {
+       .ngpio = 72,
+};
+
+/* Intel 5 series, 6 series, 3400 series, and C200 series */
+static struct ichx_desc intel5_desc = {
+       .ngpio = 76,
+};
+
+static int __devinit ichx_gpio_probe(struct platform_device *pdev)
+{
+       struct resource *res_base, *res_pm;
+       int err;
+       struct lpc_ich_info *ich_info = pdev->dev.platform_data;
+
+       if (!ich_info)
+               return -ENODEV;
+
+       ichx_priv.dev = pdev;
+
+       switch (ich_info->gpio_version) {
+       case ICH_I3100_GPIO:
+               ichx_priv.desc = &i3100_desc;
+               break;
+       case ICH_V5_GPIO:
+               ichx_priv.desc = &intel5_desc;
+               break;
+       case ICH_V6_GPIO:
+               ichx_priv.desc = &ich6_desc;
+               break;
+       case ICH_V7_GPIO:
+               ichx_priv.desc = &ich7_desc;
+               break;
+       case ICH_V9_GPIO:
+               ichx_priv.desc = &ich9_desc;
+               break;
+       case ICH_V10CORP_GPIO:
+               ichx_priv.desc = &ich10_corp_desc;
+               break;
+       case ICH_V10CONS_GPIO:
+               ichx_priv.desc = &ich10_cons_desc;
+               break;
+       default:
+               return -ENODEV;
+       }
+
+       res_base = platform_get_resource(pdev, IORESOURCE_IO, ICH_RES_GPIO);
+       if (!res_base || !res_base->start || !res_base->end)
+               return -ENODEV;
+
+       if (!request_region(res_base->start, resource_size(res_base),
+                               pdev->name))
+               return -EBUSY;
+
+       ichx_priv.gpio_base = res_base;
+
+       /*
+        * If necessary, determine the I/O address of ACPI/power management
+        * registers which are needed to read the the GPE0 register for GPI pins
+        * 0 - 15 on some chipsets.
+        */
+       if (!ichx_priv.desc->uses_gpe0)
+               goto init;
+
+       res_pm = platform_get_resource(pdev, IORESOURCE_IO, ICH_RES_GPE0);
+       if (!res_pm) {
+               pr_warn("ACPI BAR is unavailable, GPI 0 - 15 unavailable\n");
+               goto init;
+       }
+
+       if (!request_region(res_pm->start, resource_size(res_pm),
+                       pdev->name)) {
+               pr_warn("ACPI BAR is busy, GPI 0 - 15 unavailable\n");
+               goto init;
+       }
+
+       ichx_priv.pm_base = res_pm;
+
+init:
+       ichx_gpiolib_setup(&ichx_priv.chip);
+       err = gpiochip_add(&ichx_priv.chip);
+       if (err) {
+               pr_err("Failed to register GPIOs\n");
+               goto add_err;
+       }
+
+       pr_info("GPIO from %d to %d on %s\n", ichx_priv.chip.base,
+              ichx_priv.chip.base + ichx_priv.chip.ngpio - 1, DRV_NAME);
+
+       return 0;
+
+add_err:
+       release_region(ichx_priv.gpio_base->start,
+                       resource_size(ichx_priv.gpio_base));
+       if (ichx_priv.pm_base)
+               release_region(ichx_priv.pm_base->start,
+                               resource_size(ichx_priv.pm_base));
+       return err;
+}
+
+static int __devexit ichx_gpio_remove(struct platform_device *pdev)
+{
+       int err;
+
+       err = gpiochip_remove(&ichx_priv.chip);
+       if (err) {
+               dev_err(&pdev->dev, "%s failed, %d\n",
+                               "gpiochip_remove()", err);
+               return err;
+       }
+
+       release_region(ichx_priv.gpio_base->start,
+                               resource_size(ichx_priv.gpio_base));
+       if (ichx_priv.pm_base)
+               release_region(ichx_priv.pm_base->start,
+                               resource_size(ichx_priv.pm_base));
+
+       return 0;
+}
+
+static struct platform_driver ichx_gpio_driver = {
+       .driver         = {
+               .owner  = THIS_MODULE,
+               .name   = DRV_NAME,
+       },
+       .probe          = ichx_gpio_probe,
+       .remove         = __devexit_p(ichx_gpio_remove),
+};
+
+module_platform_driver(ichx_gpio_driver);
+
+MODULE_AUTHOR("Peter Tyser <ptyser@xes-inc.com>");
+MODULE_DESCRIPTION("GPIO interface for Intel ICH series");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:"DRV_NAME);
diff --git a/drivers/gpio/gpio-mm-lantiq.c b/drivers/gpio/gpio-mm-lantiq.c
new file mode 100644 (file)
index 0000000..2983dfb
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ *  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.
+ *
+ *  Copyright (C) 2012 John Crispin <blogic@openwrt.org>
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <linux/gpio.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+
+#include <lantiq_soc.h>
+
+/*
+ * By attaching hardware latches to the EBU it is possible to create output
+ * only gpios. This driver configures a special memory address, which when
+ * written to outputs 16 bit to the latches.
+ */
+
+#define LTQ_EBU_BUSCON 0x1e7ff         /* 16 bit access, slowest timing */
+#define LTQ_EBU_WP     0x80000000      /* write protect bit */
+
+struct ltq_mm {
+       struct of_mm_gpio_chip mmchip;
+       u16 shadow;     /* shadow the latches state */
+};
+
+/**
+ * ltq_mm_apply() - write the shadow value to the ebu address.
+ * @chip:     Pointer to our private data structure.
+ *
+ * Write the shadow value to the EBU to set the gpios. We need to set the
+ * global EBU lock to make sure that PCI/MTD dont break.
+ */
+static void ltq_mm_apply(struct ltq_mm *chip)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&ebu_lock, flags);
+       ltq_ebu_w32(LTQ_EBU_BUSCON, LTQ_EBU_BUSCON1);
+       __raw_writew(chip->shadow, chip->mmchip.regs);
+       ltq_ebu_w32(LTQ_EBU_BUSCON | LTQ_EBU_WP, LTQ_EBU_BUSCON1);
+       spin_unlock_irqrestore(&ebu_lock, flags);
+}
+
+/**
+ * ltq_mm_set() - gpio_chip->set - set gpios.
+ * @gc:     Pointer to gpio_chip device structure.
+ * @gpio:   GPIO signal number.
+ * @val:    Value to be written to specified signal.
+ *
+ * Set the shadow value and call ltq_mm_apply.
+ */
+static void ltq_mm_set(struct gpio_chip *gc, unsigned offset, int value)
+{
+       struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+       struct ltq_mm *chip =
+               container_of(mm_gc, struct ltq_mm, mmchip);
+
+       if (value)
+               chip->shadow |= (1 << offset);
+       else
+               chip->shadow &= ~(1 << offset);
+       ltq_mm_apply(chip);
+}
+
+/**
+ * ltq_mm_dir_out() - gpio_chip->dir_out - set gpio direction.
+ * @gc:     Pointer to gpio_chip device structure.
+ * @gpio:   GPIO signal number.
+ * @val:    Value to be written to specified signal.
+ *
+ * Same as ltq_mm_set, always returns 0.
+ */
+static int ltq_mm_dir_out(struct gpio_chip *gc, unsigned offset, int value)
+{
+       ltq_mm_set(gc, offset, value);
+
+       return 0;
+}
+
+/**
+ * ltq_mm_save_regs() - Set initial values of GPIO pins
+ * @mm_gc: pointer to memory mapped GPIO chip structure
+ */
+static void ltq_mm_save_regs(struct of_mm_gpio_chip *mm_gc)
+{
+       struct ltq_mm *chip =
+               container_of(mm_gc, struct ltq_mm, mmchip);
+
+       /* tell the ebu controller which memory address we will be using */
+       ltq_ebu_w32(CPHYSADDR(chip->mmchip.regs) | 0x1, LTQ_EBU_ADDRSEL1);
+
+       ltq_mm_apply(chip);
+}
+
+static int ltq_mm_probe(struct platform_device *pdev)
+{
+       struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       struct ltq_mm *chip;
+       const __be32 *shadow;
+       int ret = 0;
+
+       if (!res) {
+               dev_err(&pdev->dev, "failed to get memory resource\n");
+               return -ENOENT;
+       }
+
+       chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+       if (!chip)
+               return -ENOMEM;
+
+       chip->mmchip.gc.ngpio = 16;
+       chip->mmchip.gc.label = "gpio-mm-ltq";
+       chip->mmchip.gc.direction_output = ltq_mm_dir_out;
+       chip->mmchip.gc.set = ltq_mm_set;
+       chip->mmchip.save_regs = ltq_mm_save_regs;
+
+       /* store the shadow value if one was passed by the devicetree */
+       shadow = of_get_property(pdev->dev.of_node, "lantiq,shadow", NULL);
+       if (shadow)
+               chip->shadow = be32_to_cpu(*shadow);
+
+       ret = of_mm_gpiochip_add(pdev->dev.of_node, &chip->mmchip);
+       if (ret)
+               kfree(chip);
+       return ret;
+}
+
+static const struct of_device_id ltq_mm_match[] = {
+       { .compatible = "lantiq,gpio-mm" },
+       {},
+};
+MODULE_DEVICE_TABLE(of, ltq_mm_match);
+
+static struct platform_driver ltq_mm_driver = {
+       .probe = ltq_mm_probe,
+       .driver = {
+               .name = "gpio-mm-ltq",
+               .owner = THIS_MODULE,
+               .of_match_table = ltq_mm_match,
+       },
+};
+
+static int __init ltq_mm_init(void)
+{
+       return platform_driver_register(&ltq_mm_driver);
+}
+
+subsys_initcall(ltq_mm_init);
index 8cadf4d683a822e8e21c26cfe47871690fb62a75..424dce8e3f30107ce2e9b5eb9b5d2bb394164cf9 100644 (file)
@@ -232,6 +232,14 @@ static int __devinit sch_gpio_probe(struct platform_device *pdev)
                        sch_gpio_resume.ngpio = 9;
                        break;
 
+               case PCI_DEVICE_ID_INTEL_CENTERTON_ILB:
+                       sch_gpio_core.base = 0;
+                       sch_gpio_core.ngpio = 21;
+
+                       sch_gpio_resume.base = 21;
+                       sch_gpio_resume.ngpio = 9;
+                       break;
+
                default:
                        return -ENODEV;
        }
diff --git a/drivers/gpio/gpio-sta2x11.c b/drivers/gpio/gpio-sta2x11.c
new file mode 100644 (file)
index 0000000..38416be
--- /dev/null
@@ -0,0 +1,435 @@
+/*
+ * STMicroelectronics ConneXt (STA2X11) GPIO driver
+ *
+ * Copyright 2012 ST Microelectronics (Alessandro Rubini)
+ * Based on gpio-ml-ioh.c, Copyright 2010 OKI Semiconductors Ltd.
+ * Also based on previous sta2x11 work, Copyright 2011 Wind River Systems, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/sta2x11-mfd.h>
+
+struct gsta_regs {
+       u32 dat;                /* 0x00 */
+       u32 dats;
+       u32 datc;
+       u32 pdis;
+       u32 dir;                /* 0x10 */
+       u32 dirs;
+       u32 dirc;
+       u32 unused_1c;
+       u32 afsela;             /* 0x20 */
+       u32 unused_24[7];
+       u32 rimsc;              /* 0x40 */
+       u32 fimsc;
+       u32 is;
+       u32 ic;
+};
+
+struct gsta_gpio {
+       spinlock_t                      lock;
+       struct device                   *dev;
+       void __iomem                    *reg_base;
+       struct gsta_regs __iomem        *regs[GSTA_NR_BLOCKS];
+       struct gpio_chip                gpio;
+       int                             irq_base;
+       /* FIXME: save the whole config here (AF, ...) */
+       unsigned                        irq_type[GSTA_NR_GPIO];
+};
+
+static inline struct gsta_regs __iomem *__regs(struct gsta_gpio *chip, int nr)
+{
+       return chip->regs[nr / GSTA_GPIO_PER_BLOCK];
+}
+
+static inline u32 __bit(int nr)
+{
+       return 1U << (nr % GSTA_GPIO_PER_BLOCK);
+}
+
+/*
+ * gpio methods
+ */
+
+static void gsta_gpio_set(struct gpio_chip *gpio, unsigned nr, int val)
+{
+       struct gsta_gpio *chip = container_of(gpio, struct gsta_gpio, gpio);
+       struct gsta_regs __iomem *regs = __regs(chip, nr);
+       u32 bit = __bit(nr);
+
+       if (val)
+               writel(bit, &regs->dats);
+       else
+               writel(bit, &regs->datc);
+}
+
+static int gsta_gpio_get(struct gpio_chip *gpio, unsigned nr)
+{
+       struct gsta_gpio *chip = container_of(gpio, struct gsta_gpio, gpio);
+       struct gsta_regs __iomem *regs = __regs(chip, nr);
+       u32 bit = __bit(nr);
+
+       return readl(&regs->dat) & bit;
+}
+
+static int gsta_gpio_direction_output(struct gpio_chip *gpio, unsigned nr,
+                                     int val)
+{
+       struct gsta_gpio *chip = container_of(gpio, struct gsta_gpio, gpio);
+       struct gsta_regs __iomem *regs = __regs(chip, nr);
+       u32 bit = __bit(nr);
+
+       writel(bit, &regs->dirs);
+       /* Data register after direction, otherwise pullup/down is selected */
+       if (val)
+               writel(bit, &regs->dats);
+       else
+               writel(bit, &regs->datc);
+       return 0;
+}
+
+static int gsta_gpio_direction_input(struct gpio_chip *gpio, unsigned nr)
+{
+       struct gsta_gpio *chip = container_of(gpio, struct gsta_gpio, gpio);
+       struct gsta_regs __iomem *regs = __regs(chip, nr);
+       u32 bit = __bit(nr);
+
+       writel(bit, &regs->dirc);
+       return 0;
+}
+
+static int gsta_gpio_to_irq(struct gpio_chip *gpio, unsigned offset)
+{
+       struct gsta_gpio *chip = container_of(gpio, struct gsta_gpio, gpio);
+       return chip->irq_base + offset;
+}
+
+static void gsta_gpio_setup(struct gsta_gpio *chip) /* called from probe */
+{
+       struct gpio_chip *gpio = &chip->gpio;
+
+       /*
+        * ARCH_NR_GPIOS is currently 256 and dynamic allocation starts
+        * from the end. However, for compatibility, we need the first
+        * ConneXt device to start from gpio 0: it's the main chipset
+        * on most boards so documents and drivers assume gpio0..gpio127
+        */
+       static int gpio_base;
+
+       gpio->label = dev_name(chip->dev);
+       gpio->owner = THIS_MODULE;
+       gpio->direction_input = gsta_gpio_direction_input;
+       gpio->get = gsta_gpio_get;
+       gpio->direction_output = gsta_gpio_direction_output;
+       gpio->set = gsta_gpio_set;
+       gpio->dbg_show = NULL;
+       gpio->base = gpio_base;
+       gpio->ngpio = GSTA_NR_GPIO;
+       gpio->can_sleep = 0;
+       gpio->to_irq = gsta_gpio_to_irq;
+
+       /*
+        * After the first device, turn to dynamic gpio numbers.
+        * For example, with ARCH_NR_GPIOS = 256 we can fit two cards
+        */
+       if (!gpio_base)
+               gpio_base = -1;
+}
+
+/*
+ * Special method: alternate functions and pullup/pulldown. This is only
+ * invoked on startup to configure gpio's according to platform data.
+ * FIXME : this functionality shall be managed (and exported to other drivers)
+ * via the pin control subsystem.
+ */
+static void gsta_set_config(struct gsta_gpio *chip, int nr, unsigned cfg)
+{
+       struct gsta_regs __iomem *regs = __regs(chip, nr);
+       unsigned long flags;
+       u32 bit = __bit(nr);
+       u32 val;
+       int err = 0;
+
+       pr_info("%s: %p %i %i\n", __func__, chip, nr, cfg);
+
+       if (cfg == PINMUX_TYPE_NONE)
+               return;
+
+       /* Alternate function or not? */
+       spin_lock_irqsave(&chip->lock, flags);
+       val = readl(&regs->afsela);
+       if (cfg == PINMUX_TYPE_FUNCTION)
+               val |= bit;
+       else
+               val &= ~bit;
+       writel(val | bit, &regs->afsela);
+       if (cfg == PINMUX_TYPE_FUNCTION) {
+               spin_unlock_irqrestore(&chip->lock, flags);
+               return;
+       }
+
+       /* not alternate function: set details */
+       switch (cfg) {
+       case PINMUX_TYPE_OUTPUT_LOW:
+               writel(bit, &regs->dirs);
+               writel(bit, &regs->datc);
+               break;
+       case PINMUX_TYPE_OUTPUT_HIGH:
+               writel(bit, &regs->dirs);
+               writel(bit, &regs->dats);
+               break;
+       case PINMUX_TYPE_INPUT:
+               writel(bit, &regs->dirc);
+               val = readl(&regs->pdis) | bit;
+               writel(val, &regs->pdis);
+               break;
+       case PINMUX_TYPE_INPUT_PULLUP:
+               writel(bit, &regs->dirc);
+               val = readl(&regs->pdis) & ~bit;
+               writel(val, &regs->pdis);
+               writel(bit, &regs->dats);
+               break;
+       case PINMUX_TYPE_INPUT_PULLDOWN:
+               writel(bit, &regs->dirc);
+               val = readl(&regs->pdis) & ~bit;
+               writel(val, &regs->pdis);
+               writel(bit, &regs->datc);
+               break;
+       default:
+               err = 1;
+       }
+       spin_unlock_irqrestore(&chip->lock, flags);
+       if (err)
+               pr_err("%s: chip %p, pin %i, cfg %i is invalid\n",
+                      __func__, chip, nr, cfg);
+}
+
+/*
+ * Irq methods
+ */
+
+static void gsta_irq_disable(struct irq_data *data)
+{
+       struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data);
+       struct gsta_gpio *chip = gc->private;
+       int nr = data->irq - chip->irq_base;
+       struct gsta_regs __iomem *regs = __regs(chip, nr);
+       u32 bit = __bit(nr);
+       u32 val;
+       unsigned long flags;
+
+       spin_lock_irqsave(&chip->lock, flags);
+       if (chip->irq_type[nr] & IRQ_TYPE_EDGE_RISING) {
+               val = readl(&regs->rimsc) & ~bit;
+               writel(val, &regs->rimsc);
+       }
+       if (chip->irq_type[nr] & IRQ_TYPE_EDGE_FALLING) {
+               val = readl(&regs->fimsc) & ~bit;
+               writel(val, &regs->fimsc);
+       }
+       spin_unlock_irqrestore(&chip->lock, flags);
+       return;
+}
+
+static void gsta_irq_enable(struct irq_data *data)
+{
+       struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data);
+       struct gsta_gpio *chip = gc->private;
+       int nr = data->irq - chip->irq_base;
+       struct gsta_regs __iomem *regs = __regs(chip, nr);
+       u32 bit = __bit(nr);
+       u32 val;
+       int type;
+       unsigned long flags;
+
+       type = chip->irq_type[nr];
+
+       spin_lock_irqsave(&chip->lock, flags);
+       val = readl(&regs->rimsc);
+       if (type & IRQ_TYPE_EDGE_RISING)
+               writel(val | bit, &regs->rimsc);
+       else
+               writel(val & ~bit, &regs->rimsc);
+       val = readl(&regs->rimsc);
+       if (type & IRQ_TYPE_EDGE_FALLING)
+               writel(val | bit, &regs->fimsc);
+       else
+               writel(val & ~bit, &regs->fimsc);
+       spin_unlock_irqrestore(&chip->lock, flags);
+       return;
+}
+
+static int gsta_irq_type(struct irq_data *d, unsigned int type)
+{
+       struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+       struct gsta_gpio *chip = gc->private;
+       int nr = d->irq - chip->irq_base;
+
+       /* We only support edge interrupts */
+       if (!(type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING))) {
+               pr_debug("%s: unsupported type 0x%x\n", __func__, type);
+               return -EINVAL;
+       }
+
+       chip->irq_type[nr] = type; /* used for enable/disable */
+
+       gsta_irq_enable(d);
+       return 0;
+}
+
+static irqreturn_t gsta_gpio_handler(int irq, void *dev_id)
+{
+       struct gsta_gpio *chip = dev_id;
+       struct gsta_regs __iomem *regs;
+       u32 is;
+       int i, nr, base;
+       irqreturn_t ret = IRQ_NONE;
+
+       for (i = 0; i < GSTA_NR_BLOCKS; i++) {
+               regs = chip->regs[i];
+               base = chip->irq_base + i * GSTA_GPIO_PER_BLOCK;
+               while ((is = readl(&regs->is))) {
+                       nr = __ffs(is);
+                       irq = base + nr;
+                       generic_handle_irq(irq);
+                       writel(1 << nr, &regs->ic);
+                       ret = IRQ_HANDLED;
+               }
+       }
+       return ret;
+}
+
+static __devinit void gsta_alloc_irq_chip(struct gsta_gpio *chip)
+{
+       struct irq_chip_generic *gc;
+       struct irq_chip_type *ct;
+
+       gc = irq_alloc_generic_chip(KBUILD_MODNAME, 1, chip->irq_base,
+                                    chip->reg_base, handle_simple_irq);
+       gc->private = chip;
+       ct = gc->chip_types;
+
+       ct->chip.irq_set_type = gsta_irq_type;
+       ct->chip.irq_disable = gsta_irq_disable;
+       ct->chip.irq_enable = gsta_irq_enable;
+
+       /* FIXME: this makes at most 32 interrupts. Request 0 by now */
+       irq_setup_generic_chip(gc, 0 /* IRQ_MSK(GSTA_GPIO_PER_BLOCK) */, 0,
+                              IRQ_NOREQUEST | IRQ_NOPROBE, 0);
+
+       /* Set up all all 128 interrupts: code from setup_generic_chip */
+       {
+               struct irq_chip_type *ct = gc->chip_types;
+               int i, j;
+               for (j = 0; j < GSTA_NR_GPIO; j++) {
+                       i = chip->irq_base + j;
+                       irq_set_chip_and_handler(i, &ct->chip, ct->handler);
+                       irq_set_chip_data(i, gc);
+                       irq_modify_status(i, IRQ_NOREQUEST | IRQ_NOPROBE, 0);
+               }
+               gc->irq_cnt = i - gc->irq_base;
+       }
+}
+
+/* The platform device used here is instantiated by the MFD device */
+static int __devinit gsta_probe(struct platform_device *dev)
+{
+       int i, err;
+       struct pci_dev *pdev;
+       struct sta2x11_gpio_pdata *gpio_pdata;
+       struct gsta_gpio *chip;
+       struct resource *res;
+
+       pdev = *(struct pci_dev **)(dev->dev.platform_data);
+       gpio_pdata = dev_get_platdata(&pdev->dev);
+
+       if (gpio_pdata == NULL)
+               dev_err(&dev->dev, "no gpio config\n");
+       pr_debug("gpio config: %p\n", gpio_pdata);
+
+       res = platform_get_resource(dev, IORESOURCE_MEM, 0);
+
+       chip = devm_kzalloc(&dev->dev, sizeof(*chip), GFP_KERNEL);
+       chip->dev = &dev->dev;
+       chip->reg_base = devm_request_and_ioremap(&dev->dev, res);
+
+       for (i = 0; i < GSTA_NR_BLOCKS; i++) {
+               chip->regs[i] = chip->reg_base + i * 4096;
+               /* disable all irqs */
+               writel(0, &chip->regs[i]->rimsc);
+               writel(0, &chip->regs[i]->fimsc);
+               writel(~0, &chip->regs[i]->ic);
+       }
+       spin_lock_init(&chip->lock);
+       gsta_gpio_setup(chip);
+       for (i = 0; i < GSTA_NR_GPIO; i++)
+               gsta_set_config(chip, i, gpio_pdata->pinconfig[i]);
+
+       /* 384 was used in previous code: be compatible for other drivers */
+       err = irq_alloc_descs(-1, 384, GSTA_NR_GPIO, NUMA_NO_NODE);
+       if (err < 0) {
+               dev_warn(&dev->dev, "sta2x11 gpio: Can't get irq base (%i)\n",
+                        -err);
+               return err;
+       }
+       chip->irq_base = err;
+       gsta_alloc_irq_chip(chip);
+
+       err = request_irq(pdev->irq, gsta_gpio_handler,
+                            IRQF_SHARED, KBUILD_MODNAME, chip);
+       if (err < 0) {
+               dev_err(&dev->dev, "sta2x11 gpio: Can't request irq (%i)\n",
+                       -err);
+               goto err_free_descs;
+       }
+
+       err = gpiochip_add(&chip->gpio);
+       if (err < 0) {
+               dev_err(&dev->dev, "sta2x11 gpio: Can't register (%i)\n",
+                       -err);
+               goto err_free_irq;
+       }
+
+       platform_set_drvdata(dev, chip);
+       return 0;
+
+err_free_irq:
+       free_irq(pdev->irq, chip);
+err_free_descs:
+       irq_free_descs(chip->irq_base, GSTA_NR_GPIO);
+       return err;
+}
+
+static struct platform_driver sta2x11_gpio_platform_driver = {
+       .driver = {
+               .name   = "sta2x11-gpio",
+               .owner  = THIS_MODULE,
+       },
+       .probe = gsta_probe,
+};
+
+module_platform_driver(sta2x11_gpio_platform_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("sta2x11_gpio GPIO driver");
diff --git a/drivers/gpio/gpio-stp-xway.c b/drivers/gpio/gpio-stp-xway.c
new file mode 100644 (file)
index 0000000..e35096b
--- /dev/null
@@ -0,0 +1,301 @@
+/*
+ *  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.
+ *
+ *  Copyright (C) 2012 John Crispin <blogic@openwrt.org>
+ *
+ */
+
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/of_platform.h>
+#include <linux/mutex.h>
+#include <linux/gpio.h>
+#include <linux/io.h>
+#include <linux/of_gpio.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+
+#include <lantiq_soc.h>
+
+/*
+ * The Serial To Parallel (STP) is found on MIPS based Lantiq socs. It is a
+ * peripheral controller used to drive external shift register cascades. At most
+ * 3 groups of 8 bits can be driven. The hardware is able to allow the DSL modem
+ * to drive the 2 LSBs of the cascade automatically.
+ */
+
+/* control register 0 */
+#define XWAY_STP_CON0          0x00
+/* control register 1 */
+#define XWAY_STP_CON1          0x04
+/* data register 0 */
+#define XWAY_STP_CPU0          0x08
+/* data register 1 */
+#define XWAY_STP_CPU1          0x0C
+/* access register */
+#define XWAY_STP_AR            0x10
+
+/* software or hardware update select bit */
+#define XWAY_STP_CON_SWU       BIT(31)
+
+/* automatic update rates */
+#define XWAY_STP_2HZ           0
+#define XWAY_STP_4HZ           BIT(23)
+#define XWAY_STP_8HZ           BIT(24)
+#define XWAY_STP_10HZ          (BIT(24) | BIT(23))
+#define XWAY_STP_SPEED_MASK    (0xf << 23)
+
+/* clock source for automatic update */
+#define XWAY_STP_UPD_FPI       BIT(31)
+#define XWAY_STP_UPD_MASK      (BIT(31) | BIT(30))
+
+/* let the adsl core drive the 2 LSBs */
+#define XWAY_STP_ADSL_SHIFT    24
+#define XWAY_STP_ADSL_MASK     0x3
+
+/* 2 groups of 3 bits can be driven by the phys */
+#define XWAY_STP_PHY_MASK      0x3
+#define XWAY_STP_PHY1_SHIFT    27
+#define XWAY_STP_PHY2_SHIFT    15
+
+/* STP has 3 groups of 8 bits */
+#define XWAY_STP_GROUP0                BIT(0)
+#define XWAY_STP_GROUP1                BIT(1)
+#define XWAY_STP_GROUP2                BIT(2)
+#define XWAY_STP_GROUP_MASK    (0x7)
+
+/* Edge configuration bits */
+#define XWAY_STP_FALLING       BIT(26)
+#define XWAY_STP_EDGE_MASK     BIT(26)
+
+#define xway_stp_r32(m, reg)           __raw_readl(m + reg)
+#define xway_stp_w32(m, val, reg)      __raw_writel(val, m + reg)
+#define xway_stp_w32_mask(m, clear, set, reg) \
+               ltq_w32((ltq_r32(m + reg) & ~(clear)) | (set), \
+               m + reg)
+
+struct xway_stp {
+       struct gpio_chip gc;
+       void __iomem *virt;
+       u32 edge;       /* rising or falling edge triggered shift register */
+       u16 shadow;     /* shadow the shift registers state */
+       u8 groups;      /* we can drive 1-3 groups of 8bit each */
+       u8 dsl;         /* the 2 LSBs can be driven by the dsl core */
+       u8 phy1;        /* 3 bits can be driven by phy1 */
+       u8 phy2;        /* 3 bits can be driven by phy2 */
+       u8 reserved;    /* mask out the hw driven bits in gpio_request */
+};
+
+/**
+ * xway_stp_set() - gpio_chip->set - set gpios.
+ * @gc:     Pointer to gpio_chip device structure.
+ * @gpio:   GPIO signal number.
+ * @val:    Value to be written to specified signal.
+ *
+ * Set the shadow value and call ltq_ebu_apply.
+ */
+static void xway_stp_set(struct gpio_chip *gc, unsigned gpio, int val)
+{
+       struct xway_stp *chip =
+               container_of(gc, struct xway_stp, gc);
+
+       if (val)
+               chip->shadow |= BIT(gpio);
+       else
+               chip->shadow &= ~BIT(gpio);
+       xway_stp_w32(chip->virt, chip->shadow, XWAY_STP_CPU0);
+       xway_stp_w32_mask(chip->virt, 0, XWAY_STP_CON_SWU, XWAY_STP_CON0);
+}
+
+/**
+ * xway_stp_dir_out() - gpio_chip->dir_out - set gpio direction.
+ * @gc:     Pointer to gpio_chip device structure.
+ * @gpio:   GPIO signal number.
+ * @val:    Value to be written to specified signal.
+ *
+ * Same as xway_stp_set, always returns 0.
+ */
+static int xway_stp_dir_out(struct gpio_chip *gc, unsigned gpio, int val)
+{
+       xway_stp_set(gc, gpio, val);
+
+       return 0;
+}
+
+/**
+ * xway_stp_request() - gpio_chip->request
+ * @gc:     Pointer to gpio_chip device structure.
+ * @gpio:   GPIO signal number.
+ *
+ * We mask out the HW driven pins
+ */
+static int xway_stp_request(struct gpio_chip *gc, unsigned gpio)
+{
+       struct xway_stp *chip =
+               container_of(gc, struct xway_stp, gc);
+
+       if ((gpio < 8) && (chip->reserved & BIT(gpio))) {
+               dev_err(gc->dev, "GPIO %d is driven by hardware\n", gpio);
+               return -ENODEV;
+       }
+
+       return 0;
+}
+
+/**
+ * xway_stp_hw_init() - Configure the STP unit and enable the clock gate
+ * @virt: pointer to the remapped register range
+ */
+static int xway_stp_hw_init(struct xway_stp *chip)
+{
+       /* sane defaults */
+       xway_stp_w32(chip->virt, 0, XWAY_STP_AR);
+       xway_stp_w32(chip->virt, 0, XWAY_STP_CPU0);
+       xway_stp_w32(chip->virt, 0, XWAY_STP_CPU1);
+       xway_stp_w32(chip->virt, XWAY_STP_CON_SWU, XWAY_STP_CON0);
+       xway_stp_w32(chip->virt, 0, XWAY_STP_CON1);
+
+       /* apply edge trigger settings for the shift register */
+       xway_stp_w32_mask(chip->virt, XWAY_STP_EDGE_MASK,
+                               chip->edge, XWAY_STP_CON0);
+
+       /* apply led group settings */
+       xway_stp_w32_mask(chip->virt, XWAY_STP_GROUP_MASK,
+                               chip->groups, XWAY_STP_CON1);
+
+       /* tell the hardware which pins are controlled by the dsl modem */
+       xway_stp_w32_mask(chip->virt,
+                       XWAY_STP_ADSL_MASK << XWAY_STP_ADSL_SHIFT,
+                       chip->dsl << XWAY_STP_ADSL_SHIFT,
+                       XWAY_STP_CON0);
+
+       /* tell the hardware which pins are controlled by the phys */
+       xway_stp_w32_mask(chip->virt,
+                       XWAY_STP_PHY_MASK << XWAY_STP_PHY1_SHIFT,
+                       chip->phy1 << XWAY_STP_PHY1_SHIFT,
+                       XWAY_STP_CON0);
+       xway_stp_w32_mask(chip->virt,
+                       XWAY_STP_PHY_MASK << XWAY_STP_PHY2_SHIFT,
+                       chip->phy2 << XWAY_STP_PHY2_SHIFT,
+                       XWAY_STP_CON1);
+
+       /* mask out the hw driven bits in gpio_request */
+       chip->reserved = (chip->phy2 << 5) | (chip->phy1 << 2) | chip->dsl;
+
+       /*
+        * if we have pins that are driven by hw, we need to tell the stp what
+        * clock to use as a timer.
+        */
+       if (chip->reserved)
+               xway_stp_w32_mask(chip->virt, XWAY_STP_UPD_MASK,
+                       XWAY_STP_UPD_FPI, XWAY_STP_CON1);
+
+       return 0;
+}
+
+static int __devinit xway_stp_probe(struct platform_device *pdev)
+{
+       struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       const __be32 *shadow, *groups, *dsl, *phy;
+       struct xway_stp *chip;
+       struct clk *clk;
+       int ret = 0;
+
+       if (!res) {
+               dev_err(&pdev->dev, "failed to request STP resource\n");
+               return -ENOENT;
+       }
+
+       chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
+       if (!chip)
+               return -ENOMEM;
+
+       chip->virt = devm_request_and_ioremap(&pdev->dev, res);
+       if (!chip->virt) {
+               dev_err(&pdev->dev, "failed to remap STP memory\n");
+               return -ENOMEM;
+       }
+       chip->gc.dev = &pdev->dev;
+       chip->gc.label = "stp-xway";
+       chip->gc.direction_output = xway_stp_dir_out;
+       chip->gc.set = xway_stp_set;
+       chip->gc.request = xway_stp_request;
+       chip->gc.base = -1;
+       chip->gc.owner = THIS_MODULE;
+
+       /* store the shadow value if one was passed by the devicetree */
+       shadow = of_get_property(pdev->dev.of_node, "lantiq,shadow", NULL);
+       if (shadow)
+               chip->shadow = be32_to_cpu(*shadow);
+
+       /* find out which gpio groups should be enabled */
+       groups = of_get_property(pdev->dev.of_node, "lantiq,groups", NULL);
+       if (groups)
+               chip->groups = be32_to_cpu(*groups) & XWAY_STP_GROUP_MASK;
+       else
+               chip->groups = XWAY_STP_GROUP0;
+       chip->gc.ngpio = fls(chip->groups) * 8;
+
+       /* find out which gpios are controlled by the dsl core */
+       dsl = of_get_property(pdev->dev.of_node, "lantiq,dsl", NULL);
+       if (dsl)
+               chip->dsl = be32_to_cpu(*dsl) & XWAY_STP_ADSL_MASK;
+
+       /* find out which gpios are controlled by the phys */
+       if (of_machine_is_compatible("lantiq,ar9") ||
+                       of_machine_is_compatible("lantiq,gr9") ||
+                       of_machine_is_compatible("lantiq,vr9")) {
+               phy = of_get_property(pdev->dev.of_node, "lantiq,phy1", NULL);
+               if (phy)
+                       chip->phy1 = be32_to_cpu(*phy) & XWAY_STP_PHY_MASK;
+               phy = of_get_property(pdev->dev.of_node, "lantiq,phy2", NULL);
+               if (phy)
+                       chip->phy2 = be32_to_cpu(*phy) & XWAY_STP_PHY_MASK;
+       }
+
+       /* check which edge trigger we should use, default to a falling edge */
+       if (!of_find_property(pdev->dev.of_node, "lantiq,rising", NULL))
+               chip->edge = XWAY_STP_FALLING;
+
+       clk = clk_get(&pdev->dev, NULL);
+       if (IS_ERR(clk)) {
+               dev_err(&pdev->dev, "Failed to get clock\n");
+               return PTR_ERR(clk);
+       }
+       clk_enable(clk);
+
+       ret = xway_stp_hw_init(chip);
+       if (!ret)
+               ret = gpiochip_add(&chip->gc);
+
+       if (!ret)
+               dev_info(&pdev->dev, "Init done\n");
+
+       return ret;
+}
+
+static const struct of_device_id xway_stp_match[] = {
+       { .compatible = "lantiq,gpio-stp-xway" },
+       {},
+};
+MODULE_DEVICE_TABLE(of, xway_stp_match);
+
+static struct platform_driver xway_stp_driver = {
+       .probe = xway_stp_probe,
+       .driver = {
+               .name = "gpio-stp-xway",
+               .owner = THIS_MODULE,
+               .of_match_table = xway_stp_match,
+       },
+};
+
+int __init xway_stp_init(void)
+{
+       return platform_driver_register(&xway_stp_driver);
+}
+
+subsys_initcall(xway_stp_init);
index 7eef648a3351a2b89e1de03f95f168b8889f8634..c1ad2884f2edb0b79f3894a9a16662e5776bda51 100644 (file)
 #include <linux/errno.h>
 #include <linux/gpio.h>
 #include <linux/i2c.h>
+#include <linux/platform_device.h>
 #include <linux/mfd/tps65910.h>
+#include <linux/of_device.h>
+
+struct tps65910_gpio {
+       struct gpio_chip gpio_chip;
+       struct tps65910 *tps65910;
+};
+
+static inline struct tps65910_gpio *to_tps65910_gpio(struct gpio_chip *chip)
+{
+       return container_of(chip, struct tps65910_gpio, gpio_chip);
+}
 
 static int tps65910_gpio_get(struct gpio_chip *gc, unsigned offset)
 {
-       struct tps65910 *tps65910 = container_of(gc, struct tps65910, gpio);
-       uint8_t val;
+       struct tps65910_gpio *tps65910_gpio = to_tps65910_gpio(gc);
+       struct tps65910 *tps65910 = tps65910_gpio->tps65910;
+       unsigned int val;
 
-       tps65910->read(tps65910, TPS65910_GPIO0 + offset, 1, &val);
+       tps65910_reg_read(tps65910, TPS65910_GPIO0 + offset, &val);
 
        if (val & GPIO_STS_MASK)
                return 1;
@@ -36,83 +49,170 @@ static int tps65910_gpio_get(struct gpio_chip *gc, unsigned offset)
 static void tps65910_gpio_set(struct gpio_chip *gc, unsigned offset,
                              int value)
 {
-       struct tps65910 *tps65910 = container_of(gc, struct tps65910, gpio);
+       struct tps65910_gpio *tps65910_gpio = to_tps65910_gpio(gc);
+       struct tps65910 *tps65910 = tps65910_gpio->tps65910;
 
        if (value)
-               tps65910_set_bits(tps65910, TPS65910_GPIO0 + offset,
+               tps65910_reg_set_bits(tps65910, TPS65910_GPIO0 + offset,
                                                GPIO_SET_MASK);
        else
-               tps65910_clear_bits(tps65910, TPS65910_GPIO0 + offset,
+               tps65910_reg_clear_bits(tps65910, TPS65910_GPIO0 + offset,
                                                GPIO_SET_MASK);
 }
 
 static int tps65910_gpio_output(struct gpio_chip *gc, unsigned offset,
                                int value)
 {
-       struct tps65910 *tps65910 = container_of(gc, struct tps65910, gpio);
+       struct tps65910_gpio *tps65910_gpio = to_tps65910_gpio(gc);
+       struct tps65910 *tps65910 = tps65910_gpio->tps65910;
 
        /* Set the initial value */
        tps65910_gpio_set(gc, offset, value);
 
-       return tps65910_set_bits(tps65910, TPS65910_GPIO0 + offset,
+       return tps65910_reg_set_bits(tps65910, TPS65910_GPIO0 + offset,
                                                GPIO_CFG_MASK);
 }
 
 static int tps65910_gpio_input(struct gpio_chip *gc, unsigned offset)
 {
-       struct tps65910 *tps65910 = container_of(gc, struct tps65910, gpio);
+       struct tps65910_gpio *tps65910_gpio = to_tps65910_gpio(gc);
+       struct tps65910 *tps65910 = tps65910_gpio->tps65910;
 
-       return tps65910_clear_bits(tps65910, TPS65910_GPIO0 + offset,
+       return tps65910_reg_clear_bits(tps65910, TPS65910_GPIO0 + offset,
                                                GPIO_CFG_MASK);
 }
 
-void tps65910_gpio_init(struct tps65910 *tps65910, int gpio_base)
+#ifdef CONFIG_OF
+static struct tps65910_board *tps65910_parse_dt_for_gpio(struct device *dev,
+               struct tps65910 *tps65910, int chip_ngpio)
 {
+       struct tps65910_board *tps65910_board = tps65910->of_plat_data;
+       unsigned int prop_array[TPS6591X_MAX_NUM_GPIO];
+       int ngpio = min(chip_ngpio, TPS6591X_MAX_NUM_GPIO);
        int ret;
-       struct tps65910_board *board_data;
+       int idx;
+
+       tps65910_board->gpio_base = -1;
+       ret = of_property_read_u32_array(tps65910->dev->of_node,
+                       "ti,en-gpio-sleep", prop_array, ngpio);
+       if (ret < 0) {
+               dev_dbg(dev, "ti,en-gpio-sleep not specified\n");
+               return tps65910_board;
+       }
 
-       if (!gpio_base)
-               return;
+       for (idx = 0; idx < ngpio; idx++)
+               tps65910_board->en_gpio_sleep[idx] = (prop_array[idx] != 0);
 
-       tps65910->gpio.owner            = THIS_MODULE;
-       tps65910->gpio.label            = tps65910->i2c_client->name;
-       tps65910->gpio.dev              = tps65910->dev;
-       tps65910->gpio.base             = gpio_base;
+       return tps65910_board;
+}
+#else
+static struct tps65910_board *tps65910_parse_dt_for_gpio(struct device *dev,
+               struct tps65910 *tps65910, int chip_ngpio)
+{
+       return NULL;
+}
+#endif
+
+static int __devinit tps65910_gpio_probe(struct platform_device *pdev)
+{
+       struct tps65910 *tps65910 = dev_get_drvdata(pdev->dev.parent);
+       struct tps65910_board *pdata = dev_get_platdata(tps65910->dev);
+       struct tps65910_gpio *tps65910_gpio;
+       int ret;
+       int i;
+
+       tps65910_gpio = devm_kzalloc(&pdev->dev,
+                               sizeof(*tps65910_gpio), GFP_KERNEL);
+       if (!tps65910_gpio) {
+               dev_err(&pdev->dev, "Could not allocate tps65910_gpio\n");
+               return -ENOMEM;
+       }
+
+       tps65910_gpio->tps65910 = tps65910;
+
+       tps65910_gpio->gpio_chip.owner = THIS_MODULE;
+       tps65910_gpio->gpio_chip.label = tps65910->i2c_client->name;
 
        switch(tps65910_chip_id(tps65910)) {
        case TPS65910:
-               tps65910->gpio.ngpio    = TPS65910_NUM_GPIO;
+               tps65910_gpio->gpio_chip.ngpio = TPS65910_NUM_GPIO;
                break;
        case TPS65911:
-               tps65910->gpio.ngpio    = TPS65911_NUM_GPIO;
+               tps65910_gpio->gpio_chip.ngpio = TPS65911_NUM_GPIO;
                break;
        default:
-               return;
+               return -EINVAL;
+       }
+       tps65910_gpio->gpio_chip.can_sleep = 1;
+       tps65910_gpio->gpio_chip.direction_input = tps65910_gpio_input;
+       tps65910_gpio->gpio_chip.direction_output = tps65910_gpio_output;
+       tps65910_gpio->gpio_chip.set    = tps65910_gpio_set;
+       tps65910_gpio->gpio_chip.get    = tps65910_gpio_get;
+       tps65910_gpio->gpio_chip.dev = &pdev->dev;
+       if (pdata && pdata->gpio_base)
+               tps65910_gpio->gpio_chip.base = pdata->gpio_base;
+       else
+               tps65910_gpio->gpio_chip.base = -1;
+
+       if (!pdata && tps65910->dev->of_node)
+               pdata = tps65910_parse_dt_for_gpio(&pdev->dev, tps65910,
+                       tps65910_gpio->gpio_chip.ngpio);
+
+       if (!pdata)
+               goto skip_init;
+
+       /* Configure sleep control for gpios if provided */
+       for (i = 0; i < tps65910_gpio->gpio_chip.ngpio; ++i) {
+               if (!pdata->en_gpio_sleep[i])
+                       continue;
+
+               ret = tps65910_reg_set_bits(tps65910,
+                       TPS65910_GPIO0 + i, GPIO_SLEEP_MASK);
+               if (ret < 0)
+                       dev_warn(tps65910->dev,
+                               "GPIO Sleep setting failed with err %d\n", ret);
        }
-       tps65910->gpio.can_sleep        = 1;
-
-       tps65910->gpio.direction_input  = tps65910_gpio_input;
-       tps65910->gpio.direction_output = tps65910_gpio_output;
-       tps65910->gpio.set              = tps65910_gpio_set;
-       tps65910->gpio.get              = tps65910_gpio_get;
-
-       /* Configure sleep control for gpios */
-       board_data = dev_get_platdata(tps65910->dev);
-       if (board_data) {
-               int i;
-               for (i = 0; i < tps65910->gpio.ngpio; ++i) {
-                       if (board_data->en_gpio_sleep[i]) {
-                               ret = tps65910_set_bits(tps65910,
-                                       TPS65910_GPIO0 + i, GPIO_SLEEP_MASK);
-                               if (ret < 0)
-                                       dev_warn(tps65910->dev,
-                                               "GPIO Sleep setting failed\n");
-                       }
-               }
+
+skip_init:
+       ret = gpiochip_add(&tps65910_gpio->gpio_chip);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret);
+               return ret;
        }
 
-       ret = gpiochip_add(&tps65910->gpio);
+       platform_set_drvdata(pdev, tps65910_gpio);
+
+       return ret;
+}
+
+static int __devexit tps65910_gpio_remove(struct platform_device *pdev)
+{
+       struct tps65910_gpio *tps65910_gpio = platform_get_drvdata(pdev);
 
-       if (ret)
-               dev_warn(tps65910->dev, "GPIO registration failed: %d\n", ret);
+       return gpiochip_remove(&tps65910_gpio->gpio_chip);
 }
+
+static struct platform_driver tps65910_gpio_driver = {
+       .driver.name    = "tps65910-gpio",
+       .driver.owner   = THIS_MODULE,
+       .probe          = tps65910_gpio_probe,
+       .remove         = __devexit_p(tps65910_gpio_remove),
+};
+
+static int __init tps65910_gpio_init(void)
+{
+       return platform_driver_register(&tps65910_gpio_driver);
+}
+subsys_initcall(tps65910_gpio_init);
+
+static void __exit tps65910_gpio_exit(void)
+{
+       platform_driver_unregister(&tps65910_gpio_driver);
+}
+module_exit(tps65910_gpio_exit);
+
+MODULE_AUTHOR("Graeme Gregory <gg@slimlogic.co.uk>");
+MODULE_AUTHOR("Jorge Eduardo Candelaria jedu@slimlogic.co.uk>");
+MODULE_DESCRIPTION("GPIO interface for TPS65910/TPS6511 PMICs");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:tps65910-gpio");
index deb949e75ec1dee2f6ad20c2541148beac431d1a..e56a2165641c845137b0b37afd4c30d93f570a84 100644 (file)
@@ -102,10 +102,8 @@ static int wm831x_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
        struct wm831x_gpio *wm831x_gpio = to_wm831x_gpio(chip);
        struct wm831x *wm831x = wm831x_gpio->wm831x;
 
-       if (!wm831x->irq_base)
-               return -EINVAL;
-
-       return wm831x->irq_base + WM831X_IRQ_GPIO_1 + offset;
+       return irq_create_mapping(wm831x->irq_domain,
+                                 WM831X_IRQ_GPIO_1 + offset);
 }
 
 static int wm831x_gpio_set_debounce(struct gpio_chip *chip, unsigned offset,
index f920fb5e42b63846e3d8b7b782b492e547e18eef..fa9439159ebd6bc85cdf4e27a307d9cde12dcbd6 100644 (file)
@@ -130,11 +130,10 @@ static int i810_map_buffer(struct drm_buf *buf, struct drm_file *file_priv)
                return -EINVAL;
 
        /* This is all entirely broken */
-       down_write(&current->mm->mmap_sem);
        old_fops = file_priv->filp->f_op;
        file_priv->filp->f_op = &i810_buffer_fops;
        dev_priv->mmap_buffer = buf;
-       buf_priv->virtual = (void *)do_mmap(file_priv->filp, 0, buf->total,
+       buf_priv->virtual = (void *)vm_mmap(file_priv->filp, 0, buf->total,
                                            PROT_READ | PROT_WRITE,
                                            MAP_SHARED, buf->bus_address);
        dev_priv->mmap_buffer = NULL;
@@ -145,7 +144,6 @@ static int i810_map_buffer(struct drm_buf *buf, struct drm_file *file_priv)
                retcode = PTR_ERR(buf_priv->virtual);
                buf_priv->virtual = NULL;
        }
-       up_write(&current->mm->mmap_sem);
 
        return retcode;
 }
index 377c21f531e49ba93bdcb31aaaff290b15292bc2..c9cfc67c2cf58acdf7871a6e81fda66d3c45dedb 100644 (file)
@@ -942,6 +942,9 @@ struct drm_i915_gem_object {
 
        /* prime dma-buf support */
        struct sg_table *sg_table;
+       void *dma_buf_vmapping;
+       int vmapping_count;
+
        /**
         * Used for performing relocations during execbuffer insertion.
         */
index 8e269178d6a5a65c2f5ab8068ac86d283711fab6..aa308e1337db7c8bacbf73d00dc80badecb617a2 100644 (file)
@@ -74,6 +74,59 @@ static void i915_gem_dmabuf_release(struct dma_buf *dma_buf)
        }
 }
 
+static void *i915_gem_dmabuf_vmap(struct dma_buf *dma_buf)
+{
+       struct drm_i915_gem_object *obj = dma_buf->priv;
+       struct drm_device *dev = obj->base.dev;
+       int ret;
+
+       ret = i915_mutex_lock_interruptible(dev);
+       if (ret)
+               return ERR_PTR(ret);
+
+       if (obj->dma_buf_vmapping) {
+               obj->vmapping_count++;
+               goto out_unlock;
+       }
+
+       if (!obj->pages) {
+               ret = i915_gem_object_get_pages_gtt(obj, __GFP_NORETRY | __GFP_NOWARN);
+               if (ret) {
+                       mutex_unlock(&dev->struct_mutex);
+                       return ERR_PTR(ret);
+               }
+       }
+
+       obj->dma_buf_vmapping = vmap(obj->pages, obj->base.size / PAGE_SIZE, 0, PAGE_KERNEL);
+       if (!obj->dma_buf_vmapping) {
+               DRM_ERROR("failed to vmap object\n");
+               goto out_unlock;
+       }
+
+       obj->vmapping_count = 1;
+out_unlock:
+       mutex_unlock(&dev->struct_mutex);
+       return obj->dma_buf_vmapping;
+}
+
+static void i915_gem_dmabuf_vunmap(struct dma_buf *dma_buf, void *vaddr)
+{
+       struct drm_i915_gem_object *obj = dma_buf->priv;
+       struct drm_device *dev = obj->base.dev;
+       int ret;
+
+       ret = i915_mutex_lock_interruptible(dev);
+       if (ret)
+               return;
+
+       --obj->vmapping_count;
+       if (obj->vmapping_count == 0) {
+               vunmap(obj->dma_buf_vmapping);
+               obj->dma_buf_vmapping = NULL;
+       }
+       mutex_unlock(&dev->struct_mutex);
+}
+
 static void *i915_gem_dmabuf_kmap_atomic(struct dma_buf *dma_buf, unsigned long page_num)
 {
        return NULL;
@@ -93,6 +146,11 @@ static void i915_gem_dmabuf_kunmap(struct dma_buf *dma_buf, unsigned long page_n
 
 }
 
+static int i915_gem_dmabuf_mmap(struct dma_buf *dma_buf, struct vm_area_struct *vma)
+{
+       return -EINVAL;
+}
+
 static const struct dma_buf_ops i915_dmabuf_ops =  {
        .map_dma_buf = i915_gem_map_dma_buf,
        .unmap_dma_buf = i915_gem_unmap_dma_buf,
@@ -101,6 +159,9 @@ static const struct dma_buf_ops i915_dmabuf_ops =  {
        .kmap_atomic = i915_gem_dmabuf_kmap_atomic,
        .kunmap = i915_gem_dmabuf_kunmap,
        .kunmap_atomic = i915_gem_dmabuf_kunmap_atomic,
+       .mmap = i915_gem_dmabuf_mmap,
+       .vmap = i915_gem_dmabuf_vmap,
+       .vunmap = i915_gem_dmabuf_vunmap,
 };
 
 struct dma_buf *i915_gem_prime_export(struct drm_device *dev,
index 634d222c93dea4b310183a1174c474cd2f80a2b8..8613cb23808c585ef35175f4d4305f1c739d1add 100644 (file)
@@ -123,6 +123,9 @@ struct nouveau_bo {
 
        struct drm_gem_object *gem;
        int pin_refcnt;
+
+       struct ttm_bo_kmap_obj dma_buf_vmap;
+       int vmapping_count;
 };
 
 #define nouveau_bo_tile_layout(nvbo)                           \
index c58aab7370c575949a41113014120b252548169c..a89240e5fb2962334e601d7f4ff6fa1363f7f061 100644 (file)
@@ -61,6 +61,48 @@ static void nouveau_gem_kunmap(struct dma_buf *dma_buf, unsigned long page_num,
 
 }
 
+static int nouveau_gem_prime_mmap(struct dma_buf *dma_buf, struct vm_area_struct *vma)
+{
+       return -EINVAL;
+}
+
+static void *nouveau_gem_prime_vmap(struct dma_buf *dma_buf)
+{
+       struct nouveau_bo *nvbo = dma_buf->priv;
+       struct drm_device *dev = nvbo->gem->dev;
+       int ret;
+
+       mutex_lock(&dev->struct_mutex);
+       if (nvbo->vmapping_count) {
+               nvbo->vmapping_count++;
+               goto out_unlock;
+       }
+
+       ret = ttm_bo_kmap(&nvbo->bo, 0, nvbo->bo.num_pages,
+                         &nvbo->dma_buf_vmap);
+       if (ret) {
+               mutex_unlock(&dev->struct_mutex);
+               return ERR_PTR(ret);
+       }
+       nvbo->vmapping_count = 1;
+out_unlock:
+       mutex_unlock(&dev->struct_mutex);
+       return nvbo->dma_buf_vmap.virtual;
+}
+
+static void nouveau_gem_prime_vunmap(struct dma_buf *dma_buf, void *vaddr)
+{
+       struct nouveau_bo *nvbo = dma_buf->priv;
+       struct drm_device *dev = nvbo->gem->dev;
+
+       mutex_lock(&dev->struct_mutex);
+       nvbo->vmapping_count--;
+       if (nvbo->vmapping_count == 0) {
+               ttm_bo_kunmap(&nvbo->dma_buf_vmap);
+       }
+       mutex_unlock(&dev->struct_mutex);
+}
+
 static const struct dma_buf_ops nouveau_dmabuf_ops =  {
        .map_dma_buf = nouveau_gem_map_dma_buf,
        .unmap_dma_buf = nouveau_gem_unmap_dma_buf,
@@ -69,6 +111,9 @@ static const struct dma_buf_ops nouveau_dmabuf_ops =  {
        .kmap_atomic = nouveau_gem_kmap_atomic,
        .kunmap = nouveau_gem_kunmap,
        .kunmap_atomic = nouveau_gem_kunmap_atomic,
+       .mmap = nouveau_gem_prime_mmap,
+       .vmap = nouveau_gem_prime_vmap,
+       .vunmap = nouveau_gem_prime_vunmap,
 };
 
 static int
index e25836b72c73b33e41b656ba469074d2f3570ecf..85dac33e3cce3c0eeca90ae3d57d066b4c771a24 100644 (file)
@@ -346,6 +346,9 @@ struct radeon_bo {
        /* Constant after initialization */
        struct radeon_device            *rdev;
        struct drm_gem_object           gem_base;
+
+       struct ttm_bo_kmap_obj dma_buf_vmap;
+       int vmapping_count;
 };
 #define gem_to_radeon_bo(gobj) container_of((gobj), struct radeon_bo, gem_base)
 
index b8f835d8ecb4127a2c4b8771e1b74859f0561c5d..8ddab4c76710f1b00e3917e0415a6aad91965f9a 100644 (file)
@@ -85,6 +85,47 @@ static void radeon_gem_kunmap(struct dma_buf *dma_buf, unsigned long page_num, v
 
 }
 
+static int radeon_gem_prime_mmap(struct dma_buf *dma_buf, struct vm_area_struct *vma)
+{
+       return -EINVAL;
+}
+
+static void *radeon_gem_prime_vmap(struct dma_buf *dma_buf)
+{
+       struct radeon_bo *bo = dma_buf->priv;
+       struct drm_device *dev = bo->rdev->ddev;
+       int ret;
+
+       mutex_lock(&dev->struct_mutex);
+       if (bo->vmapping_count) {
+               bo->vmapping_count++;
+               goto out_unlock;
+       }
+
+       ret = ttm_bo_kmap(&bo->tbo, 0, bo->tbo.num_pages,
+                         &bo->dma_buf_vmap);
+       if (ret) {
+               mutex_unlock(&dev->struct_mutex);
+               return ERR_PTR(ret);
+       }
+       bo->vmapping_count = 1;
+out_unlock:
+       mutex_unlock(&dev->struct_mutex);
+       return bo->dma_buf_vmap.virtual;
+}
+
+static void radeon_gem_prime_vunmap(struct dma_buf *dma_buf, void *vaddr)
+{
+       struct radeon_bo *bo = dma_buf->priv;
+       struct drm_device *dev = bo->rdev->ddev;
+
+       mutex_lock(&dev->struct_mutex);
+       bo->vmapping_count--;
+       if (bo->vmapping_count == 0) {
+               ttm_bo_kunmap(&bo->dma_buf_vmap);
+       }
+       mutex_unlock(&dev->struct_mutex);
+}
 const static struct dma_buf_ops radeon_dmabuf_ops =  {
        .map_dma_buf = radeon_gem_map_dma_buf,
        .unmap_dma_buf = radeon_gem_unmap_dma_buf,
@@ -93,6 +134,9 @@ const static struct dma_buf_ops radeon_dmabuf_ops =  {
        .kmap_atomic = radeon_gem_kmap_atomic,
        .kunmap = radeon_gem_kunmap,
        .kunmap_atomic = radeon_gem_kunmap_atomic,
+       .mmap = radeon_gem_prime_mmap,
+       .vmap = radeon_gem_prime_vmap,
+       .vunmap = radeon_gem_prime_vunmap,
 };
 
 static int radeon_prime_create(struct drm_device *dev,
index a029ee39b0c526d0e0fe63150acd25ea992f73c0..ce9a61179925cd540f4d4956b991513ca53b27e2 100644 (file)
@@ -156,8 +156,17 @@ int udl_handle_damage(struct udl_framebuffer *fb, int x, int y,
        if (!fb->active_16)
                return 0;
 
-       if (!fb->obj->vmapping)
-               udl_gem_vmap(fb->obj);
+       if (!fb->obj->vmapping) {
+               ret = udl_gem_vmap(fb->obj);
+               if (ret == -ENOMEM) {
+                       DRM_ERROR("failed to vmap fb\n");
+                       return 0;
+               }
+               if (!fb->obj->vmapping) {
+                       DRM_ERROR("failed to vmapping\n");
+                       return 0;
+               }
+       }
 
        start_cycles = get_cycles();
 
index 97acc9c6c95b074c8b4ec7261e84360b5f93fccf..7bd65bdd15a8092e955d959c08fd6dd04781d490 100644 (file)
@@ -180,6 +180,18 @@ int udl_gem_vmap(struct udl_gem_object *obj)
        int page_count = obj->base.size / PAGE_SIZE;
        int ret;
 
+       if (obj->base.import_attach) {
+               ret = dma_buf_begin_cpu_access(obj->base.import_attach->dmabuf,
+                                              0, obj->base.size, DMA_BIDIRECTIONAL);
+               if (ret)
+                       return -EINVAL;
+
+               obj->vmapping = dma_buf_vmap(obj->base.import_attach->dmabuf);
+               if (!obj->vmapping)
+                       return -ENOMEM;
+               return 0;
+       }
+               
        ret = udl_gem_get_pages(obj, GFP_KERNEL);
        if (ret)
                return ret;
@@ -192,6 +204,13 @@ int udl_gem_vmap(struct udl_gem_object *obj)
 
 void udl_gem_vunmap(struct udl_gem_object *obj)
 {
+       if (obj->base.import_attach) {
+               dma_buf_vunmap(obj->base.import_attach->dmabuf, obj->vmapping);
+               dma_buf_end_cpu_access(obj->base.import_attach->dmabuf, 0,
+                                      obj->base.size, DMA_BIDIRECTIONAL);
+               return;
+       }
+
        if (obj->vmapping)
                vunmap(obj->vmapping);
 
@@ -202,12 +221,12 @@ void udl_gem_free_object(struct drm_gem_object *gem_obj)
 {
        struct udl_gem_object *obj = to_udl_bo(gem_obj);
 
-       if (gem_obj->import_attach)
-               drm_prime_gem_destroy(gem_obj, obj->sg);
-
        if (obj->vmapping)
                udl_gem_vunmap(obj);
 
+       if (gem_obj->import_attach)
+               drm_prime_gem_destroy(gem_obj, obj->sg);
+
        if (obj->pages)
                udl_gem_put_pages(obj);
 
index 7cd9bf42108b7368803322d7116c3a8951d30a1b..6f1d167cb1ea9c2174c403a8a0f0b6b0f1d33bc7 100644 (file)
@@ -1036,8 +1036,9 @@ config SENSORS_SCH56XX_COMMON
 
 config SENSORS_SCH5627
        tristate "SMSC SCH5627"
-       depends on !PPC
+       depends on !PPC && WATCHDOG
        select SENSORS_SCH56XX_COMMON
+       select WATCHDOG_CORE
        help
          If you say yes here you get support for the hardware monitoring
          features of the SMSC SCH5627 Super-I/O chip including support for
@@ -1048,8 +1049,9 @@ config SENSORS_SCH5627
 
 config SENSORS_SCH5636
        tristate "SMSC SCH5636"
-       depends on !PPC
+       depends on !PPC && WATCHDOG
        select SENSORS_SCH56XX_COMMON
+       select WATCHDOG_CORE
        help
          SMSC SCH5636 Super I/O chips include an embedded microcontroller for
          hardware monitoring solutions, allowing motherboard manufacturers to
index 8ec6dfbccb640f8e3f89d22c701cc6151ffcebfe..8342275378b85759f57b9af583f34d281e58cd25 100644 (file)
@@ -579,7 +579,7 @@ static int __devinit sch5627_probe(struct platform_device *pdev)
        }
 
        /* Note failing to register the watchdog is not a fatal error */
-       data->watchdog = sch56xx_watchdog_register(data->addr,
+       data->watchdog = sch56xx_watchdog_register(&pdev->dev, data->addr,
                        (build_code << 24) | (build_id << 8) | hwmon_rev,
                        &data->update_lock, 1);
 
index 906d4ed32d81abd2b6c7d59f1fcd77c035fad8bc..96a7e68718cadb8348ea4d7680750ade1d6d143b 100644 (file)
@@ -510,7 +510,7 @@ static int __devinit sch5636_probe(struct platform_device *pdev)
        }
 
        /* Note failing to register the watchdog is not a fatal error */
-       data->watchdog = sch56xx_watchdog_register(data->addr,
+       data->watchdog = sch56xx_watchdog_register(&pdev->dev, data->addr,
                                        (revision[0] << 8) | revision[1],
                                        &data->update_lock, 0);
 
index ce52fc57d41d70cf8bb119e9e3a2d3756e294615..4380f5d07be2b8b5398d43e2e393307f099341e6 100644 (file)
@@ -66,15 +66,10 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
 
 struct sch56xx_watchdog_data {
        u16 addr;
-       u32 revision;
        struct mutex *io_lock;
-       struct mutex watchdog_lock;
-       struct list_head list; /* member of the watchdog_data_list */
        struct kref kref;
-       struct miscdevice watchdog_miscdev;
-       unsigned long watchdog_is_open;
-       char watchdog_name[10]; /* must be unique to avoid sysfs conflict */
-       char watchdog_expect_close;
+       struct watchdog_info wdinfo;
+       struct watchdog_device wddev;
        u8 watchdog_preset;
        u8 watchdog_control;
        u8 watchdog_output_enable;
@@ -82,15 +77,6 @@ struct sch56xx_watchdog_data {
 
 static struct platform_device *sch56xx_pdev;
 
-/*
- * Somewhat ugly :( global data pointer list with all sch56xx devices, so that
- * we can find our device data as when using misc_register there is no other
- * method to get to ones device data from the open fop.
- */
-static LIST_HEAD(watchdog_data_list);
-/* Note this lock not only protect list access, but also data.kref access */
-static DEFINE_MUTEX(watchdog_data_mutex);
-
 /* Super I/O functions */
 static inline int superio_inb(int base, int reg)
 {
@@ -272,22 +258,22 @@ EXPORT_SYMBOL(sch56xx_read_virtual_reg12);
  * Watchdog routines
  */
 
-/*
- * Release our data struct when the platform device has been released *and*
- * all references to our watchdog device are released.
- */
-static void sch56xx_watchdog_release_resources(struct kref *r)
+/* Release our data struct when we're unregistered *and*
+   all references to our watchdog device are released */
+static void watchdog_release_resources(struct kref *r)
 {
        struct sch56xx_watchdog_data *data =
                container_of(r, struct sch56xx_watchdog_data, kref);
        kfree(data);
 }
 
-static int watchdog_set_timeout(struct sch56xx_watchdog_data *data,
-                               int timeout)
+static int watchdog_set_timeout(struct watchdog_device *wddev,
+                               unsigned int timeout)
 {
-       int ret, resolution;
+       struct sch56xx_watchdog_data *data = watchdog_get_drvdata(wddev);
+       unsigned int resolution;
        u8 control;
+       int ret;
 
        /* 1 second or 60 second resolution? */
        if (timeout <= 255)
@@ -298,12 +284,6 @@ static int watchdog_set_timeout(struct sch56xx_watchdog_data *data,
        if (timeout < resolution || timeout > (resolution * 255))
                return -EINVAL;
 
-       mutex_lock(&data->watchdog_lock);
-       if (!data->addr) {
-               ret = -ENODEV;
-               goto leave;
-       }
-
        if (resolution == 1)
                control = data->watchdog_control | SCH56XX_WDOG_TIME_BASE_SEC;
        else
@@ -316,7 +296,7 @@ static int watchdog_set_timeout(struct sch56xx_watchdog_data *data,
                                                control);
                mutex_unlock(data->io_lock);
                if (ret)
-                       goto leave;
+                       return ret;
 
                data->watchdog_control = control;
        }
@@ -326,38 +306,17 @@ static int watchdog_set_timeout(struct sch56xx_watchdog_data *data,
         * the watchdog countdown.
         */
        data->watchdog_preset = DIV_ROUND_UP(timeout, resolution);
+       wddev->timeout = data->watchdog_preset * resolution;
 
-       ret = data->watchdog_preset * resolution;
-leave:
-       mutex_unlock(&data->watchdog_lock);
-       return ret;
-}
-
-static int watchdog_get_timeout(struct sch56xx_watchdog_data *data)
-{
-       int timeout;
-
-       mutex_lock(&data->watchdog_lock);
-       if (data->watchdog_control & SCH56XX_WDOG_TIME_BASE_SEC)
-               timeout = data->watchdog_preset;
-       else
-               timeout = data->watchdog_preset * 60;
-       mutex_unlock(&data->watchdog_lock);
-
-       return timeout;
+       return 0;
 }
 
-static int watchdog_start(struct sch56xx_watchdog_data *data)
+static int watchdog_start(struct watchdog_device *wddev)
 {
+       struct sch56xx_watchdog_data *data = watchdog_get_drvdata(wddev);
        int ret;
        u8 val;
 
-       mutex_lock(&data->watchdog_lock);
-       if (!data->addr) {
-               ret = -ENODEV;
-               goto leave_unlock_watchdog;
-       }
-
        /*
         * The sch56xx's watchdog cannot really be started / stopped
         * it is always running, but we can avoid the timer expiring
@@ -385,18 +344,14 @@ static int watchdog_start(struct sch56xx_watchdog_data *data)
        if (ret)
                goto leave;
 
-       /* 2. Enable output (if not already enabled) */
-       if (!(data->watchdog_output_enable & SCH56XX_WDOG_OUTPUT_ENABLE)) {
-               val = data->watchdog_output_enable |
-                     SCH56XX_WDOG_OUTPUT_ENABLE;
-               ret = sch56xx_write_virtual_reg(data->addr,
-                                               SCH56XX_REG_WDOG_OUTPUT_ENABLE,
-                                               val);
-               if (ret)
-                       goto leave;
+       /* 2. Enable output */
+       val = data->watchdog_output_enable | SCH56XX_WDOG_OUTPUT_ENABLE;
+       ret = sch56xx_write_virtual_reg(data->addr,
+                                       SCH56XX_REG_WDOG_OUTPUT_ENABLE, val);
+       if (ret)
+               goto leave;
 
-               data->watchdog_output_enable = val;
-       }
+       data->watchdog_output_enable = val;
 
        /* 3. Clear the watchdog event bit if set */
        val = inb(data->addr + 9);
@@ -405,234 +360,70 @@ static int watchdog_start(struct sch56xx_watchdog_data *data)
 
 leave:
        mutex_unlock(data->io_lock);
-leave_unlock_watchdog:
-       mutex_unlock(&data->watchdog_lock);
        return ret;
 }
 
-static int watchdog_trigger(struct sch56xx_watchdog_data *data)
+static int watchdog_trigger(struct watchdog_device *wddev)
 {
+       struct sch56xx_watchdog_data *data = watchdog_get_drvdata(wddev);
        int ret;
 
-       mutex_lock(&data->watchdog_lock);
-       if (!data->addr) {
-               ret = -ENODEV;
-               goto leave;
-       }
-
        /* Reset the watchdog countdown counter */
        mutex_lock(data->io_lock);
        ret = sch56xx_write_virtual_reg(data->addr, SCH56XX_REG_WDOG_PRESET,
                                        data->watchdog_preset);
        mutex_unlock(data->io_lock);
-leave:
-       mutex_unlock(&data->watchdog_lock);
+
        return ret;
 }
 
-static int watchdog_stop_unlocked(struct sch56xx_watchdog_data *data)
+static int watchdog_stop(struct watchdog_device *wddev)
 {
+       struct sch56xx_watchdog_data *data = watchdog_get_drvdata(wddev);
        int ret = 0;
        u8 val;
 
-       if (!data->addr)
-               return -ENODEV;
-
-       if (data->watchdog_output_enable & SCH56XX_WDOG_OUTPUT_ENABLE) {
-               val = data->watchdog_output_enable &
-                     ~SCH56XX_WDOG_OUTPUT_ENABLE;
-               mutex_lock(data->io_lock);
-               ret = sch56xx_write_virtual_reg(data->addr,
-                                               SCH56XX_REG_WDOG_OUTPUT_ENABLE,
-                                               val);
-               mutex_unlock(data->io_lock);
-               if (ret)
-                       return ret;
-
-               data->watchdog_output_enable = val;
-       }
-
-       return ret;
-}
-
-static int watchdog_stop(struct sch56xx_watchdog_data *data)
-{
-       int ret;
-
-       mutex_lock(&data->watchdog_lock);
-       ret = watchdog_stop_unlocked(data);
-       mutex_unlock(&data->watchdog_lock);
-
-       return ret;
-}
-
-static int watchdog_release(struct inode *inode, struct file *filp)
-{
-       struct sch56xx_watchdog_data *data = filp->private_data;
-
-       if (data->watchdog_expect_close) {
-               watchdog_stop(data);
-               data->watchdog_expect_close = 0;
-       } else {
-               watchdog_trigger(data);
-               pr_crit("unexpected close, not stopping watchdog!\n");
-       }
-
-       clear_bit(0, &data->watchdog_is_open);
-
-       mutex_lock(&watchdog_data_mutex);
-       kref_put(&data->kref, sch56xx_watchdog_release_resources);
-       mutex_unlock(&watchdog_data_mutex);
+       val = data->watchdog_output_enable & ~SCH56XX_WDOG_OUTPUT_ENABLE;
+       mutex_lock(data->io_lock);
+       ret = sch56xx_write_virtual_reg(data->addr,
+                                       SCH56XX_REG_WDOG_OUTPUT_ENABLE, val);
+       mutex_unlock(data->io_lock);
+       if (ret)
+               return ret;
 
+       data->watchdog_output_enable = val;
        return 0;
 }
 
-static int watchdog_open(struct inode *inode, struct file *filp)
+static void watchdog_ref(struct watchdog_device *wddev)
 {
-       struct sch56xx_watchdog_data *pos, *data = NULL;
-       int ret, watchdog_is_open;
-
-       /*
-        * We get called from drivers/char/misc.c with misc_mtx hold, and we
-        * call misc_register() from sch56xx_watchdog_probe() with
-        * watchdog_data_mutex hold, as misc_register() takes the misc_mtx
-        * lock, this is a possible deadlock, so we use mutex_trylock here.
-        */
-       if (!mutex_trylock(&watchdog_data_mutex))
-               return -ERESTARTSYS;
-       list_for_each_entry(pos, &watchdog_data_list, list) {
-               if (pos->watchdog_miscdev.minor == iminor(inode)) {
-                       data = pos;
-                       break;
-               }
-       }
-       /* Note we can never not have found data, so we don't check for this */
-       watchdog_is_open = test_and_set_bit(0, &data->watchdog_is_open);
-       if (!watchdog_is_open)
-               kref_get(&data->kref);
-       mutex_unlock(&watchdog_data_mutex);
-
-       if (watchdog_is_open)
-               return -EBUSY;
-
-       filp->private_data = data;
-
-       /* Start the watchdog */
-       ret = watchdog_start(data);
-       if (ret) {
-               watchdog_release(inode, filp);
-               return ret;
-       }
+       struct sch56xx_watchdog_data *data = watchdog_get_drvdata(wddev);
 
-       return nonseekable_open(inode, filp);
+       kref_get(&data->kref);
 }
 
-static ssize_t watchdog_write(struct file *filp, const char __user *buf,
-       size_t count, loff_t *offset)
+static void watchdog_unref(struct watchdog_device *wddev)
 {
-       int ret;
-       struct sch56xx_watchdog_data *data = filp->private_data;
-
-       if (count) {
-               if (!nowayout) {
-                       size_t i;
-
-                       /* Clear it in case it was set with a previous write */
-                       data->watchdog_expect_close = 0;
-
-                       for (i = 0; i != count; i++) {
-                               char c;
-                               if (get_user(c, buf + i))
-                                       return -EFAULT;
-                               if (c == 'V')
-                                       data->watchdog_expect_close = 1;
-                       }
-               }
-               ret = watchdog_trigger(data);
-               if (ret)
-                       return ret;
-       }
-       return count;
-}
-
-static long watchdog_ioctl(struct file *filp, unsigned int cmd,
-                          unsigned long arg)
-{
-       struct watchdog_info ident = {
-               .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT,
-               .identity = "sch56xx watchdog"
-       };
-       int i, ret = 0;
-       struct sch56xx_watchdog_data *data = filp->private_data;
-
-       switch (cmd) {
-       case WDIOC_GETSUPPORT:
-               ident.firmware_version = data->revision;
-               if (!nowayout)
-                       ident.options |= WDIOF_MAGICCLOSE;
-               if (copy_to_user((void __user *)arg, &ident, sizeof(ident)))
-                       ret = -EFAULT;
-               break;
-
-       case WDIOC_GETSTATUS:
-       case WDIOC_GETBOOTSTATUS:
-               ret = put_user(0, (int __user *)arg);
-               break;
-
-       case WDIOC_KEEPALIVE:
-               ret = watchdog_trigger(data);
-               break;
+       struct sch56xx_watchdog_data *data = watchdog_get_drvdata(wddev);
 
-       case WDIOC_GETTIMEOUT:
-               i = watchdog_get_timeout(data);
-               ret = put_user(i, (int __user *)arg);
-               break;
-
-       case WDIOC_SETTIMEOUT:
-               if (get_user(i, (int __user *)arg)) {
-                       ret = -EFAULT;
-                       break;
-               }
-               ret = watchdog_set_timeout(data, i);
-               if (ret >= 0)
-                       ret = put_user(ret, (int __user *)arg);
-               break;
-
-       case WDIOC_SETOPTIONS:
-               if (get_user(i, (int __user *)arg)) {
-                       ret = -EFAULT;
-                       break;
-               }
-
-               if (i & WDIOS_DISABLECARD)
-                       ret = watchdog_stop(data);
-               else if (i & WDIOS_ENABLECARD)
-                       ret = watchdog_trigger(data);
-               else
-                       ret = -EINVAL;
-               break;
-
-       default:
-               ret = -ENOTTY;
-       }
-       return ret;
+       kref_put(&data->kref, watchdog_release_resources);
 }
 
-static const struct file_operations watchdog_fops = {
-       .owner = THIS_MODULE,
-       .llseek = no_llseek,
-       .open = watchdog_open,
-       .release = watchdog_release,
-       .write = watchdog_write,
-       .unlocked_ioctl = watchdog_ioctl,
+static const struct watchdog_ops watchdog_ops = {
+       .owner          = THIS_MODULE,
+       .start          = watchdog_start,
+       .stop           = watchdog_stop,
+       .ping           = watchdog_trigger,
+       .set_timeout    = watchdog_set_timeout,
+       .ref            = watchdog_ref,
+       .unref          = watchdog_unref,
 };
 
-struct sch56xx_watchdog_data *sch56xx_watchdog_register(
+struct sch56xx_watchdog_data *sch56xx_watchdog_register(struct device *parent,
        u16 addr, u32 revision, struct mutex *io_lock, int check_enabled)
 {
        struct sch56xx_watchdog_data *data;
-       int i, err, control, output_enable;
-       const int watchdog_minors[] = { WATCHDOG_MINOR, 212, 213, 214, 215 };
+       int err, control, output_enable;
 
        /* Cache the watchdog registers */
        mutex_lock(io_lock);
@@ -656,82 +447,55 @@ struct sch56xx_watchdog_data *sch56xx_watchdog_register(
                return NULL;
 
        data->addr = addr;
-       data->revision = revision;
        data->io_lock = io_lock;
-       data->watchdog_control = control;
-       data->watchdog_output_enable = output_enable;
-       mutex_init(&data->watchdog_lock);
-       INIT_LIST_HEAD(&data->list);
        kref_init(&data->kref);
 
-       err = watchdog_set_timeout(data, 60);
-       if (err < 0)
-               goto error;
-
-       /*
-        * We take the data_mutex lock early so that watchdog_open() cannot
-        * run when misc_register() has completed, but we've not yet added
-        * our data to the watchdog_data_list.
-        */
-       mutex_lock(&watchdog_data_mutex);
-       for (i = 0; i < ARRAY_SIZE(watchdog_minors); i++) {
-               /* Register our watchdog part */
-               snprintf(data->watchdog_name, sizeof(data->watchdog_name),
-                       "watchdog%c", (i == 0) ? '\0' : ('0' + i));
-               data->watchdog_miscdev.name = data->watchdog_name;
-               data->watchdog_miscdev.fops = &watchdog_fops;
-               data->watchdog_miscdev.minor = watchdog_minors[i];
-               err = misc_register(&data->watchdog_miscdev);
-               if (err == -EBUSY)
-                       continue;
-               if (err)
-                       break;
+       strlcpy(data->wdinfo.identity, "sch56xx watchdog",
+               sizeof(data->wdinfo.identity));
+       data->wdinfo.firmware_version = revision;
+       data->wdinfo.options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT;
+       if (!nowayout)
+               data->wdinfo.options |= WDIOF_MAGICCLOSE;
+
+       data->wddev.info = &data->wdinfo;
+       data->wddev.ops = &watchdog_ops;
+       data->wddev.parent = parent;
+       data->wddev.timeout = 60;
+       data->wddev.min_timeout = 1;
+       data->wddev.max_timeout = 255 * 60;
+       if (nowayout)
+               set_bit(WDOG_NO_WAY_OUT, &data->wddev.status);
+       if (output_enable & SCH56XX_WDOG_OUTPUT_ENABLE)
+               set_bit(WDOG_ACTIVE, &data->wddev.status);
+
+       /* Since the watchdog uses a downcounter there is no register to read
+          the BIOS set timeout from (if any was set at all) ->
+          Choose a preset which will give us a 1 minute timeout */
+       if (control & SCH56XX_WDOG_TIME_BASE_SEC)
+               data->watchdog_preset = 60; /* seconds */
+       else
+               data->watchdog_preset = 1; /* minute */
 
-               list_add(&data->list, &watchdog_data_list);
-               pr_info("Registered /dev/%s chardev major 10, minor: %d\n",
-                       data->watchdog_name, watchdog_minors[i]);
-               break;
-       }
-       mutex_unlock(&watchdog_data_mutex);
+       data->watchdog_control = control;
+       data->watchdog_output_enable = output_enable;
 
+       watchdog_set_drvdata(&data->wddev, data);
+       err = watchdog_register_device(&data->wddev);
        if (err) {
                pr_err("Registering watchdog chardev: %d\n", err);
-               goto error;
-       }
-       if (i == ARRAY_SIZE(watchdog_minors)) {
-               pr_warn("Couldn't register watchdog (no free minor)\n");
-               goto error;
+               kfree(data);
+               return NULL;
        }
 
        return data;
-
-error:
-       kfree(data);
-       return NULL;
 }
 EXPORT_SYMBOL(sch56xx_watchdog_register);
 
 void sch56xx_watchdog_unregister(struct sch56xx_watchdog_data *data)
 {
-       mutex_lock(&watchdog_data_mutex);
-       misc_deregister(&data->watchdog_miscdev);
-       list_del(&data->list);
-       mutex_unlock(&watchdog_data_mutex);
-
-       mutex_lock(&data->watchdog_lock);
-       if (data->watchdog_is_open) {
-               pr_warn("platform device unregistered with watchdog "
-                       "open! Stopping watchdog.\n");
-               watchdog_stop_unlocked(data);
-       }
-       /* Tell the wdog start/stop/trigger functions our dev is gone */
-       data->addr = 0;
-       data->io_lock = NULL;
-       mutex_unlock(&data->watchdog_lock);
-
-       mutex_lock(&watchdog_data_mutex);
-       kref_put(&data->kref, sch56xx_watchdog_release_resources);
-       mutex_unlock(&watchdog_data_mutex);
+       watchdog_unregister_device(&data->wddev);
+       kref_put(&data->kref, watchdog_release_resources);
+       /* Don't touch data after this it may have been free-ed! */
 }
 EXPORT_SYMBOL(sch56xx_watchdog_unregister);
 
index 7475086eb978e148f2e5c8039f7bedad2052b0ea..704ea2c6d28a772695d73000f1480032dcd649e8 100644 (file)
@@ -27,6 +27,6 @@ int sch56xx_read_virtual_reg16(u16 addr, u16 reg);
 int sch56xx_read_virtual_reg12(u16 addr, u16 msb_reg, u16 lsn_reg,
                               int high_nibble);
 
-struct sch56xx_watchdog_data *sch56xx_watchdog_register(
+struct sch56xx_watchdog_data *sch56xx_watchdog_register(struct device *parent,
        u16 addr, u32 revision, struct mutex *io_lock, int check_enabled);
 void sch56xx_watchdog_unregister(struct sch56xx_watchdog_data *data);
index 7f0b83219744e52aa7cd68a7ac5ea6edc49537d1..fad22b0bb5b06fff58eb7f5fd49533fd841a7464 100644 (file)
@@ -608,7 +608,7 @@ bailout:
 
 static u32 bit_func(struct i2c_adapter *adap)
 {
-       return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL |
+       return I2C_FUNC_I2C | I2C_FUNC_NOSTART | I2C_FUNC_SMBUS_EMUL |
               I2C_FUNC_SMBUS_READ_BLOCK_DATA |
               I2C_FUNC_SMBUS_BLOCK_PROC_CALL |
               I2C_FUNC_10BIT_ADDR | I2C_FUNC_PROTOCOL_MANGLING;
index 03b61577888748a4d9a61cfe46b413eaaa219737..a26dfb8cd58690ce3df06c1c1685118899259a11 100644 (file)
@@ -502,7 +502,8 @@ static int nuc900_i2c_xfer(struct i2c_adapter *adap,
 /* declare our i2c functionality */
 static u32 nuc900_i2c_func(struct i2c_adapter *adap)
 {
-       return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_PROTOCOL_MANGLING;
+       return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_NOSTART |
+               I2C_FUNC_PROTOCOL_MANGLING;
 }
 
 /* i2c bus registration info */
index fa0b134908731019a8490566fb7c0f6e4fa10f24..01959154572d88f0eb954327759fc66bcf9da5c8 100644 (file)
@@ -626,7 +626,8 @@ static int s3c24xx_i2c_xfer(struct i2c_adapter *adap,
 /* declare our i2c functionality */
 static u32 s3c24xx_i2c_func(struct i2c_adapter *adap)
 {
-       return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_PROTOCOL_MANGLING;
+       return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_NOSTART |
+               I2C_FUNC_PROTOCOL_MANGLING;
 }
 
 /* i2c bus registration info */
index 45048323b75eab01d5875cb05464207c2eb614b3..5ec2261574ec4fdb7e2b6fd3d8b3879c6ff187f0 100644 (file)
@@ -265,19 +265,41 @@ static noinline int i2cdev_ioctl_rdrw(struct i2c_client *client,
 
        res = 0;
        for (i = 0; i < rdwr_arg.nmsgs; i++) {
-               /* Limit the size of the message to a sane amount;
-                * and don't let length change either. */
-               if ((rdwr_pa[i].len > 8192) ||
-                   (rdwr_pa[i].flags & I2C_M_RECV_LEN)) {
+               /* Limit the size of the message to a sane amount */
+               if (rdwr_pa[i].len > 8192) {
                        res = -EINVAL;
                        break;
                }
+
                data_ptrs[i] = (u8 __user *)rdwr_pa[i].buf;
                rdwr_pa[i].buf = memdup_user(data_ptrs[i], rdwr_pa[i].len);
                if (IS_ERR(rdwr_pa[i].buf)) {
                        res = PTR_ERR(rdwr_pa[i].buf);
                        break;
                }
+
+               /*
+                * If the message length is received from the slave (similar
+                * to SMBus block read), we must ensure that the buffer will
+                * be large enough to cope with a message length of
+                * I2C_SMBUS_BLOCK_MAX as this is the maximum underlying bus
+                * drivers allow. The first byte in the buffer must be
+                * pre-filled with the number of extra bytes, which must be
+                * at least one to hold the message length, but can be
+                * greater (for example to account for a checksum byte at
+                * the end of the message.)
+                */
+               if (rdwr_pa[i].flags & I2C_M_RECV_LEN) {
+                       if (!(rdwr_pa[i].flags & I2C_M_RD) ||
+                           rdwr_pa[i].buf[0] < 1 ||
+                           rdwr_pa[i].len < rdwr_pa[i].buf[0] +
+                                            I2C_SMBUS_BLOCK_MAX) {
+                               res = -EINVAL;
+                               break;
+                       }
+
+                       rdwr_pa[i].len = rdwr_pa[i].buf[0];
+               }
        }
        if (res < 0) {
                int j;
index 3063464474bf637e1a65d8dd52c144bb95ffba1d..57d19d4e0a2d94a22f68d13c6f0ee44a9ab8807b 100644 (file)
@@ -231,6 +231,7 @@ static int __devinit as5011_probe(struct i2c_client *client,
        }
 
        if (!i2c_check_functionality(client->adapter,
+                                    I2C_FUNC_NOSTART |
                                     I2C_FUNC_PROTOCOL_MANGLING)) {
                dev_err(&client->dev,
                        "need i2c bus that supports protocol mangling\n");
index 47f18d6bce46826160e653821d7be213223c4bb4..6790a812a1db7ecf66b2c990687e162211624537 100644 (file)
@@ -73,7 +73,7 @@ static int __devinit wm831x_on_probe(struct platform_device *pdev)
 {
        struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
        struct wm831x_on *wm831x_on;
-       int irq = platform_get_irq(pdev, 0);
+       int irq = wm831x_irq(wm831x, platform_get_irq(pdev, 0));
        int ret;
 
        wm831x_on = kzalloc(sizeof(struct wm831x_on), GFP_KERNEL);
index 4bc851a9dc3d6cf821f1c8ec05f7abf37fb4c781..e83410721e38b79f6d9a7a93e6f6a689ef73f1ab 100644 (file)
@@ -260,15 +260,16 @@ static __devinit int wm831x_ts_probe(struct platform_device *pdev)
         * If we have a direct IRQ use it, otherwise use the interrupt
         * from the WM831x IRQ controller.
         */
+       wm831x_ts->data_irq = wm831x_irq(wm831x,
+                                        platform_get_irq_byname(pdev,
+                                                                "TCHDATA"));
        if (pdata && pdata->data_irq)
                wm831x_ts->data_irq = pdata->data_irq;
-       else
-               wm831x_ts->data_irq = platform_get_irq_byname(pdev, "TCHDATA");
 
+       wm831x_ts->pd_irq = wm831x_irq(wm831x,
+                                      platform_get_irq_byname(pdev, "TCHPD"));
        if (pdata && pdata->pd_irq)
                wm831x_ts->pd_irq = pdata->pd_irq;
-       else
-               wm831x_ts->pd_irq = platform_get_irq_byname(pdev, "TCHPD");
 
        if (pdata)
                wm831x_ts->pressure = pdata->pressure;
index a5bee8e2dfce7904b637f8fd3d9871c75b7824a5..d90a421e9caccbbfc38a3d2eb4dd20ef2a50c77b 100644 (file)
@@ -450,12 +450,27 @@ static void dump_command(unsigned long phys_addr)
 
 static void iommu_print_event(struct amd_iommu *iommu, void *__evt)
 {
-       u32 *event = __evt;
-       int type  = (event[1] >> EVENT_TYPE_SHIFT)  & EVENT_TYPE_MASK;
-       int devid = (event[0] >> EVENT_DEVID_SHIFT) & EVENT_DEVID_MASK;
-       int domid = (event[1] >> EVENT_DOMID_SHIFT) & EVENT_DOMID_MASK;
-       int flags = (event[1] >> EVENT_FLAGS_SHIFT) & EVENT_FLAGS_MASK;
-       u64 address = (u64)(((u64)event[3]) << 32) | event[2];
+       int type, devid, domid, flags;
+       volatile u32 *event = __evt;
+       int count = 0;
+       u64 address;
+
+retry:
+       type    = (event[1] >> EVENT_TYPE_SHIFT)  & EVENT_TYPE_MASK;
+       devid   = (event[0] >> EVENT_DEVID_SHIFT) & EVENT_DEVID_MASK;
+       domid   = (event[1] >> EVENT_DOMID_SHIFT) & EVENT_DOMID_MASK;
+       flags   = (event[1] >> EVENT_FLAGS_SHIFT) & EVENT_FLAGS_MASK;
+       address = (u64)(((u64)event[3]) << 32) | event[2];
+
+       if (type == 0) {
+               /* Did we hit the erratum? */
+               if (++count == LOOP_TIMEOUT) {
+                       pr_err("AMD-Vi: No event written to event log\n");
+                       return;
+               }
+               udelay(1);
+               goto retry;
+       }
 
        printk(KERN_ERR "AMD-Vi: Event logged [");
 
@@ -508,6 +523,8 @@ static void iommu_print_event(struct amd_iommu *iommu, void *__evt)
        default:
                printk(KERN_ERR "UNKNOWN type=0x%02x]\n", type);
        }
+
+       memset(__evt, 0, 4 * sizeof(u32));
 }
 
 static void iommu_poll_events(struct amd_iommu *iommu)
@@ -2035,20 +2052,20 @@ out_err:
 }
 
 /* FIXME: Move this to PCI code */
-#define PCI_PRI_TLP_OFF                (1 << 2)
+#define PCI_PRI_TLP_OFF                (1 << 15)
 
 bool pci_pri_tlp_required(struct pci_dev *pdev)
 {
-       u16 control;
+       u16 status;
        int pos;
 
        pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PRI);
        if (!pos)
                return false;
 
-       pci_read_config_word(pdev, pos + PCI_PRI_CTRL, &control);
+       pci_read_config_word(pdev, pos + PCI_PRI_STATUS, &status);
 
-       return (control & PCI_PRI_TLP_OFF) ? true : false;
+       return (status & PCI_PRI_TLP_OFF) ? true : false;
 }
 
 /*
index 2198b2dbbcd3ad964b03a13dd6fd8dd336f27bed..8b9ded88e6f5322c18c02ec6e685b05d8decfd29 100644 (file)
@@ -119,6 +119,7 @@ EXPORT_SYMBOL_GPL(iommu_present);
  * iommu_set_fault_handler() - set a fault handler for an iommu domain
  * @domain: iommu domain
  * @handler: fault handler
+ * @token: user data, will be passed back to the fault handler
  *
  * This function should be used by IOMMU users which want to be notified
  * whenever an IOMMU fault happens.
@@ -127,11 +128,13 @@ EXPORT_SYMBOL_GPL(iommu_present);
  * error code otherwise.
  */
 void iommu_set_fault_handler(struct iommu_domain *domain,
-                                       iommu_fault_handler_t handler)
+                                       iommu_fault_handler_t handler,
+                                       void *token)
 {
        BUG_ON(!domain);
 
        domain->handler = handler;
+       domain->handler_token = token;
 }
 EXPORT_SYMBOL_GPL(iommu_set_fault_handler);
 
index 6899dcd02dfa0e35df014c42651ba4c5b4f6bb67..e70ee2b59df95b9427c4c1594384776d97103015 100644 (file)
  * @pgtable:   the page table
  * @iommu_dev: an omap iommu device attached to this domain. only a single
  *             iommu device can be attached for now.
+ * @dev:       Device using this domain.
  * @lock:      domain lock, should be taken when attaching/detaching
  */
 struct omap_iommu_domain {
        u32 *pgtable;
        struct omap_iommu *iommu_dev;
+       struct device *dev;
        spinlock_t lock;
 };
 
@@ -1081,6 +1083,7 @@ omap_iommu_attach_dev(struct iommu_domain *domain, struct device *dev)
        }
 
        omap_domain->iommu_dev = arch_data->iommu_dev = oiommu;
+       omap_domain->dev = dev;
        oiommu->domain = domain;
 
 out:
@@ -1088,19 +1091,16 @@ out:
        return ret;
 }
 
-static void omap_iommu_detach_dev(struct iommu_domain *domain,
-                                struct device *dev)
+static void _omap_iommu_detach_dev(struct omap_iommu_domain *omap_domain,
+                       struct device *dev)
 {
-       struct omap_iommu_domain *omap_domain = domain->priv;
-       struct omap_iommu_arch_data *arch_data = dev->archdata.iommu;
        struct omap_iommu *oiommu = dev_to_omap_iommu(dev);
-
-       spin_lock(&omap_domain->lock);
+       struct omap_iommu_arch_data *arch_data = dev->archdata.iommu;
 
        /* only a single device is supported per domain for now */
        if (omap_domain->iommu_dev != oiommu) {
                dev_err(dev, "invalid iommu device\n");
-               goto out;
+               return;
        }
 
        iopgtable_clear_entry_all(oiommu);
@@ -1108,8 +1108,16 @@ static void omap_iommu_detach_dev(struct iommu_domain *domain,
        omap_iommu_detach(oiommu);
 
        omap_domain->iommu_dev = arch_data->iommu_dev = NULL;
+       omap_domain->dev = NULL;
+}
 
-out:
+static void omap_iommu_detach_dev(struct iommu_domain *domain,
+                                struct device *dev)
+{
+       struct omap_iommu_domain *omap_domain = domain->priv;
+
+       spin_lock(&omap_domain->lock);
+       _omap_iommu_detach_dev(omap_domain, dev);
        spin_unlock(&omap_domain->lock);
 }
 
@@ -1148,13 +1156,19 @@ out:
        return -ENOMEM;
 }
 
-/* assume device was already detached */
 static void omap_iommu_domain_destroy(struct iommu_domain *domain)
 {
        struct omap_iommu_domain *omap_domain = domain->priv;
 
        domain->priv = NULL;
 
+       /*
+        * An iommu device is still attached
+        * (currently, only one device can be attached) ?
+        */
+       if (omap_domain->iommu_dev)
+               _omap_iommu_detach_dev(omap_domain, omap_domain->dev);
+
        kfree(omap_domain->pgtable);
        kfree(omap_domain);
 }
index 779306ee7b160f1608e13179a27f244b3726771f..0c0a37792218452fb9d48f7b84364604564669b0 100644 (file)
 #include <linux/device.h>
 #include <linux/io.h>
 #include <linux/iommu.h>
+#include <linux/of.h>
 
 #include <asm/cacheflush.h>
 
 /* bitmap of the page sizes currently supported */
 #define GART_IOMMU_PGSIZES     (SZ_4K)
 
-#define GART_CONFIG            0x24
-#define GART_ENTRY_ADDR                0x28
-#define GART_ENTRY_DATA                0x2c
+#define GART_REG_BASE          0x24
+#define GART_CONFIG            (0x24 - GART_REG_BASE)
+#define GART_ENTRY_ADDR                (0x28 - GART_REG_BASE)
+#define GART_ENTRY_DATA                (0x2c - GART_REG_BASE)
 #define GART_ENTRY_PHYS_ADDR_VALID     (1 << 31)
 
 #define GART_PAGE_SHIFT                12
@@ -158,7 +160,7 @@ static int gart_iommu_attach_dev(struct iommu_domain *domain,
        struct gart_client *client, *c;
        int err = 0;
 
-       gart = dev_get_drvdata(dev->parent);
+       gart = gart_handle;
        if (!gart)
                return -EINVAL;
        domain->priv = gart;
@@ -422,6 +424,14 @@ const struct dev_pm_ops tegra_gart_pm_ops = {
        .resume         = tegra_gart_resume,
 };
 
+#ifdef CONFIG_OF
+static struct of_device_id tegra_gart_of_match[] __devinitdata = {
+       { .compatible = "nvidia,tegra20-gart", },
+       { },
+};
+MODULE_DEVICE_TABLE(of, tegra_gart_of_match);
+#endif
+
 static struct platform_driver tegra_gart_driver = {
        .probe          = tegra_gart_probe,
        .remove         = tegra_gart_remove,
@@ -429,6 +439,7 @@ static struct platform_driver tegra_gart_driver = {
                .owner  = THIS_MODULE,
                .name   = "tegra-gart",
                .pm     = &tegra_gart_pm_ops,
+               .of_match_table = of_match_ptr(tegra_gart_of_match),
        },
 };
 
@@ -448,4 +459,5 @@ module_exit(tegra_gart_exit);
 
 MODULE_DESCRIPTION("IOMMU API for GART in Tegra20");
 MODULE_AUTHOR("Hiroshi DOYU <hdoyu@nvidia.com>");
+MODULE_ALIAS("platform:tegra-gart");
 MODULE_LICENSE("GPL v2");
index eb93c821f592d8ad87fa129b8b430d3ccac17976..ecd679043d7740e6883aae9cbee68da6321fedc1 100644 (file)
@@ -733,7 +733,7 @@ static int smmu_iommu_attach_dev(struct iommu_domain *domain,
                pr_info("Reserve \"page zero\" for AVP vectors using a common dummy\n");
        }
 
-       dev_dbg(smmu->dev, "%s is attached\n", dev_name(c->dev));
+       dev_dbg(smmu->dev, "%s is attached\n", dev_name(dev));
        return 0;
 
 err_client:
index ff4b8cfda585b6461309824f277e52d263e0dcb6..04cb8c88d74b7678d12389898ba0702cf0c17b55 100644 (file)
@@ -50,6 +50,19 @@ config LEDS_LM3530
          controlled manually or using PWM input or using ambient
          light automatically.
 
+config LEDS_LM3533
+       tristate "LED support for LM3533"
+       depends on LEDS_CLASS
+       depends on MFD_LM3533
+       help
+         This option enables support for the LEDs on National Semiconductor /
+         TI LM3533 Lighting Power chips.
+
+         The LEDs can be controlled directly, through PWM input, or by the
+         ambient-light-sensor interface. The chip supports
+         hardware-accelerated blinking with maximum on and off periods of 9.8
+         and 77 seconds respectively.
+
 config LEDS_LOCOMO
        tristate "LED Support for Locomo device"
        depends on LEDS_CLASS
@@ -259,6 +272,14 @@ config LEDS_DA903X
          This option enables support for on-chip LED drivers found
          on Dialog Semiconductor DA9030/DA9034 PMICs.
 
+config LEDS_DA9052
+       tristate "Dialog DA9052/DA9053 LEDS"
+       depends on LEDS_CLASS
+       depends on PMIC_DA9052
+       help
+         This option enables support for on-chip LED drivers found
+         on Dialog Semiconductor DA9052-BC and DA9053-AA/Bx PMICs.
+
 config LEDS_DAC124S085
        tristate "LED Support for DAC124S085 SPI DAC"
        depends on LEDS_CLASS
@@ -471,4 +492,12 @@ config LEDS_TRIGGER_DEFAULT_ON
 comment "iptables trigger is under Netfilter config (LED target)"
        depends on LEDS_TRIGGERS
 
+config LEDS_TRIGGER_TRANSIENT
+       tristate "LED Transient Trigger"
+       depends on LEDS_TRIGGERS
+       help
+         This allows one time activation of a transient state on
+         GPIO/PWM based hadrware.
+         If unsure, say Y.
+
 endif # NEW_LEDS
index 890481cb09f6b23aa762a242b4a9b4e98998fb91..f8958cd6cf6e82813483aaf52a5ff3cde1e684a5 100644 (file)
@@ -10,6 +10,7 @@ obj-$(CONFIG_LEDS_ATMEL_PWM)          += leds-atmel-pwm.o
 obj-$(CONFIG_LEDS_BD2802)              += leds-bd2802.o
 obj-$(CONFIG_LEDS_LOCOMO)              += leds-locomo.o
 obj-$(CONFIG_LEDS_LM3530)              += leds-lm3530.o
+obj-$(CONFIG_LEDS_LM3533)              += leds-lm3533.o
 obj-$(CONFIG_LEDS_MIKROTIK_RB532)      += leds-rb532.o
 obj-$(CONFIG_LEDS_S3C24XX)             += leds-s3c24xx.o
 obj-$(CONFIG_LEDS_NET48XX)             += leds-net48xx.o
@@ -31,6 +32,7 @@ obj-$(CONFIG_LEDS_FSG)                        += leds-fsg.o
 obj-$(CONFIG_LEDS_PCA955X)             += leds-pca955x.o
 obj-$(CONFIG_LEDS_PCA9633)             += leds-pca9633.o
 obj-$(CONFIG_LEDS_DA903X)              += leds-da903x.o
+obj-$(CONFIG_LEDS_DA9052)              += leds-da9052.o
 obj-$(CONFIG_LEDS_WM831X_STATUS)       += leds-wm831x-status.o
 obj-$(CONFIG_LEDS_WM8350)              += leds-wm8350.o
 obj-$(CONFIG_LEDS_PWM)                 += leds-pwm.o
@@ -56,3 +58,4 @@ obj-$(CONFIG_LEDS_TRIGGER_HEARTBEAT)  += ledtrig-heartbeat.o
 obj-$(CONFIG_LEDS_TRIGGER_BACKLIGHT)   += ledtrig-backlight.o
 obj-$(CONFIG_LEDS_TRIGGER_GPIO)                += ledtrig-gpio.o
 obj-$(CONFIG_LEDS_TRIGGER_DEFAULT_ON)  += ledtrig-default-on.o
+obj-$(CONFIG_LEDS_TRIGGER_TRANSIENT)   += ledtrig-transient.o
index 5bff8439dc68a7e8c6424f0cbed3746cd9820efb..8ee92c81aec2c1577c3fb1cfd173d8835c2c7725 100644 (file)
@@ -44,23 +44,18 @@ static ssize_t led_brightness_store(struct device *dev,
                struct device_attribute *attr, const char *buf, size_t size)
 {
        struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       unsigned long state;
        ssize_t ret = -EINVAL;
-       char *after;
-       unsigned long state = simple_strtoul(buf, &after, 10);
-       size_t count = after - buf;
 
-       if (isspace(*after))
-               count++;
+       ret = kstrtoul(buf, 10, &state);
+       if (ret)
+               return ret;
 
-       if (count == size) {
-               ret = count;
+       if (state == LED_OFF)
+               led_trigger_remove(led_cdev);
+       led_set_brightness(led_cdev, state);
 
-               if (state == LED_OFF)
-                       led_trigger_remove(led_cdev);
-               led_set_brightness(led_cdev, state);
-       }
-
-       return ret;
+       return size;
 }
 
 static ssize_t led_max_brightness_show(struct device *dev,
diff --git a/drivers/leds/leds-da9052.c b/drivers/leds/leds-da9052.c
new file mode 100644 (file)
index 0000000..58a5244
--- /dev/null
@@ -0,0 +1,214 @@
+/*
+ * LED Driver for Dialog DA9052 PMICs.
+ *
+ * Copyright(c) 2012 Dialog Semiconductor Ltd.
+ *
+ * Author: David Dajun Chen <dchen@diasemi.com>
+ *
+ *  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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/leds.h>
+#include <linux/workqueue.h>
+#include <linux/slab.h>
+
+#include <linux/mfd/da9052/reg.h>
+#include <linux/mfd/da9052/da9052.h>
+#include <linux/mfd/da9052/pdata.h>
+
+#define DA9052_OPENDRAIN_OUTPUT        2
+#define DA9052_SET_HIGH_LVL_OUTPUT     (1 << 3)
+#define DA9052_MASK_UPPER_NIBBLE       0xF0
+#define DA9052_MASK_LOWER_NIBBLE       0x0F
+#define DA9052_NIBBLE_SHIFT            4
+#define DA9052_MAX_BRIGHTNESS          0x5f
+
+struct da9052_led {
+       struct led_classdev cdev;
+       struct work_struct work;
+       struct da9052 *da9052;
+       unsigned char led_index;
+       unsigned char id;
+       int brightness;
+};
+
+static unsigned char led_reg[] = {
+       DA9052_LED_CONT_4_REG,
+       DA9052_LED_CONT_5_REG,
+};
+
+static int da9052_set_led_brightness(struct da9052_led *led)
+{
+       u8 val;
+       int error;
+
+       val = (led->brightness & 0x7f) | DA9052_LED_CONT_DIM;
+
+       error = da9052_reg_write(led->da9052, led_reg[led->led_index], val);
+       if (error < 0)
+               dev_err(led->da9052->dev, "Failed to set led brightness, %d\n",
+                       error);
+       return error;
+}
+
+static void da9052_led_work(struct work_struct *work)
+{
+       struct da9052_led *led = container_of(work, struct da9052_led, work);
+
+       da9052_set_led_brightness(led);
+}
+
+static void da9052_led_set(struct led_classdev *led_cdev,
+                          enum led_brightness value)
+{
+       struct da9052_led *led;
+
+       led = container_of(led_cdev, struct da9052_led, cdev);
+       led->brightness = value;
+       schedule_work(&led->work);
+}
+
+static int da9052_configure_leds(struct da9052 *da9052)
+{
+       int error;
+       unsigned char register_value = DA9052_OPENDRAIN_OUTPUT
+                                      | DA9052_SET_HIGH_LVL_OUTPUT;
+
+       error = da9052_reg_update(da9052, DA9052_GPIO_14_15_REG,
+                                 DA9052_MASK_LOWER_NIBBLE,
+                                 register_value);
+
+       if (error < 0) {
+               dev_err(da9052->dev, "Failed to write GPIO 14-15 reg, %d\n",
+                       error);
+               return error;
+       }
+
+       error = da9052_reg_update(da9052, DA9052_GPIO_14_15_REG,
+                                 DA9052_MASK_UPPER_NIBBLE,
+                                 register_value << DA9052_NIBBLE_SHIFT);
+       if (error < 0)
+               dev_err(da9052->dev, "Failed to write GPIO 14-15 reg, %d\n",
+                       error);
+
+       return error;
+}
+
+static int __devinit da9052_led_probe(struct platform_device *pdev)
+{
+       struct da9052_pdata *pdata;
+       struct da9052 *da9052;
+       struct led_platform_data *pled;
+       struct da9052_led *led = NULL;
+       int error = -ENODEV;
+       int i;
+
+       da9052 = dev_get_drvdata(pdev->dev.parent);
+       pdata = da9052->dev->platform_data;
+       if (pdata == NULL) {
+               dev_err(&pdev->dev, "No platform data\n");
+               goto err;
+       }
+
+       pled = pdata->pled;
+       if (pled == NULL) {
+               dev_err(&pdev->dev, "No platform data for LED\n");
+               goto err;
+       }
+
+       led = devm_kzalloc(&pdev->dev,
+                          sizeof(struct da9052_led) * pled->num_leds,
+                          GFP_KERNEL);
+       if (led == NULL) {
+               dev_err(&pdev->dev, "Failed to alloc memory\n");
+               error = -ENOMEM;
+               goto err;
+       }
+
+       for (i = 0; i < pled->num_leds; i++) {
+               led[i].cdev.name = pled->leds[i].name;
+               led[i].cdev.brightness_set = da9052_led_set;
+               led[i].cdev.brightness = LED_OFF;
+               led[i].cdev.max_brightness = DA9052_MAX_BRIGHTNESS;
+               led[i].brightness = LED_OFF;
+               led[i].led_index = pled->leds[i].flags;
+               led[i].da9052 = dev_get_drvdata(pdev->dev.parent);
+               INIT_WORK(&led[i].work, da9052_led_work);
+
+               error = led_classdev_register(pdev->dev.parent, &led[i].cdev);
+               if (error) {
+                       dev_err(&pdev->dev, "Failed to register led %d\n",
+                               led[i].led_index);
+                       goto err_register;
+               }
+
+               error = da9052_set_led_brightness(&led[i]);
+               if (error) {
+                       dev_err(&pdev->dev, "Unable to init led %d\n",
+                               led[i].led_index);
+                       continue;
+               }
+       }
+       error = da9052_configure_leds(led->da9052);
+       if (error) {
+               dev_err(&pdev->dev, "Failed to configure GPIO LED%d\n", error);
+               goto err_register;
+       }
+
+       platform_set_drvdata(pdev, led);
+
+       return 0;
+
+err_register:
+       for (i = i - 1; i >= 0; i--) {
+               led_classdev_unregister(&led[i].cdev);
+               cancel_work_sync(&led[i].work);
+       }
+err:
+       return error;
+}
+
+static int __devexit da9052_led_remove(struct platform_device *pdev)
+{
+       struct da9052_led *led = platform_get_drvdata(pdev);
+       struct da9052_pdata *pdata;
+       struct da9052 *da9052;
+       struct led_platform_data *pled;
+       int i;
+
+       da9052 = dev_get_drvdata(pdev->dev.parent);
+       pdata = da9052->dev->platform_data;
+       pled = pdata->pled;
+
+       for (i = 0; i < pled->num_leds; i++) {
+               led[i].brightness = 0;
+               da9052_set_led_brightness(&led[i]);
+               led_classdev_unregister(&led[i].cdev);
+               cancel_work_sync(&led[i].work);
+       }
+
+       return 0;
+}
+
+static struct platform_driver da9052_led_driver = {
+       .driver         = {
+               .name   = "da9052-leds",
+               .owner  = THIS_MODULE,
+       },
+       .probe          = da9052_led_probe,
+       .remove         = __devexit_p(da9052_led_remove),
+};
+
+module_platform_driver(da9052_led_driver);
+
+MODULE_AUTHOR("Dialog Semiconductor Ltd <dchen@diasemi.com>");
+MODULE_DESCRIPTION("LED driver for Dialog DA9052 PMIC");
+MODULE_LICENSE("GPL");
index 968fd5fef4fc5e2871dc7b5539f80b6b5328ea1b..84ba6de8039c8334558175d231bc6c062ff8c579 100644 (file)
@@ -113,6 +113,18 @@ struct lm3530_data {
        bool enable;
 };
 
+/*
+ * struct lm3530_als_data
+ * @config  : value of ALS configuration register
+ * @imp_sel : value of ALS resistor select register
+ * @zone    : values of ALS ZB(Zone Boundary) registers
+ */
+struct lm3530_als_data {
+       u8 config;
+       u8 imp_sel;
+       u8 zones[LM3530_ALS_ZB_MAX];
+};
+
 static const u8 lm3530_reg[LM3530_REG_MAX] = {
        LM3530_GEN_CONFIG,
        LM3530_ALS_CONFIG,
@@ -141,29 +153,65 @@ static int lm3530_get_mode_from_str(const char *str)
        return -1;
 }
 
+static void lm3530_als_configure(struct lm3530_platform_data *pdata,
+                               struct lm3530_als_data *als)
+{
+       int i;
+       u32 als_vmin, als_vmax, als_vstep;
+
+       if (pdata->als_vmax == 0) {
+               pdata->als_vmin = 0;
+               pdata->als_vmax = LM3530_ALS_WINDOW_mV;
+       }
+
+       als_vmin = pdata->als_vmin;
+       als_vmax = pdata->als_vmax;
+
+       if ((als_vmax - als_vmin) > LM3530_ALS_WINDOW_mV)
+               pdata->als_vmax = als_vmax = als_vmin + LM3530_ALS_WINDOW_mV;
+
+       /* n zone boundary makes n+1 zones */
+       als_vstep = (als_vmax - als_vmin) / (LM3530_ALS_ZB_MAX + 1);
+
+       for (i = 0; i < LM3530_ALS_ZB_MAX; i++)
+               als->zones[i] = (((als_vmin + LM3530_ALS_OFFSET_mV) +
+                       als_vstep + (i * als_vstep)) * LED_FULL) / 1000;
+
+       als->config =
+               (pdata->als_avrg_time << LM3530_ALS_AVG_TIME_SHIFT) |
+               (LM3530_ENABLE_ALS) |
+               (pdata->als_input_mode << LM3530_ALS_SEL_SHIFT);
+
+       als->imp_sel =
+               (pdata->als1_resistor_sel << LM3530_ALS1_IMP_SHIFT) |
+               (pdata->als2_resistor_sel << LM3530_ALS2_IMP_SHIFT);
+}
+
 static int lm3530_init_registers(struct lm3530_data *drvdata)
 {
        int ret = 0;
        int i;
        u8 gen_config;
-       u8 als_config = 0;
        u8 brt_ramp;
-       u8 als_imp_sel = 0;
        u8 brightness;
        u8 reg_val[LM3530_REG_MAX];
-       u8 zones[LM3530_ALS_ZB_MAX];
-       u32 als_vmin, als_vmax, als_vstep;
        struct lm3530_platform_data *pdata = drvdata->pdata;
        struct i2c_client *client = drvdata->client;
        struct lm3530_pwm_data *pwm = &pdata->pwm_data;
+       struct lm3530_als_data als;
+
+       memset(&als, 0, sizeof(struct lm3530_als_data));
 
        gen_config = (pdata->brt_ramp_law << LM3530_RAMP_LAW_SHIFT) |
                        ((pdata->max_current & 7) << LM3530_MAX_CURR_SHIFT);
 
        switch (drvdata->mode) {
        case LM3530_BL_MODE_MANUAL:
+               gen_config |= LM3530_ENABLE_I2C;
+               break;
        case LM3530_BL_MODE_ALS:
                gen_config |= LM3530_ENABLE_I2C;
+               lm3530_als_configure(pdata, &als);
                break;
        case LM3530_BL_MODE_PWM:
                gen_config |= LM3530_ENABLE_PWM | LM3530_ENABLE_PWM_SIMPLE |
@@ -171,38 +219,6 @@ static int lm3530_init_registers(struct lm3530_data *drvdata)
                break;
        }
 
-       if (drvdata->mode == LM3530_BL_MODE_ALS) {
-               if (pdata->als_vmax == 0) {
-                       pdata->als_vmin = 0;
-                       pdata->als_vmax = LM3530_ALS_WINDOW_mV;
-               }
-
-               als_vmin = pdata->als_vmin;
-               als_vmax = pdata->als_vmax;
-
-               if ((als_vmax - als_vmin) > LM3530_ALS_WINDOW_mV)
-                       pdata->als_vmax = als_vmax =
-                               als_vmin + LM3530_ALS_WINDOW_mV;
-
-               /* n zone boundary makes n+1 zones */
-               als_vstep = (als_vmax - als_vmin) / (LM3530_ALS_ZB_MAX + 1);
-
-               for (i = 0; i < LM3530_ALS_ZB_MAX; i++)
-                       zones[i] = (((als_vmin + LM3530_ALS_OFFSET_mV) +
-                                       als_vstep + (i * als_vstep)) * LED_FULL)
-                                       / 1000;
-
-               als_config =
-                       (pdata->als_avrg_time << LM3530_ALS_AVG_TIME_SHIFT) |
-                       (LM3530_ENABLE_ALS) |
-                       (pdata->als_input_mode << LM3530_ALS_SEL_SHIFT);
-
-               als_imp_sel =
-                       (pdata->als1_resistor_sel << LM3530_ALS1_IMP_SHIFT) |
-                       (pdata->als2_resistor_sel << LM3530_ALS2_IMP_SHIFT);
-
-       }
-
        brt_ramp = (pdata->brt_ramp_fall << LM3530_BRT_RAMP_FALL_SHIFT) |
                        (pdata->brt_ramp_rise << LM3530_BRT_RAMP_RISE_SHIFT);
 
@@ -215,14 +231,14 @@ static int lm3530_init_registers(struct lm3530_data *drvdata)
                brightness = drvdata->led_dev.max_brightness;
 
        reg_val[0] = gen_config;        /* LM3530_GEN_CONFIG */
-       reg_val[1] = als_config;        /* LM3530_ALS_CONFIG */
+       reg_val[1] = als.config;        /* LM3530_ALS_CONFIG */
        reg_val[2] = brt_ramp;          /* LM3530_BRT_RAMP_RATE */
-       reg_val[3] = als_imp_sel;       /* LM3530_ALS_IMP_SELECT */
+       reg_val[3] = als.imp_sel;       /* LM3530_ALS_IMP_SELECT */
        reg_val[4] = brightness;        /* LM3530_BRT_CTRL_REG */
-       reg_val[5] = zones[0];          /* LM3530_ALS_ZB0_REG */
-       reg_val[6] = zones[1];          /* LM3530_ALS_ZB1_REG */
-       reg_val[7] = zones[2];          /* LM3530_ALS_ZB2_REG */
-       reg_val[8] = zones[3];          /* LM3530_ALS_ZB3_REG */
+       reg_val[5] = als.zones[0];      /* LM3530_ALS_ZB0_REG */
+       reg_val[6] = als.zones[1];      /* LM3530_ALS_ZB1_REG */
+       reg_val[7] = als.zones[2];      /* LM3530_ALS_ZB2_REG */
+       reg_val[8] = als.zones[3];      /* LM3530_ALS_ZB3_REG */
        reg_val[9] = LM3530_DEF_ZT_0;   /* LM3530_ALS_Z0T_REG */
        reg_val[10] = LM3530_DEF_ZT_1;  /* LM3530_ALS_Z1T_REG */
        reg_val[11] = LM3530_DEF_ZT_2;  /* LM3530_ALS_Z2T_REG */
diff --git a/drivers/leds/leds-lm3533.c b/drivers/leds/leds-lm3533.c
new file mode 100644 (file)
index 0000000..f56b6e7
--- /dev/null
@@ -0,0 +1,785 @@
+/*
+ * leds-lm3533.c -- LM3533 LED driver
+ *
+ * Copyright (C) 2011-2012 Texas Instruments
+ *
+ * Author: Johan Hovold <jhovold@gmail.com>
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/leds.h>
+#include <linux/mfd/core.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+
+#include <linux/mfd/lm3533.h>
+
+
+#define LM3533_LVCTRLBANK_MIN          2
+#define LM3533_LVCTRLBANK_MAX          5
+#define LM3533_LVCTRLBANK_COUNT                4
+#define LM3533_RISEFALLTIME_MAX                7
+#define LM3533_ALS_CHANNEL_LV_MIN      1
+#define LM3533_ALS_CHANNEL_LV_MAX      2
+
+#define LM3533_REG_CTRLBANK_BCONF_BASE         0x1b
+#define LM3533_REG_PATTERN_ENABLE              0x28
+#define LM3533_REG_PATTERN_LOW_TIME_BASE       0x71
+#define LM3533_REG_PATTERN_HIGH_TIME_BASE      0x72
+#define LM3533_REG_PATTERN_RISETIME_BASE       0x74
+#define LM3533_REG_PATTERN_FALLTIME_BASE       0x75
+
+#define LM3533_REG_PATTERN_STEP                        0x10
+
+#define LM3533_REG_CTRLBANK_BCONF_MAPPING_MASK         0x04
+#define LM3533_REG_CTRLBANK_BCONF_ALS_EN_MASK          0x02
+#define LM3533_REG_CTRLBANK_BCONF_ALS_CHANNEL_MASK     0x01
+
+#define LM3533_LED_FLAG_PATTERN_ENABLE         1
+
+
+struct lm3533_led {
+       struct lm3533 *lm3533;
+       struct lm3533_ctrlbank cb;
+       struct led_classdev cdev;
+       int id;
+
+       struct mutex mutex;
+       unsigned long flags;
+
+       struct work_struct work;
+       u8 new_brightness;
+};
+
+
+static inline struct lm3533_led *to_lm3533_led(struct led_classdev *cdev)
+{
+       return container_of(cdev, struct lm3533_led, cdev);
+}
+
+static inline int lm3533_led_get_ctrlbank_id(struct lm3533_led *led)
+{
+       return led->id + 2;
+}
+
+static inline u8 lm3533_led_get_lv_reg(struct lm3533_led *led, u8 base)
+{
+       return base + led->id;
+}
+
+static inline u8 lm3533_led_get_pattern(struct lm3533_led *led)
+{
+       return led->id;
+}
+
+static inline u8 lm3533_led_get_pattern_reg(struct lm3533_led *led,
+                                                               u8 base)
+{
+       return base + lm3533_led_get_pattern(led) * LM3533_REG_PATTERN_STEP;
+}
+
+static int lm3533_led_pattern_enable(struct lm3533_led *led, int enable)
+{
+       u8 mask;
+       u8 val;
+       int pattern;
+       int state;
+       int ret = 0;
+
+       dev_dbg(led->cdev.dev, "%s - %d\n", __func__, enable);
+
+       mutex_lock(&led->mutex);
+
+       state = test_bit(LM3533_LED_FLAG_PATTERN_ENABLE, &led->flags);
+       if ((enable && state) || (!enable && !state))
+               goto out;
+
+       pattern = lm3533_led_get_pattern(led);
+       mask = 1 << (2 * pattern);
+
+       if (enable)
+               val = mask;
+       else
+               val = 0;
+
+       ret = lm3533_update(led->lm3533, LM3533_REG_PATTERN_ENABLE, val, mask);
+       if (ret) {
+               dev_err(led->cdev.dev, "failed to enable pattern %d (%d)\n",
+                                                       pattern, enable);
+               goto out;
+       }
+
+       __change_bit(LM3533_LED_FLAG_PATTERN_ENABLE, &led->flags);
+out:
+       mutex_unlock(&led->mutex);
+
+       return ret;
+}
+
+static void lm3533_led_work(struct work_struct *work)
+{
+       struct lm3533_led *led = container_of(work, struct lm3533_led, work);
+
+       dev_dbg(led->cdev.dev, "%s - %u\n", __func__, led->new_brightness);
+
+       if (led->new_brightness == 0)
+               lm3533_led_pattern_enable(led, 0);      /* disable blink */
+
+       lm3533_ctrlbank_set_brightness(&led->cb, led->new_brightness);
+}
+
+static void lm3533_led_set(struct led_classdev *cdev,
+                                               enum led_brightness value)
+{
+       struct lm3533_led *led = to_lm3533_led(cdev);
+
+       dev_dbg(led->cdev.dev, "%s - %d\n", __func__, value);
+
+       led->new_brightness = value;
+       schedule_work(&led->work);
+}
+
+static enum led_brightness lm3533_led_get(struct led_classdev *cdev)
+{
+       struct lm3533_led *led = to_lm3533_led(cdev);
+       u8 val;
+       int ret;
+
+       ret = lm3533_ctrlbank_get_brightness(&led->cb, &val);
+       if (ret)
+               return ret;
+
+       dev_dbg(led->cdev.dev, "%s - %u\n", __func__, val);
+
+       return val;
+}
+
+/* Pattern generator defines (delays in us). */
+#define LM3533_LED_DELAY1_VMIN 0x00
+#define LM3533_LED_DELAY2_VMIN 0x3d
+#define LM3533_LED_DELAY3_VMIN 0x80
+
+#define LM3533_LED_DELAY1_VMAX (LM3533_LED_DELAY2_VMIN - 1)
+#define LM3533_LED_DELAY2_VMAX (LM3533_LED_DELAY3_VMIN - 1)
+#define LM3533_LED_DELAY3_VMAX 0xff
+
+#define LM3533_LED_DELAY1_TMIN 16384U
+#define LM3533_LED_DELAY2_TMIN 1130496U
+#define LM3533_LED_DELAY3_TMIN 10305536U
+
+#define LM3533_LED_DELAY1_TMAX 999424U
+#define LM3533_LED_DELAY2_TMAX 9781248U
+#define LM3533_LED_DELAY3_TMAX 76890112U
+
+/* t_step = (t_max - t_min) / (v_max - v_min) */
+#define LM3533_LED_DELAY1_TSTEP        16384
+#define LM3533_LED_DELAY2_TSTEP        131072
+#define LM3533_LED_DELAY3_TSTEP        524288
+
+/* Delay limits for hardware accelerated blinking (in ms). */
+#define LM3533_LED_DELAY_ON_MAX \
+       ((LM3533_LED_DELAY2_TMAX + LM3533_LED_DELAY2_TSTEP / 2) / 1000)
+#define LM3533_LED_DELAY_OFF_MAX \
+       ((LM3533_LED_DELAY3_TMAX + LM3533_LED_DELAY3_TSTEP / 2) / 1000)
+
+/*
+ * Returns linear map of *t from [t_min,t_max] to [v_min,v_max] with a step
+ * size of t_step, where
+ *
+ *     t_step = (t_max - t_min) / (v_max - v_min)
+ *
+ * and updates *t to reflect the mapped value.
+ */
+static u8 time_to_val(unsigned *t, unsigned t_min, unsigned t_step,
+                                                       u8 v_min, u8 v_max)
+{
+       unsigned val;
+
+       val = (*t + t_step / 2 - t_min) / t_step + v_min;
+
+       *t = t_step * (val - v_min) + t_min;
+
+       return (u8)val;
+}
+
+/*
+ * Returns time code corresponding to *delay (in ms) and updates *delay to
+ * reflect actual hardware delay.
+ *
+ * Hardware supports 256 discrete delay times, divided into three groups with
+ * the following ranges and step-sizes:
+ *
+ *     [   16,   999]  [0x00, 0x3e]    step  16 ms
+ *     [ 1130,  9781]  [0x3d, 0x7f]    step 131 ms
+ *     [10306, 76890]  [0x80, 0xff]    step 524 ms
+ *
+ * Note that delay group 3 is only available for delay_off.
+ */
+static u8 lm3533_led_get_hw_delay(unsigned *delay)
+{
+       unsigned t;
+       u8 val;
+
+       t = *delay * 1000;
+
+       if (t >= (LM3533_LED_DELAY2_TMAX + LM3533_LED_DELAY3_TMIN) / 2) {
+               t = clamp(t, LM3533_LED_DELAY3_TMIN, LM3533_LED_DELAY3_TMAX);
+               val = time_to_val(&t,   LM3533_LED_DELAY3_TMIN,
+                                       LM3533_LED_DELAY3_TSTEP,
+                                       LM3533_LED_DELAY3_VMIN,
+                                       LM3533_LED_DELAY3_VMAX);
+       } else if (t >= (LM3533_LED_DELAY1_TMAX + LM3533_LED_DELAY2_TMIN) / 2) {
+               t = clamp(t, LM3533_LED_DELAY2_TMIN, LM3533_LED_DELAY2_TMAX);
+               val = time_to_val(&t,   LM3533_LED_DELAY2_TMIN,
+                                       LM3533_LED_DELAY2_TSTEP,
+                                       LM3533_LED_DELAY2_VMIN,
+                                       LM3533_LED_DELAY2_VMAX);
+       } else {
+               t = clamp(t, LM3533_LED_DELAY1_TMIN, LM3533_LED_DELAY1_TMAX);
+               val = time_to_val(&t,   LM3533_LED_DELAY1_TMIN,
+                                       LM3533_LED_DELAY1_TSTEP,
+                                       LM3533_LED_DELAY1_VMIN,
+                                       LM3533_LED_DELAY1_VMAX);
+       }
+
+       *delay = (t + 500) / 1000;
+
+       return val;
+}
+
+/*
+ * Set delay register base to *delay (in ms) and update *delay to reflect
+ * actual hardware delay used.
+ */
+static u8 lm3533_led_delay_set(struct lm3533_led *led, u8 base,
+                                                       unsigned long *delay)
+{
+       unsigned t;
+       u8 val;
+       u8 reg;
+       int ret;
+
+       t = (unsigned)*delay;
+
+       /* Delay group 3 is only available for low time (delay off). */
+       if (base != LM3533_REG_PATTERN_LOW_TIME_BASE)
+               t = min(t, LM3533_LED_DELAY2_TMAX / 1000);
+
+       val = lm3533_led_get_hw_delay(&t);
+
+       dev_dbg(led->cdev.dev, "%s - %lu: %u (0x%02x)\n", __func__,
+                                                       *delay, t, val);
+       reg = lm3533_led_get_pattern_reg(led, base);
+       ret = lm3533_write(led->lm3533, reg, val);
+       if (ret)
+               dev_err(led->cdev.dev, "failed to set delay (%02x)\n", reg);
+
+       *delay = t;
+
+       return ret;
+}
+
+static int lm3533_led_delay_on_set(struct lm3533_led *led, unsigned long *t)
+{
+       return lm3533_led_delay_set(led, LM3533_REG_PATTERN_HIGH_TIME_BASE, t);
+}
+
+static int lm3533_led_delay_off_set(struct lm3533_led *led, unsigned long *t)
+{
+       return lm3533_led_delay_set(led, LM3533_REG_PATTERN_LOW_TIME_BASE, t);
+}
+
+static int lm3533_led_blink_set(struct led_classdev *cdev,
+                               unsigned long *delay_on,
+                               unsigned long *delay_off)
+{
+       struct lm3533_led *led = to_lm3533_led(cdev);
+       int ret;
+
+       dev_dbg(led->cdev.dev, "%s - on = %lu, off = %lu\n", __func__,
+                                                       *delay_on, *delay_off);
+
+       if (*delay_on > LM3533_LED_DELAY_ON_MAX ||
+                                       *delay_off > LM3533_LED_DELAY_OFF_MAX)
+               return -EINVAL;
+
+       if (*delay_on == 0 && *delay_off == 0) {
+               *delay_on = 500;
+               *delay_off = 500;
+       }
+
+       ret = lm3533_led_delay_on_set(led, delay_on);
+       if (ret)
+               return ret;
+
+       ret = lm3533_led_delay_off_set(led, delay_off);
+       if (ret)
+               return ret;
+
+       return lm3533_led_pattern_enable(led, 1);
+}
+
+static ssize_t show_id(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct lm3533_led *led = to_lm3533_led(led_cdev);
+
+       return scnprintf(buf, PAGE_SIZE, "%d\n", led->id);
+}
+
+/*
+ * Pattern generator rise/fall times:
+ *
+ *   0 - 2048 us (default)
+ *   1 - 262 ms
+ *   2 - 524 ms
+ *   3 - 1.049 s
+ *   4 - 2.097 s
+ *   5 - 4.194 s
+ *   6 - 8.389 s
+ *   7 - 16.78 s
+ */
+static ssize_t show_risefalltime(struct device *dev,
+                                       struct device_attribute *attr,
+                                       char *buf, u8 base)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct lm3533_led *led = to_lm3533_led(led_cdev);
+       ssize_t ret;
+       u8 reg;
+       u8 val;
+
+       reg = lm3533_led_get_pattern_reg(led, base);
+       ret = lm3533_read(led->lm3533, reg, &val);
+       if (ret)
+               return ret;
+
+       return scnprintf(buf, PAGE_SIZE, "%x\n", val);
+}
+
+static ssize_t show_risetime(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       return show_risefalltime(dev, attr, buf,
+                                       LM3533_REG_PATTERN_RISETIME_BASE);
+}
+
+static ssize_t show_falltime(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       return show_risefalltime(dev, attr, buf,
+                                       LM3533_REG_PATTERN_FALLTIME_BASE);
+}
+
+static ssize_t store_risefalltime(struct device *dev,
+                                       struct device_attribute *attr,
+                                       const char *buf, size_t len, u8 base)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct lm3533_led *led = to_lm3533_led(led_cdev);
+       u8 val;
+       u8 reg;
+       int ret;
+
+       if (kstrtou8(buf, 0, &val) || val > LM3533_RISEFALLTIME_MAX)
+               return -EINVAL;
+
+       reg = lm3533_led_get_pattern_reg(led, base);
+       ret = lm3533_write(led->lm3533, reg, val);
+       if (ret)
+               return ret;
+
+       return len;
+}
+
+static ssize_t store_risetime(struct device *dev,
+                                       struct device_attribute *attr,
+                                       const char *buf, size_t len)
+{
+       return store_risefalltime(dev, attr, buf, len,
+                                       LM3533_REG_PATTERN_RISETIME_BASE);
+}
+
+static ssize_t store_falltime(struct device *dev,
+                                       struct device_attribute *attr,
+                                       const char *buf, size_t len)
+{
+       return store_risefalltime(dev, attr, buf, len,
+                                       LM3533_REG_PATTERN_FALLTIME_BASE);
+}
+
+static ssize_t show_als_channel(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct lm3533_led *led = to_lm3533_led(led_cdev);
+       unsigned channel;
+       u8 reg;
+       u8 val;
+       int ret;
+
+       reg = lm3533_led_get_lv_reg(led, LM3533_REG_CTRLBANK_BCONF_BASE);
+       ret = lm3533_read(led->lm3533, reg, &val);
+       if (ret)
+               return ret;
+
+       channel = (val & LM3533_REG_CTRLBANK_BCONF_ALS_CHANNEL_MASK) + 1;
+
+       return scnprintf(buf, PAGE_SIZE, "%u\n", channel);
+}
+
+static ssize_t store_als_channel(struct device *dev,
+                                       struct device_attribute *attr,
+                                       const char *buf, size_t len)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct lm3533_led *led = to_lm3533_led(led_cdev);
+       unsigned channel;
+       u8 reg;
+       u8 val;
+       u8 mask;
+       int ret;
+
+       if (kstrtouint(buf, 0, &channel))
+               return -EINVAL;
+
+       if (channel < LM3533_ALS_CHANNEL_LV_MIN ||
+                                       channel > LM3533_ALS_CHANNEL_LV_MAX)
+               return -EINVAL;
+
+       reg = lm3533_led_get_lv_reg(led, LM3533_REG_CTRLBANK_BCONF_BASE);
+       mask = LM3533_REG_CTRLBANK_BCONF_ALS_CHANNEL_MASK;
+       val = channel - 1;
+
+       ret = lm3533_update(led->lm3533, reg, val, mask);
+       if (ret)
+               return ret;
+
+       return len;
+}
+
+static ssize_t show_als_en(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct lm3533_led *led = to_lm3533_led(led_cdev);
+       bool enable;
+       u8 reg;
+       u8 val;
+       int ret;
+
+       reg = lm3533_led_get_lv_reg(led, LM3533_REG_CTRLBANK_BCONF_BASE);
+       ret = lm3533_read(led->lm3533, reg, &val);
+       if (ret)
+               return ret;
+
+       enable = val & LM3533_REG_CTRLBANK_BCONF_ALS_EN_MASK;
+
+       return scnprintf(buf, PAGE_SIZE, "%d\n", enable);
+}
+
+static ssize_t store_als_en(struct device *dev,
+                                       struct device_attribute *attr,
+                                       const char *buf, size_t len)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct lm3533_led *led = to_lm3533_led(led_cdev);
+       unsigned enable;
+       u8 reg;
+       u8 mask;
+       u8 val;
+       int ret;
+
+       if (kstrtouint(buf, 0, &enable))
+               return -EINVAL;
+
+       reg = lm3533_led_get_lv_reg(led, LM3533_REG_CTRLBANK_BCONF_BASE);
+       mask = LM3533_REG_CTRLBANK_BCONF_ALS_EN_MASK;
+
+       if (enable)
+               val = mask;
+       else
+               val = 0;
+
+       ret = lm3533_update(led->lm3533, reg, val, mask);
+       if (ret)
+               return ret;
+
+       return len;
+}
+
+static ssize_t show_linear(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct lm3533_led *led = to_lm3533_led(led_cdev);
+       u8 reg;
+       u8 val;
+       int linear;
+       int ret;
+
+       reg = lm3533_led_get_lv_reg(led, LM3533_REG_CTRLBANK_BCONF_BASE);
+       ret = lm3533_read(led->lm3533, reg, &val);
+       if (ret)
+               return ret;
+
+       if (val & LM3533_REG_CTRLBANK_BCONF_MAPPING_MASK)
+               linear = 1;
+       else
+               linear = 0;
+
+       return scnprintf(buf, PAGE_SIZE, "%x\n", linear);
+}
+
+static ssize_t store_linear(struct device *dev,
+                                       struct device_attribute *attr,
+                                       const char *buf, size_t len)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct lm3533_led *led = to_lm3533_led(led_cdev);
+       unsigned long linear;
+       u8 reg;
+       u8 mask;
+       u8 val;
+       int ret;
+
+       if (kstrtoul(buf, 0, &linear))
+               return -EINVAL;
+
+       reg = lm3533_led_get_lv_reg(led, LM3533_REG_CTRLBANK_BCONF_BASE);
+       mask = LM3533_REG_CTRLBANK_BCONF_MAPPING_MASK;
+
+       if (linear)
+               val = mask;
+       else
+               val = 0;
+
+       ret = lm3533_update(led->lm3533, reg, val, mask);
+       if (ret)
+               return ret;
+
+       return len;
+}
+
+static ssize_t show_pwm(struct device *dev,
+                                       struct device_attribute *attr,
+                                       char *buf)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct lm3533_led *led = to_lm3533_led(led_cdev);
+       u8 val;
+       int ret;
+
+       ret = lm3533_ctrlbank_get_pwm(&led->cb, &val);
+       if (ret)
+               return ret;
+
+       return scnprintf(buf, PAGE_SIZE, "%u\n", val);
+}
+
+static ssize_t store_pwm(struct device *dev,
+                                       struct device_attribute *attr,
+                                       const char *buf, size_t len)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct lm3533_led *led = to_lm3533_led(led_cdev);
+       u8 val;
+       int ret;
+
+       if (kstrtou8(buf, 0, &val))
+               return -EINVAL;
+
+       ret = lm3533_ctrlbank_set_pwm(&led->cb, val);
+       if (ret)
+               return ret;
+
+       return len;
+}
+
+static LM3533_ATTR_RW(als_channel);
+static LM3533_ATTR_RW(als_en);
+static LM3533_ATTR_RW(falltime);
+static LM3533_ATTR_RO(id);
+static LM3533_ATTR_RW(linear);
+static LM3533_ATTR_RW(pwm);
+static LM3533_ATTR_RW(risetime);
+
+static struct attribute *lm3533_led_attributes[] = {
+       &dev_attr_als_channel.attr,
+       &dev_attr_als_en.attr,
+       &dev_attr_falltime.attr,
+       &dev_attr_id.attr,
+       &dev_attr_linear.attr,
+       &dev_attr_pwm.attr,
+       &dev_attr_risetime.attr,
+       NULL,
+};
+
+static umode_t lm3533_led_attr_is_visible(struct kobject *kobj,
+                                            struct attribute *attr, int n)
+{
+       struct device *dev = container_of(kobj, struct device, kobj);
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct lm3533_led *led = to_lm3533_led(led_cdev);
+       umode_t mode = attr->mode;
+
+       if (attr == &dev_attr_als_channel.attr ||
+                                       attr == &dev_attr_als_en.attr) {
+               if (!led->lm3533->have_als)
+                       mode = 0;
+       }
+
+       return mode;
+};
+
+static struct attribute_group lm3533_led_attribute_group = {
+       .is_visible     = lm3533_led_attr_is_visible,
+       .attrs          = lm3533_led_attributes
+};
+
+static int __devinit lm3533_led_setup(struct lm3533_led *led,
+                                       struct lm3533_led_platform_data *pdata)
+{
+       int ret;
+
+       ret = lm3533_ctrlbank_set_max_current(&led->cb, pdata->max_current);
+       if (ret)
+               return ret;
+
+       return lm3533_ctrlbank_set_pwm(&led->cb, pdata->pwm);
+}
+
+static int __devinit lm3533_led_probe(struct platform_device *pdev)
+{
+       struct lm3533 *lm3533;
+       struct lm3533_led_platform_data *pdata;
+       struct lm3533_led *led;
+       int ret;
+
+       dev_dbg(&pdev->dev, "%s\n", __func__);
+
+       lm3533 = dev_get_drvdata(pdev->dev.parent);
+       if (!lm3533)
+               return -EINVAL;
+
+       pdata = pdev->dev.platform_data;
+       if (!pdata) {
+               dev_err(&pdev->dev, "no platform data\n");
+               return -EINVAL;
+       }
+
+       if (pdev->id < 0 || pdev->id >= LM3533_LVCTRLBANK_COUNT) {
+               dev_err(&pdev->dev, "illegal LED id %d\n", pdev->id);
+               return -EINVAL;
+       }
+
+       led = devm_kzalloc(&pdev->dev, sizeof(*led), GFP_KERNEL);
+       if (!led)
+               return -ENOMEM;
+
+       led->lm3533 = lm3533;
+       led->cdev.name = pdata->name;
+       led->cdev.default_trigger = pdata->default_trigger;
+       led->cdev.brightness_set = lm3533_led_set;
+       led->cdev.brightness_get = lm3533_led_get;
+       led->cdev.blink_set = lm3533_led_blink_set;
+       led->cdev.brightness = LED_OFF;
+       led->id = pdev->id;
+
+       mutex_init(&led->mutex);
+       INIT_WORK(&led->work, lm3533_led_work);
+
+       /* The class framework makes a callback to get brightness during
+        * registration so use parent device (for error reporting) until
+        * registered.
+        */
+       led->cb.lm3533 = lm3533;
+       led->cb.id = lm3533_led_get_ctrlbank_id(led);
+       led->cb.dev = lm3533->dev;
+
+       platform_set_drvdata(pdev, led);
+
+       ret = led_classdev_register(pdev->dev.parent, &led->cdev);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to register LED %d\n", pdev->id);
+               return ret;
+       }
+
+       led->cb.dev = led->cdev.dev;
+
+       ret = sysfs_create_group(&led->cdev.dev->kobj,
+                                               &lm3533_led_attribute_group);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "failed to create sysfs attributes\n");
+               goto err_unregister;
+       }
+
+       ret = lm3533_led_setup(led, pdata);
+       if (ret)
+               goto err_sysfs_remove;
+
+       ret = lm3533_ctrlbank_enable(&led->cb);
+       if (ret)
+               goto err_sysfs_remove;
+
+       return 0;
+
+err_sysfs_remove:
+       sysfs_remove_group(&led->cdev.dev->kobj, &lm3533_led_attribute_group);
+err_unregister:
+       led_classdev_unregister(&led->cdev);
+       flush_work_sync(&led->work);
+
+       return ret;
+}
+
+static int __devexit lm3533_led_remove(struct platform_device *pdev)
+{
+       struct lm3533_led *led = platform_get_drvdata(pdev);
+
+       dev_dbg(&pdev->dev, "%s\n", __func__);
+
+       lm3533_ctrlbank_disable(&led->cb);
+       sysfs_remove_group(&led->cdev.dev->kobj, &lm3533_led_attribute_group);
+       led_classdev_unregister(&led->cdev);
+       flush_work_sync(&led->work);
+
+       return 0;
+}
+
+static void lm3533_led_shutdown(struct platform_device *pdev)
+{
+
+       struct lm3533_led *led = platform_get_drvdata(pdev);
+
+       dev_dbg(&pdev->dev, "%s\n", __func__);
+
+       lm3533_ctrlbank_disable(&led->cb);
+       lm3533_led_set(&led->cdev, LED_OFF);            /* disable blink */
+       flush_work_sync(&led->work);
+}
+
+static struct platform_driver lm3533_led_driver = {
+       .driver = {
+               .name = "lm3533-leds",
+               .owner = THIS_MODULE,
+       },
+       .probe          = lm3533_led_probe,
+       .remove         = __devexit_p(lm3533_led_remove),
+       .shutdown       = lm3533_led_shutdown,
+};
+module_platform_driver(lm3533_led_driver);
+
+MODULE_AUTHOR("Johan Hovold <jhovold@gmail.com>");
+MODULE_DESCRIPTION("LM3533 LED driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:lm3533-leds");
index 410a723b86910ac8876e696bc4d6b0b85237e998..23815624f35ef59ea11d6cfe167e173257c548c2 100644 (file)
@@ -193,9 +193,14 @@ static int lp5521_load_program(struct lp5521_engine *eng, const u8 *pattern)
 
        /* move current engine to direct mode and remember the state */
        ret = lp5521_set_engine_mode(eng, LP5521_CMD_DIRECT);
+       if (ret)
+               return ret;
+
        /* Mode change requires min 500 us delay. 1 - 2 ms  with margin */
        usleep_range(1000, 2000);
-       ret |= lp5521_read(client, LP5521_REG_OP_MODE, &mode);
+       ret = lp5521_read(client, LP5521_REG_OP_MODE, &mode);
+       if (ret)
+               return ret;
 
        /* For loading, all the engines to load mode */
        lp5521_write(client, LP5521_REG_OP_MODE, LP5521_CMD_DIRECT);
@@ -211,8 +216,7 @@ static int lp5521_load_program(struct lp5521_engine *eng, const u8 *pattern)
                                LP5521_PROG_MEM_SIZE,
                                pattern);
 
-       ret |= lp5521_write(client, LP5521_REG_OP_MODE, mode);
-       return ret;
+       return lp5521_write(client, LP5521_REG_OP_MODE, mode);
 }
 
 static int lp5521_set_led_current(struct lp5521_chip *chip, int led, u8 curr)
@@ -785,7 +789,7 @@ static int __devinit lp5521_probe(struct i2c_client *client,
         * LP5521_REG_ENABLE register will not have any effect - strange!
         */
        ret = lp5521_read(client, LP5521_REG_R_CURRENT, &buf);
-       if (buf != LP5521_REG_R_CURR_DEFAULT) {
+       if (ret || buf != LP5521_REG_R_CURR_DEFAULT) {
                dev_err(&client->dev, "error in resetting chip\n");
                goto fail2;
        }
index 8bc4915415509d6e2456a0d4ea46446bf9188eca..4cc6a2e3df3487e1ce971b407bd56095d84b9b54 100644 (file)
@@ -280,7 +280,7 @@ static int __devinit mc13783_led_probe(struct platform_device *pdev)
                return -EINVAL;
        }
 
-       led = kzalloc(sizeof(*led) * pdata->num_leds, GFP_KERNEL);
+       led = kcalloc(pdata->num_leds, sizeof(*led), GFP_KERNEL);
        if (led == NULL) {
                dev_err(&pdev->dev, "failed to alloc memory\n");
                return -ENOMEM;
index dcc3bc3d38db8cac7384c65009965967bc026f24..5f462dbf0dbbf4920bfc281c6ad69cfc8486ed5d 100644 (file)
@@ -101,11 +101,16 @@ static const struct i2c_device_id pca955x_id[] = {
 };
 MODULE_DEVICE_TABLE(i2c, pca955x_id);
 
-struct pca955x_led {
+struct pca955x {
+       struct mutex lock;
+       struct pca955x_led *leds;
        struct pca955x_chipdef  *chipdef;
        struct i2c_client       *client;
+};
+
+struct pca955x_led {
+       struct pca955x  *pca955x;
        struct work_struct      work;
-       spinlock_t              lock;
        enum led_brightness     brightness;
        struct led_classdev     led_cdev;
        int                     led_num;        /* 0 .. 15 potentially */
@@ -140,7 +145,7 @@ static inline u8 pca955x_ledsel(u8 oldval, int led_num, int state)
  */
 static void pca955x_write_psc(struct i2c_client *client, int n, u8 val)
 {
-       struct pca955x_led *pca955x = i2c_get_clientdata(client);
+       struct pca955x *pca955x = i2c_get_clientdata(client);
 
        i2c_smbus_write_byte_data(client,
                pca95xx_num_input_regs(pca955x->chipdef->bits) + 2*n,
@@ -156,7 +161,7 @@ static void pca955x_write_psc(struct i2c_client *client, int n, u8 val)
  */
 static void pca955x_write_pwm(struct i2c_client *client, int n, u8 val)
 {
-       struct pca955x_led *pca955x = i2c_get_clientdata(client);
+       struct pca955x *pca955x = i2c_get_clientdata(client);
 
        i2c_smbus_write_byte_data(client,
                pca95xx_num_input_regs(pca955x->chipdef->bits) + 1 + 2*n,
@@ -169,7 +174,7 @@ static void pca955x_write_pwm(struct i2c_client *client, int n, u8 val)
  */
 static void pca955x_write_ls(struct i2c_client *client, int n, u8 val)
 {
-       struct pca955x_led *pca955x = i2c_get_clientdata(client);
+       struct pca955x *pca955x = i2c_get_clientdata(client);
 
        i2c_smbus_write_byte_data(client,
                pca95xx_num_input_regs(pca955x->chipdef->bits) + 4 + n,
@@ -182,7 +187,7 @@ static void pca955x_write_ls(struct i2c_client *client, int n, u8 val)
  */
 static u8 pca955x_read_ls(struct i2c_client *client, int n)
 {
-       struct pca955x_led *pca955x = i2c_get_clientdata(client);
+       struct pca955x *pca955x = i2c_get_clientdata(client);
 
        return (u8) i2c_smbus_read_byte_data(client,
                pca95xx_num_input_regs(pca955x->chipdef->bits) + 4 + n);
@@ -190,18 +195,23 @@ static u8 pca955x_read_ls(struct i2c_client *client, int n)
 
 static void pca955x_led_work(struct work_struct *work)
 {
-       struct pca955x_led *pca955x;
+       struct pca955x_led *pca955x_led;
+       struct pca955x *pca955x;
        u8 ls;
        int chip_ls;    /* which LSx to use (0-3 potentially) */
        int ls_led;     /* which set of bits within LSx to use (0-3) */
 
-       pca955x = container_of(work, struct pca955x_led, work);
-       chip_ls = pca955x->led_num / 4;
-       ls_led = pca955x->led_num % 4;
+       pca955x_led = container_of(work, struct pca955x_led, work);
+       pca955x = pca955x_led->pca955x;
+
+       chip_ls = pca955x_led->led_num / 4;
+       ls_led = pca955x_led->led_num % 4;
+
+       mutex_lock(&pca955x->lock);
 
        ls = pca955x_read_ls(pca955x->client, chip_ls);
 
-       switch (pca955x->brightness) {
+       switch (pca955x_led->brightness) {
        case LED_FULL:
                ls = pca955x_ledsel(ls, ls_led, PCA955X_LS_LED_ON);
                break;
@@ -219,12 +229,15 @@ static void pca955x_led_work(struct work_struct *work)
                 * OFF, HALF, or FULL.  But, this is probably better than
                 * just turning off for all other values.
                 */
-               pca955x_write_pwm(pca955x->client, 1, 255-pca955x->brightness);
+               pca955x_write_pwm(pca955x->client, 1,
+                               255 - pca955x_led->brightness);
                ls = pca955x_ledsel(ls, ls_led, PCA955X_LS_BLINK1);
                break;
        }
 
        pca955x_write_ls(pca955x->client, chip_ls, ls);
+
+       mutex_unlock(&pca955x->lock);
 }
 
 static void pca955x_led_set(struct led_classdev *led_cdev, enum led_brightness value)
@@ -233,7 +246,6 @@ static void pca955x_led_set(struct led_classdev *led_cdev, enum led_brightness v
 
        pca955x = container_of(led_cdev, struct pca955x_led, led_cdev);
 
-       spin_lock(&pca955x->lock);
        pca955x->brightness = value;
 
        /*
@@ -241,14 +253,13 @@ static void pca955x_led_set(struct led_classdev *led_cdev, enum led_brightness v
         * can sleep.
         */
        schedule_work(&pca955x->work);
-
-       spin_unlock(&pca955x->lock);
 }
 
 static int __devinit pca955x_probe(struct i2c_client *client,
                                        const struct i2c_device_id *id)
 {
-       struct pca955x_led *pca955x;
+       struct pca955x *pca955x;
+       struct pca955x_led *pca955x_led;
        struct pca955x_chipdef *chip;
        struct i2c_adapter *adapter;
        struct led_platform_data *pdata;
@@ -282,39 +293,48 @@ static int __devinit pca955x_probe(struct i2c_client *client,
                }
        }
 
-       pca955x = kzalloc(sizeof(*pca955x) * chip->bits, GFP_KERNEL);
+       pca955x = kzalloc(sizeof(*pca955x), GFP_KERNEL);
        if (!pca955x)
                return -ENOMEM;
 
+       pca955x->leds = kzalloc(sizeof(*pca955x_led) * chip->bits, GFP_KERNEL);
+       if (!pca955x->leds) {
+               err = -ENOMEM;
+               goto exit_nomem;
+       }
+
        i2c_set_clientdata(client, pca955x);
 
+       mutex_init(&pca955x->lock);
+       pca955x->client = client;
+       pca955x->chipdef = chip;
+
        for (i = 0; i < chip->bits; i++) {
-               pca955x[i].chipdef = chip;
-               pca955x[i].client = client;
-               pca955x[i].led_num = i;
+               pca955x_led = &pca955x->leds[i];
+               pca955x_led->led_num = i;
+               pca955x_led->pca955x = pca955x;
 
                /* Platform data can specify LED names and default triggers */
                if (pdata) {
                        if (pdata->leds[i].name)
-                               snprintf(pca955x[i].name,
-                                        sizeof(pca955x[i].name), "pca955x:%s",
-                                        pdata->leds[i].name);
+                               snprintf(pca955x_led->name,
+                                       sizeof(pca955x_led->name), "pca955x:%s",
+                                       pdata->leds[i].name);
                        if (pdata->leds[i].default_trigger)
-                               pca955x[i].led_cdev.default_trigger =
+                               pca955x_led->led_cdev.default_trigger =
                                        pdata->leds[i].default_trigger;
                } else {
-                       snprintf(pca955x[i].name, sizeof(pca955x[i].name),
+                       snprintf(pca955x_led->name, sizeof(pca955x_led->name),
                                 "pca955x:%d", i);
                }
 
-               spin_lock_init(&pca955x[i].lock);
-
-               pca955x[i].led_cdev.name = pca955x[i].name;
-               pca955x[i].led_cdev.brightness_set = pca955x_led_set;
+               pca955x_led->led_cdev.name = pca955x_led->name;
+               pca955x_led->led_cdev.brightness_set = pca955x_led_set;
 
-               INIT_WORK(&pca955x[i].work, pca955x_led_work);
+               INIT_WORK(&pca955x_led->work, pca955x_led_work);
 
-               err = led_classdev_register(&client->dev, &pca955x[i].led_cdev);
+               err = led_classdev_register(&client->dev,
+                                       &pca955x_led->led_cdev);
                if (err < 0)
                        goto exit;
        }
@@ -337,10 +357,12 @@ static int __devinit pca955x_probe(struct i2c_client *client,
 
 exit:
        while (i--) {
-               led_classdev_unregister(&pca955x[i].led_cdev);
-               cancel_work_sync(&pca955x[i].work);
+               led_classdev_unregister(&pca955x->leds[i].led_cdev);
+               cancel_work_sync(&pca955x->leds[i].work);
        }
 
+       kfree(pca955x->leds);
+exit_nomem:
        kfree(pca955x);
 
        return err;
@@ -348,14 +370,15 @@ exit:
 
 static int __devexit pca955x_remove(struct i2c_client *client)
 {
-       struct pca955x_led *pca955x = i2c_get_clientdata(client);
+       struct pca955x *pca955x = i2c_get_clientdata(client);
        int i;
 
        for (i = 0; i < pca955x->chipdef->bits; i++) {
-               led_classdev_unregister(&pca955x[i].led_cdev);
-               cancel_work_sync(&pca955x[i].work);
+               led_classdev_unregister(&pca955x->leds[i].led_cdev);
+               cancel_work_sync(&pca955x->leds[i].work);
        }
 
+       kfree(pca955x->leds);
        kfree(pca955x);
 
        return 0;
index 2b513a2ad7dec3b7d81b934e8d852903f73548c9..e2726867c5d42c7a5f2cc6c37b786d922eb0b883 100644 (file)
@@ -120,6 +120,7 @@ static void bl_trig_activate(struct led_classdev *led)
        ret = fb_register_client(&n->notifier);
        if (ret)
                dev_err(led->dev, "unable to register backlight trigger\n");
+       led->activated = true;
 
        return;
 
@@ -133,10 +134,11 @@ static void bl_trig_deactivate(struct led_classdev *led)
        struct bl_trig_notifier *n =
                (struct bl_trig_notifier *) led->trigger_data;
 
-       if (n) {
+       if (led->activated) {
                device_remove_file(led->dev, &dev_attr_inverted);
                fb_unregister_client(&n->notifier);
                kfree(n);
+               led->activated = false;
        }
 }
 
index ecc4bf3f37a937d74ab71f6c6cf91c9fc3763eb9..f057c101b896e56561a41f54809e1a10e52d0aaa 100644 (file)
@@ -200,6 +200,7 @@ static void gpio_trig_activate(struct led_classdev *led)
        gpio_data->led = led;
        led->trigger_data = gpio_data;
        INIT_WORK(&gpio_data->work, gpio_trig_work);
+       led->activated = true;
 
        return;
 
@@ -217,7 +218,7 @@ static void gpio_trig_deactivate(struct led_classdev *led)
 {
        struct gpio_trig_data *gpio_data = led->trigger_data;
 
-       if (gpio_data) {
+       if (led->activated) {
                device_remove_file(led->dev, &dev_attr_gpio);
                device_remove_file(led->dev, &dev_attr_inverted);
                device_remove_file(led->dev, &dev_attr_desired_brightness);
@@ -225,6 +226,7 @@ static void gpio_trig_deactivate(struct led_classdev *led)
                if (gpio_data->gpio != 0)
                        free_irq(gpio_to_irq(gpio_data->gpio), led);
                kfree(gpio_data);
+               led->activated = false;
        }
 }
 
index 759c0bba4a8fa54d95b12d5434ad72a643a25f88..41dc76db43118a347e4a8a06923b0fd076a12dc3 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/timer.h>
 #include <linux/sched.h>
 #include <linux/leds.h>
+#include <linux/reboot.h>
 #include "leds.h"
 
 struct heartbeat_trig_data {
@@ -83,15 +84,17 @@ static void heartbeat_trig_activate(struct led_classdev *led_cdev)
                    led_heartbeat_function, (unsigned long) led_cdev);
        heartbeat_data->phase = 0;
        led_heartbeat_function(heartbeat_data->timer.data);
+       led_cdev->activated = true;
 }
 
 static void heartbeat_trig_deactivate(struct led_classdev *led_cdev)
 {
        struct heartbeat_trig_data *heartbeat_data = led_cdev->trigger_data;
 
-       if (heartbeat_data) {
+       if (led_cdev->activated) {
                del_timer_sync(&heartbeat_data->timer);
                kfree(heartbeat_data);
+               led_cdev->activated = false;
        }
 }
 
@@ -101,13 +104,38 @@ static struct led_trigger heartbeat_led_trigger = {
        .deactivate = heartbeat_trig_deactivate,
 };
 
+static int heartbeat_reboot_notifier(struct notifier_block *nb,
+                                    unsigned long code, void *unused)
+{
+       led_trigger_unregister(&heartbeat_led_trigger);
+       return NOTIFY_DONE;
+}
+
+static struct notifier_block heartbeat_reboot_nb = {
+       .notifier_call = heartbeat_reboot_notifier,
+};
+
+static struct notifier_block heartbeat_panic_nb = {
+       .notifier_call = heartbeat_reboot_notifier,
+};
+
 static int __init heartbeat_trig_init(void)
 {
-       return led_trigger_register(&heartbeat_led_trigger);
+       int rc = led_trigger_register(&heartbeat_led_trigger);
+
+       if (!rc) {
+               atomic_notifier_chain_register(&panic_notifier_list,
+                                              &heartbeat_panic_nb);
+               register_reboot_notifier(&heartbeat_reboot_nb);
+       }
+       return rc;
 }
 
 static void __exit heartbeat_trig_exit(void)
 {
+       unregister_reboot_notifier(&heartbeat_reboot_nb);
+       atomic_notifier_chain_unregister(&panic_notifier_list,
+                                        &heartbeat_panic_nb);
        led_trigger_unregister(&heartbeat_led_trigger);
 }
 
index 328c64c0841cdda76f0d4ac7ed2b13cf801bec7b..9010f7abaf2cac05b5d65c118af6fcbb70a8f34e 100644 (file)
@@ -31,21 +31,17 @@ static ssize_t led_delay_on_store(struct device *dev,
                struct device_attribute *attr, const char *buf, size_t size)
 {
        struct led_classdev *led_cdev = dev_get_drvdata(dev);
-       int ret = -EINVAL;
-       char *after;
-       unsigned long state = simple_strtoul(buf, &after, 10);
-       size_t count = after - buf;
-
-       if (isspace(*after))
-               count++;
-
-       if (count == size) {
-               led_blink_set(led_cdev, &state, &led_cdev->blink_delay_off);
-               led_cdev->blink_delay_on = state;
-               ret = count;
-       }
+       unsigned long state;
+       ssize_t ret = -EINVAL;
+
+       ret = kstrtoul(buf, 10, &state);
+       if (ret)
+               return ret;
 
-       return ret;
+       led_blink_set(led_cdev, &state, &led_cdev->blink_delay_off);
+       led_cdev->blink_delay_on = state;
+
+       return size;
 }
 
 static ssize_t led_delay_off_show(struct device *dev,
@@ -60,21 +56,17 @@ static ssize_t led_delay_off_store(struct device *dev,
                struct device_attribute *attr, const char *buf, size_t size)
 {
        struct led_classdev *led_cdev = dev_get_drvdata(dev);
-       int ret = -EINVAL;
-       char *after;
-       unsigned long state = simple_strtoul(buf, &after, 10);
-       size_t count = after - buf;
-
-       if (isspace(*after))
-               count++;
-
-       if (count == size) {
-               led_blink_set(led_cdev, &led_cdev->blink_delay_on, &state);
-               led_cdev->blink_delay_off = state;
-               ret = count;
-       }
+       unsigned long state;
+       ssize_t ret = -EINVAL;
 
-       return ret;
+       ret = kstrtoul(buf, 10, &state);
+       if (ret)
+               return ret;
+
+       led_blink_set(led_cdev, &led_cdev->blink_delay_on, &state);
+       led_cdev->blink_delay_off = state;
+
+       return size;
 }
 
 static DEVICE_ATTR(delay_on, 0644, led_delay_on_show, led_delay_on_store);
@@ -95,8 +87,7 @@ static void timer_trig_activate(struct led_classdev *led_cdev)
 
        led_blink_set(led_cdev, &led_cdev->blink_delay_on,
                      &led_cdev->blink_delay_off);
-
-       led_cdev->trigger_data = (void *)1;
+       led_cdev->activated = true;
 
        return;
 
@@ -106,9 +97,10 @@ err_out_delayon:
 
 static void timer_trig_deactivate(struct led_classdev *led_cdev)
 {
-       if (led_cdev->trigger_data) {
+       if (led_cdev->activated) {
                device_remove_file(led_cdev->dev, &dev_attr_delay_on);
                device_remove_file(led_cdev->dev, &dev_attr_delay_off);
+               led_cdev->activated = false;
        }
 
        /* Stop blinking */
diff --git a/drivers/leds/ledtrig-transient.c b/drivers/leds/ledtrig-transient.c
new file mode 100644 (file)
index 0000000..83179f4
--- /dev/null
@@ -0,0 +1,237 @@
+/*
+ * LED Kernel Transient Trigger
+ *
+ * Copyright (C) 2012 Shuah Khan <shuahkhan@gmail.com>
+ *
+ * Based on Richard Purdie's ledtrig-timer.c and Atsushi Nemoto's
+ * ledtrig-heartbeat.c
+ * Design and use-case input from Jonas Bonn <jonas@southpole.se> and
+ * Neil Brown <neilb@suse.de>
+ *
+ * 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.
+ *
+ */
+/*
+ * Transient trigger allows one shot timer activation. Please refer to
+ * Documentation/leds/ledtrig-transient.txt for details
+*/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/timer.h>
+#include <linux/leds.h>
+#include "leds.h"
+
+struct transient_trig_data {
+       int activate;
+       int state;
+       int restore_state;
+       unsigned long duration;
+       struct timer_list timer;
+};
+
+static void transient_timer_function(unsigned long data)
+{
+       struct led_classdev *led_cdev = (struct led_classdev *) data;
+       struct transient_trig_data *transient_data = led_cdev->trigger_data;
+
+       transient_data->activate = 0;
+       led_set_brightness(led_cdev, transient_data->restore_state);
+}
+
+static ssize_t transient_activate_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct transient_trig_data *transient_data = led_cdev->trigger_data;
+
+       return sprintf(buf, "%d\n", transient_data->activate);
+}
+
+static ssize_t transient_activate_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t size)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct transient_trig_data *transient_data = led_cdev->trigger_data;
+       unsigned long state;
+       ssize_t ret;
+
+       ret = kstrtoul(buf, 10, &state);
+       if (ret)
+               return ret;
+
+       if (state != 1 && state != 0)
+               return -EINVAL;
+
+       /* cancel the running timer */
+       if (state == 0 && transient_data->activate == 1) {
+               del_timer(&transient_data->timer);
+               transient_data->activate = state;
+               led_set_brightness(led_cdev, transient_data->restore_state);
+               return size;
+       }
+
+       /* start timer if there is no active timer */
+       if (state == 1 && transient_data->activate == 0 &&
+           transient_data->duration != 0) {
+               transient_data->activate = state;
+               led_set_brightness(led_cdev, transient_data->state);
+               transient_data->restore_state =
+                   (transient_data->state == LED_FULL) ? LED_OFF : LED_FULL;
+               mod_timer(&transient_data->timer,
+                         jiffies + transient_data->duration);
+       }
+
+       /* state == 0 && transient_data->activate == 0
+               timer is not active - just return */
+       /* state == 1 && transient_data->activate == 1
+               timer is already active - just return */
+
+       return size;
+}
+
+static ssize_t transient_duration_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct transient_trig_data *transient_data = led_cdev->trigger_data;
+
+       return sprintf(buf, "%lu\n", transient_data->duration);
+}
+
+static ssize_t transient_duration_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t size)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct transient_trig_data *transient_data = led_cdev->trigger_data;
+       unsigned long state;
+       ssize_t ret;
+
+       ret = kstrtoul(buf, 10, &state);
+       if (ret)
+               return ret;
+
+       transient_data->duration = state;
+       return size;
+}
+
+static ssize_t transient_state_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct transient_trig_data *transient_data = led_cdev->trigger_data;
+       int state;
+
+       state = (transient_data->state == LED_FULL) ? 1 : 0;
+       return sprintf(buf, "%d\n", state);
+}
+
+static ssize_t transient_state_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t size)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct transient_trig_data *transient_data = led_cdev->trigger_data;
+       unsigned long state;
+       ssize_t ret;
+
+       ret = kstrtoul(buf, 10, &state);
+       if (ret)
+               return ret;
+
+       if (state != 1 && state != 0)
+               return -EINVAL;
+
+       transient_data->state = (state == 1) ? LED_FULL : LED_OFF;
+       return size;
+}
+
+static DEVICE_ATTR(activate, 0644, transient_activate_show,
+                  transient_activate_store);
+static DEVICE_ATTR(duration, 0644, transient_duration_show,
+                  transient_duration_store);
+static DEVICE_ATTR(state, 0644, transient_state_show, transient_state_store);
+
+static void transient_trig_activate(struct led_classdev *led_cdev)
+{
+       int rc;
+       struct transient_trig_data *tdata;
+
+       tdata = kzalloc(sizeof(struct transient_trig_data), GFP_KERNEL);
+       if (!tdata) {
+               dev_err(led_cdev->dev,
+                       "unable to allocate transient trigger\n");
+               return;
+       }
+       led_cdev->trigger_data = tdata;
+
+       rc = device_create_file(led_cdev->dev, &dev_attr_activate);
+       if (rc)
+               goto err_out;
+
+       rc = device_create_file(led_cdev->dev, &dev_attr_duration);
+       if (rc)
+               goto err_out_duration;
+
+       rc = device_create_file(led_cdev->dev, &dev_attr_state);
+       if (rc)
+               goto err_out_state;
+
+       setup_timer(&tdata->timer, transient_timer_function,
+                   (unsigned long) led_cdev);
+       led_cdev->activated = true;
+
+       return;
+
+err_out_state:
+       device_remove_file(led_cdev->dev, &dev_attr_duration);
+err_out_duration:
+       device_remove_file(led_cdev->dev, &dev_attr_activate);
+err_out:
+       dev_err(led_cdev->dev, "unable to register transient trigger\n");
+       led_cdev->trigger_data = NULL;
+       kfree(tdata);
+}
+
+static void transient_trig_deactivate(struct led_classdev *led_cdev)
+{
+       struct transient_trig_data *transient_data = led_cdev->trigger_data;
+
+       if (led_cdev->activated) {
+               del_timer_sync(&transient_data->timer);
+               led_set_brightness(led_cdev, transient_data->restore_state);
+               device_remove_file(led_cdev->dev, &dev_attr_activate);
+               device_remove_file(led_cdev->dev, &dev_attr_duration);
+               device_remove_file(led_cdev->dev, &dev_attr_state);
+               led_cdev->trigger_data = NULL;
+               led_cdev->activated = false;
+               kfree(transient_data);
+       }
+}
+
+static struct led_trigger transient_trigger = {
+       .name     = "transient",
+       .activate = transient_trig_activate,
+       .deactivate = transient_trig_deactivate,
+};
+
+static int __init transient_trig_init(void)
+{
+       return led_trigger_register(&transient_trigger);
+}
+
+static void __exit transient_trig_exit(void)
+{
+       led_trigger_unregister(&transient_trigger);
+}
+
+module_init(transient_trig_init);
+module_exit(transient_trig_exit);
+
+MODULE_AUTHOR("Shuah Khan <shuahkhan@gmail.com>");
+MODULE_DESCRIPTION("Transient LED trigger");
+MODULE_LICENSE("GPL");
index a5c591ffe395d01b6e7e09f927c03f3048688620..d99db5623acf45039f53dd9eb7e6b9620d88735d 100644 (file)
@@ -1653,7 +1653,6 @@ mpt_mapresources(MPT_ADAPTER *ioc)
        unsigned long    port;
        u32              msize;
        u32              psize;
-       u8               revision;
        int              r = -ENODEV;
        struct pci_dev *pdev;
 
@@ -1670,8 +1669,6 @@ mpt_mapresources(MPT_ADAPTER *ioc)
                return r;
        }
 
-       pci_read_config_byte(pdev, PCI_CLASS_REVISION, &revision);
-
        if (sizeof(dma_addr_t) > 4) {
                const uint64_t required_mask = dma_get_required_mask
                    (&pdev->dev);
@@ -1779,7 +1776,6 @@ mpt_attach(struct pci_dev *pdev, const struct pci_device_id *id)
        MPT_ADAPTER     *ioc;
        u8               cb_idx;
        int              r = -ENODEV;
-       u8               revision;
        u8               pcixcmd;
        static int       mpt_ids = 0;
 #ifdef CONFIG_PROC_FS
@@ -1887,8 +1883,8 @@ mpt_attach(struct pci_dev *pdev, const struct pci_device_id *id)
        dinitprintk(ioc, printk(MYIOC_s_INFO_FMT "facts @ %p, pfacts[0] @ %p\n",
            ioc->name, &ioc->facts, &ioc->pfacts[0]));
 
-       pci_read_config_byte(pdev, PCI_CLASS_REVISION, &revision);
-       mpt_get_product_name(pdev->vendor, pdev->device, revision, ioc->prod_name);
+       mpt_get_product_name(pdev->vendor, pdev->device, pdev->revision,
+                            ioc->prod_name);
 
        switch (pdev->device)
        {
@@ -1903,7 +1899,7 @@ mpt_attach(struct pci_dev *pdev, const struct pci_device_id *id)
                break;
 
        case MPI_MANUFACTPAGE_DEVICEID_FC929X:
-               if (revision < XL_929) {
+               if (pdev->revision < XL_929) {
                        /* 929X Chip Fix. Set Split transactions level
                        * for PCIX. Set MOST bits to zero.
                        */
@@ -1934,7 +1930,7 @@ mpt_attach(struct pci_dev *pdev, const struct pci_device_id *id)
                /* 1030 Chip Fix. Disable Split transactions
                 * for PCIX. Set MOST bits to zero if Rev < C0( = 8).
                 */
-               if (revision < C0_1030) {
+               if (pdev->revision < C0_1030) {
                        pci_read_config_byte(pdev, 0x6a, &pcixcmd);
                        pcixcmd &= 0x8F;
                        pci_write_config_byte(pdev, 0x6a, pcixcmd);
@@ -6483,6 +6479,7 @@ mpt_config(MPT_ADAPTER *ioc, CONFIGPARMS *pCfg)
                                printk(MYIOC_s_INFO_FMT "%s: host reset in"
                                        " progress mpt_config timed out.!!\n",
                                        __func__, ioc->name);
+                               mutex_unlock(&ioc->mptbase_cmds.mutex);
                                return -EFAULT;
                        }
                        spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags);
index 6e6e16aab9dae36e8a1d736ba436c3e0431ca749..b383b6961e59549c8075b197d89adb6417b252cf 100644 (file)
@@ -1250,7 +1250,6 @@ mptctl_getiocinfo (unsigned long arg, unsigned int data_size)
        int                     iocnum;
        unsigned int            port;
        int                     cim_rev;
-       u8                      revision;
        struct scsi_device      *sdev;
        VirtDevice              *vdevice;
 
@@ -1324,8 +1323,7 @@ mptctl_getiocinfo (unsigned long arg, unsigned int data_size)
        pdev = (struct pci_dev *) ioc->pcidev;
 
        karg->pciId = pdev->device;
-       pci_read_config_byte(pdev, PCI_CLASS_REVISION, &revision);
-       karg->hwRev = revision;
+       karg->hwRev = pdev->revision;
        karg->subSystemDevice = pdev->subsystem_device;
        karg->subSystemVendor = pdev->subsystem_vendor;
 
index f4b4dad77391590d02967faefc1476c5c65ac6b8..e129c820df7da7d6430e62891558f92cdbe235ab 100644 (file)
@@ -106,6 +106,19 @@ config UCB1400_CORE
          To compile this driver as a module, choose M here: the
          module will be called ucb1400_core.
 
+config MFD_LM3533
+       tristate "LM3533 Lighting Power chip"
+       depends on I2C
+       select MFD_CORE
+       select REGMAP_I2C
+       help
+         Say yes here to enable support for National Semiconductor / TI
+         LM3533 Lighting Power chips.
+
+         This driver provides common support for accessing the device;
+         additional drivers must be enabled in order to use the LED,
+         backlight or ambient-light-sensor functionality of the device.
+
 config TPS6105X
        tristate "TPS61050/61052 Boost Converters"
        depends on I2C
@@ -177,8 +190,8 @@ config MFD_TPS65910
        bool "TPS65910 Power Management chip"
        depends on I2C=y && GPIOLIB
        select MFD_CORE
-       select GPIO_TPS65910
        select REGMAP_I2C
+       select IRQ_DOMAIN
        help
          if you say yes here you get support for the TPS65910 series of
          Power Management chips.
@@ -409,6 +422,19 @@ config PMIC_ADP5520
          individual components like LCD backlight, LEDs, GPIOs and Kepad
          under the corresponding menus.
 
+config MFD_MAX77693
+       bool "Maxim Semiconductor MAX77693 PMIC Support"
+       depends on I2C=y && GENERIC_HARDIRQS
+       select MFD_CORE
+       select REGMAP_I2C
+       help
+         Say yes here to support for Maxim Semiconductor MAX77693.
+         This is a companion Power Management IC with Flash, Haptic, Charger,
+         and MUIC(Micro USB Interface Controller) controls on chip.
+         This driver provides common support for accessing the device;
+         additional drivers must be enabled in order to use the functionality
+         of the device.
+
 config MFD_MAX8925
        bool "Maxim Semiconductor MAX8925 PMIC Support"
        depends on I2C=y && GENERIC_HARDIRQS
@@ -454,9 +480,9 @@ config MFD_S5M_CORE
         of the device
 
 config MFD_WM8400
-       tristate "Support Wolfson Microelectronics WM8400"
+       bool "Support Wolfson Microelectronics WM8400"
        select MFD_CORE
-       depends on I2C
+       depends on I2C=y
        select REGMAP_I2C
        help
          Support for the Wolfson Microelecronics WM8400 PMIC and audio
@@ -473,6 +499,7 @@ config MFD_WM831X_I2C
        select MFD_CORE
        select MFD_WM831X
        select REGMAP_I2C
+       select IRQ_DOMAIN
        depends on I2C=y && GENERIC_HARDIRQS
        help
          Support for the Wolfson Microelecronics WM831x and WM832x PMICs
@@ -485,6 +512,7 @@ config MFD_WM831X_SPI
        select MFD_CORE
        select MFD_WM831X
        select REGMAP_SPI
+       select IRQ_DOMAIN
        depends on SPI_MASTER && GENERIC_HARDIRQS
        help
          Support for the Wolfson Microelecronics WM831x and WM832x PMICs
@@ -597,17 +625,32 @@ config MFD_MC13783
        tristate
 
 config MFD_MC13XXX
-       tristate "Support Freescale MC13783 and MC13892"
-       depends on SPI_MASTER
+       tristate
+       depends on SPI_MASTER || I2C
        select MFD_CORE
        select MFD_MC13783
        help
-         Support for the Freescale (Atlas) PMIC and audio CODECs
-         MC13783 and MC13892.
-         This driver provides common support for accessing  the device,
+         Enable support for the Freescale MC13783 and MC13892 PMICs.
+         This driver provides common support for accessing the device,
          additional drivers must be enabled in order to use the
          functionality of the device.
 
+config MFD_MC13XXX_SPI
+       tristate "Freescale MC13783 and MC13892 SPI interface"
+       depends on SPI_MASTER
+       select REGMAP_SPI
+       select MFD_MC13XXX
+       help
+         Select this if your MC13xxx is connected via an SPI bus.
+
+config MFD_MC13XXX_I2C
+       tristate "Freescale MC13892 I2C interface"
+       depends on I2C
+       select REGMAP_I2C
+       select MFD_MC13XXX
+       help
+         Select this if your MC13xxx is connected via an I2C bus.
+
 config ABX500_CORE
        bool "ST-Ericsson ABX500 Mixed Signal Circuit register functions"
        default y if ARCH_U300 || ARCH_U8500
@@ -651,7 +694,7 @@ config EZX_PCAP
 
 config AB8500_CORE
        bool "ST-Ericsson AB8500 Mixed Signal Power Management chip"
-       depends on GENERIC_HARDIRQS && ABX500_CORE
+       depends on GENERIC_HARDIRQS && ABX500_CORE && MFD_DB8500_PRCMU
        select MFD_CORE
        help
          Select this option to enable access to AB8500 power management
@@ -722,6 +765,16 @@ config LPC_SCH
          LPC bridge function of the Intel SCH provides support for
          System Management Bus and General Purpose I/O.
 
+config LPC_ICH
+       tristate "Intel ICH LPC"
+       depends on PCI
+       select MFD_CORE
+       help
+         The LPC bridge function of the Intel ICH provides support for
+         many functional units. This driver provides needed support for
+         other drivers to control these functions, currently GPIO and
+         watchdog.
+
 config MFD_RDC321X
        tristate "Support for RDC-R321x southbridge"
        select MFD_CORE
@@ -854,6 +907,11 @@ config MFD_RC5T583
          Additional drivers must be enabled in order to use the
          different functionality of the device.
 
+config MFD_STA2X11
+       bool "STA2X11 multi function device support"
+       depends on STA2X11
+       select MFD_CORE
+
 config MFD_ANATOP
        bool "Support for Freescale i.MX on-chip ANATOP controller"
        depends on SOC_IMX6Q
index 43672b87805a6346178d000426f11f38a15efaad..75f6ed68a4b9e259616795e41bba94eea0b755ea 100644 (file)
@@ -15,6 +15,7 @@ obj-$(CONFIG_MFD_DAVINCI_VOICECODEC)  += davinci_voicecodec.o
 obj-$(CONFIG_MFD_DM355EVM_MSP) += dm355evm_msp.o
 obj-$(CONFIG_MFD_TI_SSP)       += ti-ssp.o
 
+obj-$(CONFIG_MFD_STA2X11)      += sta2x11-mfd.o
 obj-$(CONFIG_MFD_STMPE)                += stmpe.o
 obj-$(CONFIG_STMPE_I2C)                += stmpe-i2c.o
 obj-$(CONFIG_STMPE_SPI)                += stmpe-spi.o
@@ -54,6 +55,8 @@ obj-$(CONFIG_TWL6030_PWM)     += twl6030-pwm.o
 obj-$(CONFIG_TWL6040_CORE)     += twl6040-core.o twl6040-irq.o
 
 obj-$(CONFIG_MFD_MC13XXX)      += mc13xxx-core.o
+obj-$(CONFIG_MFD_MC13XXX_SPI)  += mc13xxx-spi.o
+obj-$(CONFIG_MFD_MC13XXX_I2C)  += mc13xxx-i2c.o
 
 obj-$(CONFIG_MFD_CORE)         += mfd-core.o
 
@@ -75,6 +78,7 @@ obj-$(CONFIG_PMIC_DA9052)     += da9052-core.o
 obj-$(CONFIG_MFD_DA9052_SPI)   += da9052-spi.o
 obj-$(CONFIG_MFD_DA9052_I2C)   += da9052-i2c.o
 
+obj-$(CONFIG_MFD_MAX77693)     += max77693.o max77693-irq.o
 max8925-objs                   := max8925-core.o max8925-i2c.o
 obj-$(CONFIG_MFD_MAX8925)      += max8925.o
 obj-$(CONFIG_MFD_MAX8997)      += max8997.o max8997-irq.o
@@ -87,15 +91,15 @@ obj-$(CONFIG_PCF50633_GPIO) += pcf50633-gpio.o
 obj-$(CONFIG_ABX500_CORE)      += abx500-core.o
 obj-$(CONFIG_AB3100_CORE)      += ab3100-core.o
 obj-$(CONFIG_AB3100_OTP)       += ab3100-otp.o
-obj-$(CONFIG_AB8500_CORE)      += ab8500-core.o ab8500-sysctrl.o
 obj-$(CONFIG_AB8500_DEBUG)     += ab8500-debugfs.o
 obj-$(CONFIG_AB8500_GPADC)     += ab8500-gpadc.o
 obj-$(CONFIG_MFD_DB8500_PRCMU) += db8500-prcmu.o
-# ab8500-i2c need to come after db8500-prcmu (which provides the channel)
-obj-$(CONFIG_AB8500_I2C_CORE)  += ab8500-i2c.o
+# ab8500-core need to come after db8500-prcmu (which provides the channel)
+obj-$(CONFIG_AB8500_CORE)      += ab8500-core.o ab8500-sysctrl.o
 obj-$(CONFIG_MFD_TIMBERDALE)    += timberdale.o
 obj-$(CONFIG_PMIC_ADP5520)     += adp5520.o
 obj-$(CONFIG_LPC_SCH)          += lpc_sch.o
+obj-$(CONFIG_LPC_ICH)          += lpc_ich.o
 obj-$(CONFIG_MFD_RDC321X)      += rdc321x-southbridge.o
 obj-$(CONFIG_MFD_JANZ_CMODIO)  += janz-cmodio.o
 obj-$(CONFIG_MFD_JZ4740_ADC)   += jz4740-adc.o
index 1f08704f7ae8ecab27c3247d4009f0c71de91b83..dac0e299860353f0a299a8b66c14c745e587a133 100644 (file)
 #include <linux/mfd/core.h>
 #include <linux/mfd/abx500.h>
 #include <linux/mfd/abx500/ab8500.h>
+#include <linux/mfd/dbx500-prcmu.h>
 #include <linux/regulator/ab8500.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
 
 /*
  * Interrupt register offsets
 #define AB8500_IT_MASK23_REG           0x56
 #define AB8500_IT_MASK24_REG           0x57
 
+/*
+ * latch hierarchy registers
+ */
+#define AB8500_IT_LATCHHIER1_REG       0x60
+#define AB8500_IT_LATCHHIER2_REG       0x61
+#define AB8500_IT_LATCHHIER3_REG       0x62
+
+#define AB8500_IT_LATCHHIER_NUM                3
+
 #define AB8500_REV_REG                 0x80
 #define AB8500_IC_NAME_REG             0x82
 #define AB8500_SWITCH_OFF_STATUS       0x00
 
 #define AB8500_TURN_ON_STATUS          0x00
 
+static bool no_bm; /* No battery management */
+module_param(no_bm, bool, S_IRUGO);
+
 #define AB9540_MODEM_CTRL2_REG                 0x23
 #define AB9540_MODEM_CTRL2_SWDBBRSTN_BIT       BIT(2)
 
@@ -125,6 +140,41 @@ static const char ab8500_version_str[][7] = {
        [AB8500_VERSION_AB8540] = "AB8540",
 };
 
+static int ab8500_i2c_write(struct ab8500 *ab8500, u16 addr, u8 data)
+{
+       int ret;
+
+       ret = prcmu_abb_write((u8)(addr >> 8), (u8)(addr & 0xFF), &data, 1);
+       if (ret < 0)
+               dev_err(ab8500->dev, "prcmu i2c error %d\n", ret);
+       return ret;
+}
+
+static int ab8500_i2c_write_masked(struct ab8500 *ab8500, u16 addr, u8 mask,
+       u8 data)
+{
+       int ret;
+
+       ret = prcmu_abb_write_masked((u8)(addr >> 8), (u8)(addr & 0xFF), &data,
+               &mask, 1);
+       if (ret < 0)
+               dev_err(ab8500->dev, "prcmu i2c error %d\n", ret);
+       return ret;
+}
+
+static int ab8500_i2c_read(struct ab8500 *ab8500, u16 addr)
+{
+       int ret;
+       u8 data;
+
+       ret = prcmu_abb_read((u8)(addr >> 8), (u8)(addr & 0xFF), &data, 1);
+       if (ret < 0) {
+               dev_err(ab8500->dev, "prcmu i2c error %d\n", ret);
+               return ret;
+       }
+       return (int)data;
+}
+
 static int ab8500_get_chip_id(struct device *dev)
 {
        struct ab8500 *ab8500;
@@ -161,9 +211,13 @@ static int set_register_interruptible(struct ab8500 *ab8500, u8 bank,
 static int ab8500_set_register(struct device *dev, u8 bank,
        u8 reg, u8 value)
 {
+       int ret;
        struct ab8500 *ab8500 = dev_get_drvdata(dev->parent);
 
-       return set_register_interruptible(ab8500, bank, reg, value);
+       atomic_inc(&ab8500->transfer_ongoing);
+       ret = set_register_interruptible(ab8500, bank, reg, value);
+       atomic_dec(&ab8500->transfer_ongoing);
+       return ret;
 }
 
 static int get_register_interruptible(struct ab8500 *ab8500, u8 bank,
@@ -192,9 +246,13 @@ static int get_register_interruptible(struct ab8500 *ab8500, u8 bank,
 static int ab8500_get_register(struct device *dev, u8 bank,
        u8 reg, u8 *value)
 {
+       int ret;
        struct ab8500 *ab8500 = dev_get_drvdata(dev->parent);
 
-       return get_register_interruptible(ab8500, bank, reg, value);
+       atomic_inc(&ab8500->transfer_ongoing);
+       ret = get_register_interruptible(ab8500, bank, reg, value);
+       atomic_dec(&ab8500->transfer_ongoing);
+       return ret;
 }
 
 static int mask_and_set_register_interruptible(struct ab8500 *ab8500, u8 bank,
@@ -241,11 +299,14 @@ out:
 static int ab8500_mask_and_set_register(struct device *dev,
        u8 bank, u8 reg, u8 bitmask, u8 bitvalues)
 {
+       int ret;
        struct ab8500 *ab8500 = dev_get_drvdata(dev->parent);
 
-       return mask_and_set_register_interruptible(ab8500, bank, reg,
-               bitmask, bitvalues);
-
+       atomic_inc(&ab8500->transfer_ongoing);
+       ret= mask_and_set_register_interruptible(ab8500, bank, reg,
+                                                bitmask, bitvalues);
+       atomic_dec(&ab8500->transfer_ongoing);
+       return ret;
 }
 
 static struct abx500_ops ab8500_ops = {
@@ -264,6 +325,7 @@ static void ab8500_irq_lock(struct irq_data *data)
        struct ab8500 *ab8500 = irq_data_get_irq_chip_data(data);
 
        mutex_lock(&ab8500->irq_lock);
+       atomic_inc(&ab8500->transfer_ongoing);
 }
 
 static void ab8500_irq_sync_unlock(struct irq_data *data)
@@ -292,7 +354,7 @@ static void ab8500_irq_sync_unlock(struct irq_data *data)
                reg = AB8500_IT_MASK1_REG + ab8500->irq_reg_offset[i];
                set_register_interruptible(ab8500, AB8500_INTERRUPT, reg, new);
        }
-
+       atomic_dec(&ab8500->transfer_ongoing);
        mutex_unlock(&ab8500->irq_lock);
 }
 
@@ -325,6 +387,90 @@ static struct irq_chip ab8500_irq_chip = {
        .irq_unmask             = ab8500_irq_unmask,
 };
 
+static int ab8500_handle_hierarchical_line(struct ab8500 *ab8500,
+                                       int latch_offset, u8 latch_val)
+{
+       int int_bit = __ffs(latch_val);
+       int line, i;
+
+       do {
+               int_bit = __ffs(latch_val);
+
+               for (i = 0; i < ab8500->mask_size; i++)
+                       if (ab8500->irq_reg_offset[i] == latch_offset)
+                               break;
+
+               if (i >= ab8500->mask_size) {
+                       dev_err(ab8500->dev, "Register offset 0x%2x not declared\n",
+                                       latch_offset);
+                       return -ENXIO;
+               }
+
+               line = (i << 3) + int_bit;
+               latch_val &= ~(1 << int_bit);
+
+               handle_nested_irq(ab8500->irq_base + line);
+       } while (latch_val);
+
+       return 0;
+}
+
+static int ab8500_handle_hierarchical_latch(struct ab8500 *ab8500,
+                                       int hier_offset, u8 hier_val)
+{
+       int latch_bit, status;
+       u8 latch_offset, latch_val;
+
+       do {
+               latch_bit = __ffs(hier_val);
+               latch_offset = (hier_offset << 3) + latch_bit;
+
+               /* Fix inconsistent ITFromLatch25 bit mapping... */
+               if (unlikely(latch_offset == 17))
+                       latch_offset = 24;
+
+               status = get_register_interruptible(ab8500,
+                               AB8500_INTERRUPT,
+                               AB8500_IT_LATCH1_REG + latch_offset,
+                               &latch_val);
+               if (status < 0 || latch_val == 0)
+                       goto discard;
+
+               status = ab8500_handle_hierarchical_line(ab8500,
+                               latch_offset, latch_val);
+               if (status < 0)
+                       return status;
+discard:
+               hier_val &= ~(1 << latch_bit);
+       } while (hier_val);
+
+       return 0;
+}
+
+static irqreturn_t ab8500_hierarchical_irq(int irq, void *dev)
+{
+       struct ab8500 *ab8500 = dev;
+       u8 i;
+
+       dev_vdbg(ab8500->dev, "interrupt\n");
+
+       /*  Hierarchical interrupt version */
+       for (i = 0; i < AB8500_IT_LATCHHIER_NUM; i++) {
+               int status;
+               u8 hier_val;
+
+               status = get_register_interruptible(ab8500, AB8500_INTERRUPT,
+                       AB8500_IT_LATCHHIER1_REG + i, &hier_val);
+               if (status < 0 || hier_val == 0)
+                       continue;
+
+               status = ab8500_handle_hierarchical_latch(ab8500, i, hier_val);
+               if (status < 0)
+                       break;
+       }
+       return IRQ_HANDLED;
+}
+
 static irqreturn_t ab8500_irq(int irq, void *dev)
 {
        struct ab8500 *ab8500 = dev;
@@ -332,6 +478,8 @@ static irqreturn_t ab8500_irq(int irq, void *dev)
 
        dev_vdbg(ab8500->dev, "interrupt\n");
 
+       atomic_inc(&ab8500->transfer_ongoing);
+
        for (i = 0; i < ab8500->mask_size; i++) {
                int regoffset = ab8500->irq_reg_offset[i];
                int status;
@@ -355,9 +503,10 @@ static irqreturn_t ab8500_irq(int irq, void *dev)
 
                        handle_nested_irq(ab8500->irq_base + line);
                        value &= ~(1 << bit);
+
                } while (value);
        }
-
+       atomic_dec(&ab8500->transfer_ongoing);
        return IRQ_HANDLED;
 }
 
@@ -411,6 +560,14 @@ static void ab8500_irq_remove(struct ab8500 *ab8500)
        }
 }
 
+int ab8500_suspend(struct ab8500 *ab8500)
+{
+       if (atomic_read(&ab8500->transfer_ongoing))
+               return -EINVAL;
+       else
+               return 0;
+}
+
 /* AB8500 GPIO Resources */
 static struct resource __devinitdata ab8500_gpio_resources[] = {
        {
@@ -744,6 +901,39 @@ static struct resource __devinitdata ab8500_usb_resources[] = {
        },
 };
 
+static struct resource __devinitdata ab8505_iddet_resources[] = {
+       {
+               .name  = "KeyDeglitch",
+               .start = AB8505_INT_KEYDEGLITCH,
+               .end   = AB8505_INT_KEYDEGLITCH,
+               .flags = IORESOURCE_IRQ,
+       },
+       {
+               .name  = "KP",
+               .start = AB8505_INT_KP,
+               .end   = AB8505_INT_KP,
+               .flags = IORESOURCE_IRQ,
+       },
+       {
+               .name  = "IKP",
+               .start = AB8505_INT_IKP,
+               .end   = AB8505_INT_IKP,
+               .flags = IORESOURCE_IRQ,
+       },
+       {
+               .name  = "IKR",
+               .start = AB8505_INT_IKR,
+               .end   = AB8505_INT_IKR,
+               .flags = IORESOURCE_IRQ,
+       },
+       {
+               .name  = "KeyStuck",
+               .start = AB8505_INT_KEYSTUCK,
+               .end   = AB8505_INT_KEYSTUCK,
+               .flags = IORESOURCE_IRQ,
+       },
+};
+
 static struct resource __devinitdata ab8500_temp_resources[] = {
        {
                .name  = "AB8500_TEMP_WARM",
@@ -777,35 +967,11 @@ static struct mfd_cell __devinitdata abx500_common_devs[] = {
                .num_resources = ARRAY_SIZE(ab8500_rtc_resources),
                .resources = ab8500_rtc_resources,
        },
-       {
-               .name = "ab8500-charger",
-               .num_resources = ARRAY_SIZE(ab8500_charger_resources),
-               .resources = ab8500_charger_resources,
-       },
-       {
-               .name = "ab8500-btemp",
-               .num_resources = ARRAY_SIZE(ab8500_btemp_resources),
-               .resources = ab8500_btemp_resources,
-       },
-       {
-               .name = "ab8500-fg",
-               .num_resources = ARRAY_SIZE(ab8500_fg_resources),
-               .resources = ab8500_fg_resources,
-       },
-       {
-               .name = "ab8500-chargalg",
-               .num_resources = ARRAY_SIZE(ab8500_chargalg_resources),
-               .resources = ab8500_chargalg_resources,
-       },
        {
                .name = "ab8500-acc-det",
                .num_resources = ARRAY_SIZE(ab8500_av_acc_detect_resources),
                .resources = ab8500_av_acc_detect_resources,
        },
-       {
-               .name = "ab8500-codec",
-       },
-
        {
                .name = "ab8500-poweron-key",
                .num_resources = ARRAY_SIZE(ab8500_poweronkey_db_resources),
@@ -834,6 +1000,29 @@ static struct mfd_cell __devinitdata abx500_common_devs[] = {
        },
 };
 
+static struct mfd_cell __devinitdata ab8500_bm_devs[] = {
+       {
+               .name = "ab8500-charger",
+               .num_resources = ARRAY_SIZE(ab8500_charger_resources),
+               .resources = ab8500_charger_resources,
+       },
+       {
+               .name = "ab8500-btemp",
+               .num_resources = ARRAY_SIZE(ab8500_btemp_resources),
+               .resources = ab8500_btemp_resources,
+       },
+       {
+               .name = "ab8500-fg",
+               .num_resources = ARRAY_SIZE(ab8500_fg_resources),
+               .resources = ab8500_fg_resources,
+       },
+       {
+               .name = "ab8500-chargalg",
+               .num_resources = ARRAY_SIZE(ab8500_chargalg_resources),
+               .resources = ab8500_chargalg_resources,
+       },
+};
+
 static struct mfd_cell __devinitdata ab8500_devs[] = {
        {
                .name = "ab8500-gpio",
@@ -845,6 +1034,9 @@ static struct mfd_cell __devinitdata ab8500_devs[] = {
                .num_resources = ARRAY_SIZE(ab8500_usb_resources),
                .resources = ab8500_usb_resources,
        },
+       {
+               .name = "ab8500-codec",
+       },
 };
 
 static struct mfd_cell __devinitdata ab9540_devs[] = {
@@ -858,6 +1050,18 @@ static struct mfd_cell __devinitdata ab9540_devs[] = {
                .num_resources = ARRAY_SIZE(ab8500_usb_resources),
                .resources = ab8500_usb_resources,
        },
+       {
+               .name = "ab9540-codec",
+       },
+};
+
+/* Device list common to ab9540 and ab8505 */
+static struct mfd_cell __devinitdata ab9540_ab8505_devs[] = {
+       {
+               .name = "ab-iddet",
+               .num_resources = ARRAY_SIZE(ab8505_iddet_resources),
+               .resources = ab8505_iddet_resources,
+       },
 };
 
 static ssize_t show_chip_id(struct device *dev,
@@ -1003,18 +1207,66 @@ static struct attribute_group ab9540_attr_group = {
        .attrs  = ab9540_sysfs_entries,
 };
 
-int __devinit ab8500_init(struct ab8500 *ab8500, enum ab8500_version version)
+static const struct of_device_id ab8500_match[] = {
+       {
+               .compatible = "stericsson,ab8500",
+               .data = (void *)AB8500_VERSION_AB8500,
+       },
+       {},
+};
+
+static int __devinit ab8500_probe(struct platform_device *pdev)
 {
-       struct ab8500_platform_data *plat = dev_get_platdata(ab8500->dev);
+       struct ab8500_platform_data *plat = dev_get_platdata(&pdev->dev);
+       const struct platform_device_id *platid = platform_get_device_id(pdev);
+       enum ab8500_version version = AB8500_VERSION_UNDEFINED;
+       struct device_node *np = pdev->dev.of_node;
+       struct ab8500 *ab8500;
+       struct resource *resource;
        int ret;
        int i;
        u8 value;
 
+       ab8500 = kzalloc(sizeof *ab8500, GFP_KERNEL);
+       if (!ab8500)
+               return -ENOMEM;
+
        if (plat)
                ab8500->irq_base = plat->irq_base;
+       else if (np)
+               ret = of_property_read_u32(np, "stericsson,irq-base", &ab8500->irq_base);
+
+       if (!ab8500->irq_base) {
+               dev_info(&pdev->dev, "couldn't find irq-base\n");
+               ret = -EINVAL;
+               goto out_free_ab8500;
+       }
+
+       ab8500->dev = &pdev->dev;
+
+       resource = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+       if (!resource) {
+               ret = -ENODEV;
+               goto out_free_ab8500;
+       }
+
+       ab8500->irq = resource->start;
+
+       ab8500->read = ab8500_i2c_read;
+       ab8500->write = ab8500_i2c_write;
+       ab8500->write_masked = ab8500_i2c_write_masked;
 
        mutex_init(&ab8500->lock);
        mutex_init(&ab8500->irq_lock);
+       atomic_set(&ab8500->transfer_ongoing, 0);
+
+       platform_set_drvdata(pdev, ab8500);
+
+       if (platid)
+               version = platid->driver_data;
+       else if (np)
+               version = (unsigned int)
+                       of_match_device(ab8500_match, &pdev->dev)->data;
 
        if (version != AB8500_VERSION_UNDEFINED)
                ab8500->version = version;
@@ -1022,7 +1274,7 @@ int __devinit ab8500_init(struct ab8500 *ab8500, enum ab8500_version version)
                ret = get_register_interruptible(ab8500, AB8500_MISC,
                        AB8500_IC_NAME_REG, &value);
                if (ret < 0)
-                       return ret;
+                       goto out_free_ab8500;
 
                ab8500->version = value;
        }
@@ -1030,7 +1282,7 @@ int __devinit ab8500_init(struct ab8500 *ab8500, enum ab8500_version version)
        ret = get_register_interruptible(ab8500, AB8500_MISC,
                AB8500_REV_REG, &value);
        if (ret < 0)
-               return ret;
+               goto out_free_ab8500;
 
        ab8500->chip_id = value;
 
@@ -1105,30 +1357,57 @@ int __devinit ab8500_init(struct ab8500 *ab8500, enum ab8500_version version)
                if (ret)
                        goto out_freeoldmask;
 
-               ret = request_threaded_irq(ab8500->irq, NULL, ab8500_irq,
-                                          IRQF_ONESHOT | IRQF_NO_SUSPEND,
-                                          "ab8500", ab8500);
+               /*  Activate this feature only in ab9540 */
+               /*  till tests are done on ab8500 1p2 or later*/
+               if (is_ab9540(ab8500))
+                       ret = request_threaded_irq(ab8500->irq, NULL,
+                                       ab8500_hierarchical_irq,
+                                       IRQF_ONESHOT | IRQF_NO_SUSPEND,
+                                       "ab8500", ab8500);
+               else
+                       ret = request_threaded_irq(ab8500->irq, NULL,
+                                       ab8500_irq,
+                                       IRQF_ONESHOT | IRQF_NO_SUSPEND,
+                                       "ab8500", ab8500);
                if (ret)
                        goto out_removeirq;
        }
 
-       ret = mfd_add_devices(ab8500->dev, 0, abx500_common_devs,
-                             ARRAY_SIZE(abx500_common_devs), NULL,
-                             ab8500->irq_base);
+       if (!np) {
+               ret = mfd_add_devices(ab8500->dev, 0, abx500_common_devs,
+                               ARRAY_SIZE(abx500_common_devs), NULL,
+                               ab8500->irq_base);
 
-       if (ret)
-               goto out_freeirq;
+               if (ret)
+                       goto out_freeirq;
+
+               if (is_ab9540(ab8500))
+                       ret = mfd_add_devices(ab8500->dev, 0, ab9540_devs,
+                                       ARRAY_SIZE(ab9540_devs), NULL,
+                                       ab8500->irq_base);
+               else
+                       ret = mfd_add_devices(ab8500->dev, 0, ab8500_devs,
+                                       ARRAY_SIZE(ab8500_devs), NULL,
+                                       ab8500->irq_base);
+               if (ret)
+                       goto out_freeirq;
 
-       if (is_ab9540(ab8500))
-               ret = mfd_add_devices(ab8500->dev, 0, ab9540_devs,
-                             ARRAY_SIZE(ab9540_devs), NULL,
-                             ab8500->irq_base);
-       else
-               ret = mfd_add_devices(ab8500->dev, 0, ab8500_devs,
-                             ARRAY_SIZE(ab9540_devs), NULL,
-                             ab8500->irq_base);
-       if (ret)
-               goto out_freeirq;
+               if (is_ab9540(ab8500) || is_ab8505(ab8500))
+                       ret = mfd_add_devices(ab8500->dev, 0, ab9540_ab8505_devs,
+                                       ARRAY_SIZE(ab9540_ab8505_devs), NULL,
+                                       ab8500->irq_base);
+               if (ret)
+                       goto out_freeirq;
+       }
+
+       if (!no_bm) {
+               /* Add battery management devices */
+               ret = mfd_add_devices(ab8500->dev, 0, ab8500_bm_devs,
+                                     ARRAY_SIZE(ab8500_bm_devs), NULL,
+                                     ab8500->irq_base);
+               if (ret)
+                       dev_err(ab8500->dev, "error adding bm devices\n");
+       }
 
        if (is_ab9540(ab8500))
                ret = sysfs_create_group(&ab8500->dev->kobj,
@@ -1151,12 +1430,16 @@ out_freeoldmask:
        kfree(ab8500->oldmask);
 out_freemask:
        kfree(ab8500->mask);
+out_free_ab8500:
+       kfree(ab8500);
 
        return ret;
 }
 
-int __devexit ab8500_exit(struct ab8500 *ab8500)
+static int __devexit ab8500_remove(struct platform_device *pdev)
 {
+       struct ab8500 *ab8500 = platform_get_drvdata(pdev);
+
        if (is_ab9540(ab8500))
                sysfs_remove_group(&ab8500->dev->kobj, &ab9540_attr_group);
        else
@@ -1168,10 +1451,42 @@ int __devexit ab8500_exit(struct ab8500 *ab8500)
        }
        kfree(ab8500->oldmask);
        kfree(ab8500->mask);
+       kfree(ab8500);
 
        return 0;
 }
 
+static const struct platform_device_id ab8500_id[] = {
+       { "ab8500-core", AB8500_VERSION_AB8500 },
+       { "ab8505-i2c", AB8500_VERSION_AB8505 },
+       { "ab9540-i2c", AB8500_VERSION_AB9540 },
+       { "ab8540-i2c", AB8500_VERSION_AB8540 },
+       { }
+};
+
+static struct platform_driver ab8500_core_driver = {
+       .driver = {
+               .name = "ab8500-core",
+               .owner = THIS_MODULE,
+               .of_match_table = ab8500_match,
+       },
+       .probe  = ab8500_probe,
+       .remove = __devexit_p(ab8500_remove),
+       .id_table = ab8500_id,
+};
+
+static int __init ab8500_core_init(void)
+{
+       return platform_driver_register(&ab8500_core_driver);
+}
+
+static void __exit ab8500_core_exit(void)
+{
+       platform_driver_unregister(&ab8500_core_driver);
+}
+arch_initcall(ab8500_core_init);
+module_exit(ab8500_core_exit);
+
 MODULE_AUTHOR("Mattias Wallin, Srinidhi Kasagar, Rabin Vincent");
 MODULE_DESCRIPTION("AB8500 MFD core");
 MODULE_LICENSE("GPL v2");
index 9a0211aa88971c0207b3caa18d30eab6cb57821a..50c4c89ab2202fba117a05087799bfaf9ddba9df 100644 (file)
@@ -608,10 +608,16 @@ static int __devexit ab8500_debug_remove(struct platform_device *plf)
        return 0;
 }
 
+static const struct of_device_id ab8500_debug_match[] = {
+        { .compatible = "stericsson,ab8500-debug", },
+        {}
+};
+
 static struct platform_driver ab8500_debug_driver = {
        .driver = {
                .name = "ab8500-debug",
                .owner = THIS_MODULE,
+               .of_match_table = ab8500_debug_match,
        },
        .probe  = ab8500_debug_probe,
        .remove = __devexit_p(ab8500_debug_remove)
index c39fc716e1dcf520592bc866cba0be4e661d0e09..b86fd8e1ec3fbae7c4bf6770c1598a777d657d78 100644 (file)
@@ -584,7 +584,7 @@ static int __devinit ab8500_gpadc_probe(struct platform_device *pdev)
 
        gpadc->irq = platform_get_irq_byname(pdev, "SW_CONV_END");
        if (gpadc->irq < 0) {
-               dev_err(gpadc->dev, "failed to get platform irq-%d\n",
+               dev_err(&pdev->dev, "failed to get platform irq-%d\n",
                        gpadc->irq);
                ret = gpadc->irq;
                goto fail;
@@ -648,12 +648,18 @@ static int __devexit ab8500_gpadc_remove(struct platform_device *pdev)
        return 0;
 }
 
+static const struct of_device_id ab8500_gpadc_match[] = {
+       { .compatible = "stericsson,ab8500-gpadc", },
+       {}
+};
+
 static struct platform_driver ab8500_gpadc_driver = {
        .probe = ab8500_gpadc_probe,
        .remove = __devexit_p(ab8500_gpadc_remove),
        .driver = {
                .name = "ab8500-gpadc",
                .owner = THIS_MODULE,
+               .of_match_table = ab8500_gpadc_match,
        },
 };
 
diff --git a/drivers/mfd/ab8500-i2c.c b/drivers/mfd/ab8500-i2c.c
deleted file mode 100644 (file)
index b83045f..0000000
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Copyright (C) ST-Ericsson SA 2010
- * Author: Mattias Wallin <mattias.wallin@stericsson.com> for ST-Ericsson.
- * License Terms: GNU General Public License v2
- * This file was based on drivers/mfd/ab8500-spi.c
- */
-
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/mfd/abx500/ab8500.h>
-#include <linux/mfd/dbx500-prcmu.h>
-
-static int ab8500_i2c_write(struct ab8500 *ab8500, u16 addr, u8 data)
-{
-       int ret;
-
-       ret = prcmu_abb_write((u8)(addr >> 8), (u8)(addr & 0xFF), &data, 1);
-       if (ret < 0)
-               dev_err(ab8500->dev, "prcmu i2c error %d\n", ret);
-       return ret;
-}
-
-static int ab8500_i2c_write_masked(struct ab8500 *ab8500, u16 addr, u8 mask,
-       u8 data)
-{
-       int ret;
-
-       ret = prcmu_abb_write_masked((u8)(addr >> 8), (u8)(addr & 0xFF), &data,
-               &mask, 1);
-       if (ret < 0)
-               dev_err(ab8500->dev, "prcmu i2c error %d\n", ret);
-       return ret;
-}
-
-static int ab8500_i2c_read(struct ab8500 *ab8500, u16 addr)
-{
-       int ret;
-       u8 data;
-
-       ret = prcmu_abb_read((u8)(addr >> 8), (u8)(addr & 0xFF), &data, 1);
-       if (ret < 0) {
-               dev_err(ab8500->dev, "prcmu i2c error %d\n", ret);
-               return ret;
-       }
-       return (int)data;
-}
-
-static int __devinit ab8500_i2c_probe(struct platform_device *plf)
-{
-       const struct platform_device_id *platid = platform_get_device_id(plf);
-       struct ab8500 *ab8500;
-       struct resource *resource;
-       int ret;
-
-       ab8500 = kzalloc(sizeof *ab8500, GFP_KERNEL);
-       if (!ab8500)
-               return -ENOMEM;
-
-       ab8500->dev = &plf->dev;
-
-       resource = platform_get_resource(plf, IORESOURCE_IRQ, 0);
-       if (!resource) {
-               kfree(ab8500);
-               return -ENODEV;
-       }
-
-       ab8500->irq = resource->start;
-
-       ab8500->read = ab8500_i2c_read;
-       ab8500->write = ab8500_i2c_write;
-       ab8500->write_masked = ab8500_i2c_write_masked;
-
-       platform_set_drvdata(plf, ab8500);
-
-       ret = ab8500_init(ab8500, platid->driver_data);
-       if (ret)
-               kfree(ab8500);
-
-
-       return ret;
-}
-
-static int __devexit ab8500_i2c_remove(struct platform_device *plf)
-{
-       struct ab8500 *ab8500 = platform_get_drvdata(plf);
-
-       ab8500_exit(ab8500);
-       kfree(ab8500);
-
-       return 0;
-}
-
-static const struct platform_device_id ab8500_id[] = {
-       { "ab8500-i2c", AB8500_VERSION_AB8500 },
-       { "ab8505-i2c", AB8500_VERSION_AB8505 },
-       { "ab9540-i2c", AB8500_VERSION_AB9540 },
-       { "ab8540-i2c", AB8500_VERSION_AB8540 },
-       { }
-};
-
-static struct platform_driver ab8500_i2c_driver = {
-       .driver = {
-               .name = "ab8500-i2c",
-               .owner = THIS_MODULE,
-       },
-       .probe  = ab8500_i2c_probe,
-       .remove = __devexit_p(ab8500_i2c_remove),
-       .id_table = ab8500_id,
-};
-
-static int __init ab8500_i2c_init(void)
-{
-       return platform_driver_register(&ab8500_i2c_driver);
-}
-
-static void __exit ab8500_i2c_exit(void)
-{
-       platform_driver_unregister(&ab8500_i2c_driver);
-}
-arch_initcall(ab8500_i2c_init);
-module_exit(ab8500_i2c_exit);
-
-MODULE_AUTHOR("Mattias WALLIN <mattias.wallin@stericsson.com");
-MODULE_DESCRIPTION("AB8500 Core access via PRCMU I2C");
-MODULE_LICENSE("GPL v2");
index c28d4eb1eff019517d166476c590652f2ac1b8b1..5a3e51ccf25863b94dccd26b25f4e7002b90ac97 100644 (file)
@@ -61,10 +61,16 @@ static int __devexit ab8500_sysctrl_remove(struct platform_device *pdev)
        return 0;
 }
 
+static const struct of_device_id ab8500_sysctrl_match[] = {
+       { .compatible = "stericsson,ab8500-sysctrl", },
+       {}
+};
+
 static struct platform_driver ab8500_sysctrl_driver = {
        .driver = {
                .name = "ab8500-sysctrl",
                .owner = THIS_MODULE,
+               .of_match_table = ab8500_sysctrl_match,
        },
        .probe = ab8500_sysctrl_probe,
        .remove = __devexit_p(ab8500_sysctrl_remove),
index 2af42480635ec458f868bd180839b79050bbd6de..6da06341f6c909312afea387e875fe82cc1592b8 100644 (file)
 #include <linux/of_address.h>
 #include <linux/mfd/anatop.h>
 
-u32 anatop_get_bits(struct anatop *adata, u32 addr, int bit_shift,
-                   int bit_width)
+u32 anatop_read_reg(struct anatop *adata, u32 addr)
 {
-       u32 val, mask;
-
-       if (bit_width == 32)
-               mask = ~0;
-       else
-               mask = (1 << bit_width) - 1;
-
-       val = readl(adata->ioreg + addr);
-       val = (val >> bit_shift) & mask;
-
-       return val;
+       return readl(adata->ioreg + addr);
 }
-EXPORT_SYMBOL_GPL(anatop_get_bits);
+EXPORT_SYMBOL_GPL(anatop_read_reg);
 
-void anatop_set_bits(struct anatop *adata, u32 addr, int bit_shift,
-                    int bit_width, u32 data)
+void anatop_write_reg(struct anatop *adata, u32 addr, u32 data, u32 mask)
 {
-       u32 val, mask;
+       u32 val;
 
-       if (bit_width == 32)
-               mask = ~0;
-       else
-               mask = (1 << bit_width) - 1;
+       data &= mask;
 
        spin_lock(&adata->reglock);
-       val = readl(adata->ioreg + addr) & ~(mask << bit_shift);
-       writel((data << bit_shift) | val, adata->ioreg + addr);
+       val = readl(adata->ioreg + addr);
+       val &= ~mask;
+       val |= data;
+       writel(val, adata->ioreg + addr);
        spin_unlock(&adata->reglock);
 }
-EXPORT_SYMBOL_GPL(anatop_set_bits);
+EXPORT_SYMBOL_GPL(anatop_write_reg);
 
 static const struct of_device_id of_anatop_match[] = {
        { .compatible = "fsl,imx6q-anatop", },
index 1582c3d952579e66306e1ad8ce2713f3b0d03f9c..383421bf57609a994b7f8d537f97c269d2ab6302 100644 (file)
@@ -353,12 +353,28 @@ static int asic3_gpio_irq_type(struct irq_data *data, unsigned int type)
        return 0;
 }
 
+static int asic3_gpio_irq_set_wake(struct irq_data *data, unsigned int on)
+{
+       struct asic3 *asic = irq_data_get_irq_chip_data(data);
+       u32 bank, index;
+       u16 bit;
+
+       bank = asic3_irq_to_bank(asic, data->irq);
+       index = asic3_irq_to_index(asic, data->irq);
+       bit = 1<<index;
+
+       asic3_set_register(asic, bank + ASIC3_GPIO_SLEEP_MASK, bit, !on);
+
+       return 0;
+}
+
 static struct irq_chip asic3_gpio_irq_chip = {
        .name           = "ASIC3-GPIO",
        .irq_ack        = asic3_mask_gpio_irq,
        .irq_mask       = asic3_mask_gpio_irq,
        .irq_unmask     = asic3_unmask_gpio_irq,
        .irq_set_type   = asic3_gpio_irq_type,
+       .irq_set_wake   = asic3_gpio_irq_set_wake,
 };
 
 static struct irq_chip asic3_irq_chip = {
@@ -529,7 +545,7 @@ static int asic3_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
 {
        struct asic3 *asic = container_of(chip, struct asic3, gpio);
 
-       return (offset < ASIC3_NUM_GPIOS) ? asic->irq_base + offset : -ENXIO;
+       return asic->irq_base + offset;
 }
 
 static __init int asic3_gpio_probe(struct platform_device *pdev,
@@ -894,10 +910,13 @@ static int __init asic3_mfd_probe(struct platform_device *pdev,
        asic3_mmc_resources[0].start >>= asic->bus_shift;
        asic3_mmc_resources[0].end   >>= asic->bus_shift;
 
-       ret = mfd_add_devices(&pdev->dev, pdev->id,
+       if (pdata->clock_rate) {
+               ds1wm_pdata.clock_rate = pdata->clock_rate;
+               ret = mfd_add_devices(&pdev->dev, pdev->id,
                        &asic3_cell_ds1wm, 1, mem, asic->irq_base);
-       if (ret < 0)
-               goto out;
+               if (ret < 0)
+                       goto out;
+       }
 
        if (mem_sdio && (irq >= 0)) {
                ret = mfd_add_devices(&pdev->dev, pdev->id,
@@ -1000,6 +1019,9 @@ static int __init asic3_probe(struct platform_device *pdev)
 
        asic3_mfd_probe(pdev, pdata, mem);
 
+       asic3_set_register(asic, ASIC3_OFFSET(EXTCF, SELECT),
+               (ASIC3_EXTCF_CF0_BUF_EN|ASIC3_EXTCF_CF0_PWAIT_EN), 1);
+
        dev_info(asic->dev, "ASIC3 Core driver\n");
 
        return 0;
@@ -1021,6 +1043,9 @@ static int __devexit asic3_remove(struct platform_device *pdev)
        int ret;
        struct asic3 *asic = platform_get_drvdata(pdev);
 
+       asic3_set_register(asic, ASIC3_OFFSET(EXTCF, SELECT),
+               (ASIC3_EXTCF_CF0_BUF_EN|ASIC3_EXTCF_CF0_PWAIT_EN), 0);
+
        asic3_mfd_remove(pdev);
 
        ret = asic3_gpio_remove(pdev);
index 315fef5d466ac3f2d380246ffa75a056d131cb52..3419e726de478cb330801d1dfb1db42d2a5d1748 100644 (file)
@@ -186,18 +186,7 @@ static struct pci_driver cs5535_mfd_driver = {
        .remove = __devexit_p(cs5535_mfd_remove),
 };
 
-static int __init cs5535_mfd_init(void)
-{
-       return pci_register_driver(&cs5535_mfd_driver);
-}
-
-static void __exit cs5535_mfd_exit(void)
-{
-       pci_unregister_driver(&cs5535_mfd_driver);
-}
-
-module_init(cs5535_mfd_init);
-module_exit(cs5535_mfd_exit);
+module_pci_driver(cs5535_mfd_driver);
 
 MODULE_AUTHOR("Andres Salomon <dilinger@queued.net>");
 MODULE_DESCRIPTION("MFD driver for CS5535/CS5536 southbridge's ISA PCI device");
index 7776aff46269c38134bc4ca67d81d703989545b1..1f1313c905736f352d09c8f8d764be2464467cc5 100644 (file)
@@ -318,6 +318,135 @@ static bool da9052_reg_volatile(struct device *dev, unsigned int reg)
        }
 }
 
+/*
+ * TBAT look-up table is computed from the R90 reg (8 bit register)
+ * reading as below. The battery temperature is in milliCentigrade
+ * TBAT = (1/(t1+1/298) - 273) * 1000 mC
+ * where t1 = (1/B)* ln(( ADCval * 2.5)/(R25*ITBAT*255))
+ * Default values are R25 = 10e3, B = 3380, ITBAT = 50e-6
+ * Example:
+ * R25=10E3, B=3380, ITBAT=50e-6, ADCVAL=62d calculates
+ * TBAT = 20015 mili degrees Centrigrade
+ *
+*/
+static const int32_t tbat_lookup[255] = {
+       183258, 144221, 124334, 111336, 101826, 94397, 88343, 83257,
+       78889, 75071, 71688, 68656, 65914, 63414, 61120, 59001,
+       570366, 55204, 53490, 51881, 50364, 48931, 47574, 46285,
+       45059, 43889, 42772, 41703, 40678, 39694, 38748, 37838,
+       36961, 36115, 35297, 34507, 33743, 33002, 32284, 31588,
+       30911, 30254, 29615, 28994, 28389, 27799, 27225, 26664,
+       26117, 25584, 25062, 24553, 24054, 23567, 23091, 22624,
+       22167, 21719, 21281, 20851, 20429, 20015, 19610, 19211,
+       18820, 18436, 18058, 17688, 17323, 16965, 16612, 16266,
+       15925, 15589, 15259, 14933, 14613, 14298, 13987, 13681,
+       13379, 13082, 12788, 12499, 12214, 11933, 11655, 11382,
+       11112, 10845, 10582, 10322, 10066, 9812, 9562, 9315,
+       9071, 8830, 8591, 8356, 8123, 7893, 7665, 7440,
+       7218, 6998, 6780, 6565, 6352, 6141, 5933, 5726,
+       5522, 5320, 5120, 4922, 4726, 4532, 4340, 4149,
+       3961, 3774, 3589, 3406, 3225, 3045, 2867, 2690,
+       2516, 2342, 2170, 2000, 1831, 1664, 1498, 1334,
+       1171, 1009, 849, 690, 532, 376, 221, 67,
+       -84, -236, -386, -535, -683, -830, -975, -1119,
+       -1263, -1405, -1546, -1686, -1825, -1964, -2101, -2237,
+       -2372, -2506, -2639, -2771, -2902, -3033, -3162, -3291,
+       -3418, -3545, -3671, -3796, -3920, -4044, -4166, -4288,
+       -4409, -4529, -4649, -4767, -4885, -5002, -5119, -5235,
+       -5349, -5464, -5577, -5690, -5802, -5913, -6024, -6134,
+       -6244, -6352, -6461, -6568, -6675, -6781, -6887, -6992,
+       -7096, -7200, -7303, -7406, -7508, -7609, -7710, -7810,
+       -7910, -8009, -8108, -8206, -8304, -8401, -8497, -8593,
+       -8689, -8784, -8878, -8972, -9066, -9159, -9251, -9343,
+       -9435, -9526, -9617, -9707, -9796, -9886, -9975, -10063,
+       -10151, -10238, -10325, -10412, -10839, -10923, -11007, -11090,
+       -11173, -11256, -11338, -11420, -11501, -11583, -11663, -11744,
+       -11823, -11903, -11982
+};
+
+static const u8 chan_mux[DA9052_ADC_VBBAT + 1] = {
+       [DA9052_ADC_VDDOUT]     = DA9052_ADC_MAN_MUXSEL_VDDOUT,
+       [DA9052_ADC_ICH]        = DA9052_ADC_MAN_MUXSEL_ICH,
+       [DA9052_ADC_TBAT]       = DA9052_ADC_MAN_MUXSEL_TBAT,
+       [DA9052_ADC_VBAT]       = DA9052_ADC_MAN_MUXSEL_VBAT,
+       [DA9052_ADC_IN4]        = DA9052_ADC_MAN_MUXSEL_AD4,
+       [DA9052_ADC_IN5]        = DA9052_ADC_MAN_MUXSEL_AD5,
+       [DA9052_ADC_IN6]        = DA9052_ADC_MAN_MUXSEL_AD6,
+       [DA9052_ADC_VBBAT]      = DA9052_ADC_MAN_MUXSEL_VBBAT
+};
+
+int da9052_adc_manual_read(struct da9052 *da9052, unsigned char channel)
+{
+       int ret;
+       unsigned short calc_data;
+       unsigned short data;
+       unsigned char mux_sel;
+
+       if (channel > DA9052_ADC_VBBAT)
+               return -EINVAL;
+
+       mutex_lock(&da9052->auxadc_lock);
+
+       /* Channel gets activated on enabling the Conversion bit */
+       mux_sel = chan_mux[channel] | DA9052_ADC_MAN_MAN_CONV;
+
+       ret = da9052_reg_write(da9052, DA9052_ADC_MAN_REG, mux_sel);
+       if (ret < 0)
+               goto err;
+
+       /* Wait for an interrupt */
+       if (!wait_for_completion_timeout(&da9052->done,
+                                        msecs_to_jiffies(500))) {
+               dev_err(da9052->dev,
+                       "timeout waiting for ADC conversion interrupt\n");
+               ret = -ETIMEDOUT;
+               goto err;
+       }
+
+       ret = da9052_reg_read(da9052, DA9052_ADC_RES_H_REG);
+       if (ret < 0)
+               goto err;
+
+       calc_data = (unsigned short)ret;
+       data = calc_data << 2;
+
+       ret = da9052_reg_read(da9052, DA9052_ADC_RES_L_REG);
+       if (ret < 0)
+               goto err;
+
+       calc_data = (unsigned short)(ret & DA9052_ADC_RES_LSB);
+       data |= calc_data;
+
+       ret = data;
+
+err:
+       mutex_unlock(&da9052->auxadc_lock);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(da9052_adc_manual_read);
+
+static irqreturn_t da9052_auxadc_irq(int irq, void *irq_data)
+{
+       struct da9052 *da9052 = irq_data;
+
+       complete(&da9052->done);
+
+       return IRQ_HANDLED;
+}
+
+int da9052_adc_read_temp(struct da9052 *da9052)
+{
+       int tbat;
+
+       tbat = da9052_reg_read(da9052, DA9052_TBAT_RES_REG);
+       if (tbat <= 0)
+               return tbat;
+
+       /* ARRAY_SIZE check is not needed since TBAT is a 8-bit register */
+       return tbat_lookup[tbat - 1];
+}
+EXPORT_SYMBOL_GPL(da9052_adc_read_temp);
+
 static struct resource da9052_rtc_resource = {
        .name = "ALM",
        .start = DA9052_IRQ_ALARM,
@@ -646,6 +775,9 @@ int __devinit da9052_device_init(struct da9052 *da9052, u8 chip_id)
        struct irq_desc *desc;
        int ret;
 
+       mutex_init(&da9052->auxadc_lock);
+       init_completion(&da9052->done);
+
        if (pdata && pdata->init != NULL)
                pdata->init(da9052);
 
@@ -665,6 +797,12 @@ int __devinit da9052_device_init(struct da9052 *da9052, u8 chip_id)
 
        da9052->irq_base = regmap_irq_chip_get_base(da9052->irq_data);
 
+       ret = request_threaded_irq(DA9052_IRQ_ADC_EOM, NULL, da9052_auxadc_irq,
+                                  IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+                                  "adc irq", da9052);
+       if (ret != 0)
+               dev_err(da9052->dev, "DA9052 ADC IRQ failed ret=%d\n", ret);
+
        ret = mfd_add_devices(da9052->dev, -1, da9052_subdev_info,
                              ARRAY_SIZE(da9052_subdev_info), NULL, 0);
        if (ret)
@@ -673,6 +811,7 @@ int __devinit da9052_device_init(struct da9052 *da9052, u8 chip_id)
        return 0;
 
 err:
+       free_irq(DA9052_IRQ_ADC_EOM, da9052);
        mfd_remove_devices(da9052->dev);
 regmap_err:
        return ret;
@@ -680,6 +819,7 @@ regmap_err:
 
 void da9052_device_exit(struct da9052 *da9052)
 {
+       free_irq(DA9052_IRQ_ADC_EOM, da9052);
        regmap_del_irq_chip(da9052->chip_irq, da9052->irq_data);
        mfd_remove_devices(da9052->dev);
 }
index 36b88e395499f1673ecb57eee39b0c6a3fa920cd..82c9d64502868ba94ea8d215d9a5e864ed42f19a 100644 (file)
 #include <linux/mfd/da9052/da9052.h>
 #include <linux/mfd/da9052/reg.h>
 
+#ifdef CONFIG_OF
+#include <linux/of.h>
+#include <linux/of_device.h>
+#endif
+
 static int da9052_i2c_enable_multiwrite(struct da9052 *da9052)
 {
        int reg_val, ret;
@@ -41,13 +46,31 @@ static int da9052_i2c_enable_multiwrite(struct da9052 *da9052)
        return 0;
 }
 
+static struct i2c_device_id da9052_i2c_id[] = {
+       {"da9052", DA9052},
+       {"da9053-aa", DA9053_AA},
+       {"da9053-ba", DA9053_BA},
+       {"da9053-bb", DA9053_BB},
+       {}
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id dialog_dt_ids[] = {
+       { .compatible = "dlg,da9052", .data = &da9052_i2c_id[0] },
+       { .compatible = "dlg,da9053-aa", .data = &da9052_i2c_id[1] },
+       { .compatible = "dlg,da9053-ab", .data = &da9052_i2c_id[2] },
+       { .compatible = "dlg,da9053-bb", .data = &da9052_i2c_id[3] },
+       { /* sentinel */ }
+};
+#endif
+
 static int __devinit da9052_i2c_probe(struct i2c_client *client,
                                       const struct i2c_device_id *id)
 {
        struct da9052 *da9052;
        int ret;
 
-       da9052 = kzalloc(sizeof(struct da9052), GFP_KERNEL);
+       da9052 = devm_kzalloc(&client->dev, sizeof(struct da9052), GFP_KERNEL);
        if (!da9052)
                return -ENOMEM;
 
@@ -55,8 +78,7 @@ static int __devinit da9052_i2c_probe(struct i2c_client *client,
                                     I2C_FUNC_SMBUS_BYTE_DATA)) {
                dev_info(&client->dev, "Error in %s:i2c_check_functionality\n",
                         __func__);
-               ret = -ENODEV;
-               goto err;
+               return  -ENODEV;
        }
 
        da9052->dev = &client->dev;
@@ -64,29 +86,39 @@ static int __devinit da9052_i2c_probe(struct i2c_client *client,
 
        i2c_set_clientdata(client, da9052);
 
-       da9052->regmap = regmap_init_i2c(client, &da9052_regmap_config);
+       da9052->regmap = devm_regmap_init_i2c(client, &da9052_regmap_config);
        if (IS_ERR(da9052->regmap)) {
                ret = PTR_ERR(da9052->regmap);
                dev_err(&client->dev, "Failed to allocate register map: %d\n",
                        ret);
-               goto err;
+               return ret;
        }
 
        ret = da9052_i2c_enable_multiwrite(da9052);
        if (ret < 0)
-               goto err_regmap;
+               return ret;
+
+#ifdef CONFIG_OF
+       if (!id) {
+               struct device_node *np = client->dev.of_node;
+               const struct of_device_id *deviceid;
+
+               deviceid = of_match_node(dialog_dt_ids, np);
+               id = (const struct i2c_device_id *)deviceid->data;
+       }
+#endif
+
+       if (!id) {
+               ret = -ENODEV;
+               dev_err(&client->dev, "id is null.\n");
+               return ret;
+       }
 
        ret = da9052_device_init(da9052, id->driver_data);
        if (ret != 0)
-               goto err_regmap;
+               return ret;
 
        return 0;
-
-err_regmap:
-       regmap_exit(da9052->regmap);
-err:
-       kfree(da9052);
-       return ret;
 }
 
 static int __devexit da9052_i2c_remove(struct i2c_client *client)
@@ -94,20 +126,9 @@ static int __devexit da9052_i2c_remove(struct i2c_client *client)
        struct da9052 *da9052 = i2c_get_clientdata(client);
 
        da9052_device_exit(da9052);
-       regmap_exit(da9052->regmap);
-       kfree(da9052);
-
        return 0;
 }
 
-static struct i2c_device_id da9052_i2c_id[] = {
-       {"da9052", DA9052},
-       {"da9053-aa", DA9053_AA},
-       {"da9053-ba", DA9053_BA},
-       {"da9053-bb", DA9053_BB},
-       {}
-};
-
 static struct i2c_driver da9052_i2c_driver = {
        .probe = da9052_i2c_probe,
        .remove = __devexit_p(da9052_i2c_remove),
@@ -115,6 +136,9 @@ static struct i2c_driver da9052_i2c_driver = {
        .driver = {
                .name = "da9052",
                .owner = THIS_MODULE,
+#ifdef CONFIG_OF
+               .of_match_table = dialog_dt_ids,
+#endif
        },
 };
 
index 6faf149e8d94c8d2b90c3ef69edc6d2e98a0fa6d..dbeadc5a6436caca55f69a68f0865cc71084b0c8 100644 (file)
@@ -25,8 +25,9 @@ static int __devinit da9052_spi_probe(struct spi_device *spi)
 {
        int ret;
        const struct spi_device_id *id = spi_get_device_id(spi);
-       struct da9052 *da9052 = kzalloc(sizeof(struct da9052), GFP_KERNEL);
+       struct da9052 *da9052;
 
+       da9052 = devm_kzalloc(&spi->dev, sizeof(struct da9052), GFP_KERNEL);
        if (!da9052)
                return -ENOMEM;
 
@@ -42,25 +43,19 @@ static int __devinit da9052_spi_probe(struct spi_device *spi)
        da9052_regmap_config.read_flag_mask = 1;
        da9052_regmap_config.write_flag_mask = 0;
 
-       da9052->regmap = regmap_init_spi(spi, &da9052_regmap_config);
+       da9052->regmap = devm_regmap_init_spi(spi, &da9052_regmap_config);
        if (IS_ERR(da9052->regmap)) {
                ret = PTR_ERR(da9052->regmap);
                dev_err(&spi->dev, "Failed to allocate register map: %d\n",
                        ret);
-               goto err;
+               return ret;
        }
 
        ret = da9052_device_init(da9052, id->driver_data);
        if (ret != 0)
-               goto err_regmap;
+               return ret;
 
        return 0;
-
-err_regmap:
-       regmap_exit(da9052->regmap);
-err:
-       kfree(da9052);
-       return ret;
 }
 
 static int __devexit da9052_spi_remove(struct spi_device *spi)
@@ -68,9 +63,6 @@ static int __devexit da9052_spi_remove(struct spi_device *spi)
        struct da9052 *da9052 = dev_get_drvdata(&spi->dev);
 
        da9052_device_exit(da9052);
-       regmap_exit(da9052->regmap);
-       kfree(da9052);
-
        return 0;
 }
 
@@ -88,7 +80,6 @@ static struct spi_driver da9052_spi_driver = {
        .id_table = da9052_spi_id,
        .driver = {
                .name = "da9052",
-               .bus = &spi_bus_type,
                .owner = THIS_MODULE,
        },
 };
index 5be32489714f61a279b4fe30ce7badb37d5833a4..671c8bc14bbcb0bf3c91c450b14af6c0c34b4f19 100644 (file)
@@ -2720,6 +2720,7 @@ static struct regulator_consumer_supply db8500_vape_consumers[] = {
        REGULATOR_SUPPLY("v-i2c", "nmk-i2c.1"),
        REGULATOR_SUPPLY("v-i2c", "nmk-i2c.2"),
        REGULATOR_SUPPLY("v-i2c", "nmk-i2c.3"),
+       REGULATOR_SUPPLY("v-i2c", "nmk-i2c.4"),
        /* "v-mmc" changed to "vcore" in the mainline kernel */
        REGULATOR_SUPPLY("vcore", "sdi0"),
        REGULATOR_SUPPLY("vcore", "sdi1"),
@@ -2958,9 +2959,10 @@ static struct mfd_cell db8500_prcmu_devs[] = {
  * prcmu_fw_init - arch init call for the Linux PRCMU fw init logic
  *
  */
-static int __init db8500_prcmu_probe(struct platform_device *pdev)
+static int __devinit db8500_prcmu_probe(struct platform_device *pdev)
 {
-       int err = 0;
+       struct device_node *np = pdev->dev.of_node;
+       int irq = 0, err = 0;
 
        if (ux500_is_svp())
                return -ENODEV;
@@ -2970,8 +2972,14 @@ static int __init db8500_prcmu_probe(struct platform_device *pdev)
        /* Clean up the mailbox interrupts after pre-kernel code. */
        writel(ALL_MBOX_BITS, PRCM_ARM_IT1_CLR);
 
-       err = request_threaded_irq(IRQ_DB8500_PRCMU1, prcmu_irq_handler,
-               prcmu_irq_thread_fn, IRQF_NO_SUSPEND, "prcmu", NULL);
+       if (np)
+               irq = platform_get_irq(pdev, 0);
+
+       if (!np || irq <= 0)
+               irq = IRQ_DB8500_PRCMU1;
+
+       err = request_threaded_irq(irq, prcmu_irq_handler,
+               prcmu_irq_thread_fn, IRQF_NO_SUSPEND, "prcmu", NULL);
        if (err < 0) {
                pr_err("prcmu: Failed to allocate IRQ_DB8500_PRCMU1.\n");
                err = -EBUSY;
@@ -2981,14 +2989,16 @@ static int __init db8500_prcmu_probe(struct platform_device *pdev)
        if (cpu_is_u8500v20_or_later())
                prcmu_config_esram0_deep_sleep(ESRAM0_DEEP_SLEEP_STATE_RET);
 
-       err = mfd_add_devices(&pdev->dev, 0, db8500_prcmu_devs,
-                             ARRAY_SIZE(db8500_prcmu_devs), NULL,
-                             0);
+       if (!np) {
+               err = mfd_add_devices(&pdev->dev, 0, db8500_prcmu_devs,
+                               ARRAY_SIZE(db8500_prcmu_devs), NULL, 0);
+               if (err) {
+                       pr_err("prcmu: Failed to add subdevices\n");
+                       return err;
+               }
+       }
 
-       if (err)
-               pr_err("prcmu: Failed to add subdevices\n");
-       else
-               pr_info("DB8500 PRCMU initialized\n");
+       pr_info("DB8500 PRCMU initialized\n");
 
 no_irq_return:
        return err;
@@ -2999,11 +3009,12 @@ static struct platform_driver db8500_prcmu_driver = {
                .name = "db8500-prcmu",
                .owner = THIS_MODULE,
        },
+       .probe = db8500_prcmu_probe,
 };
 
 static int __init db8500_prcmu_init(void)
 {
-       return platform_driver_probe(&db8500_prcmu_driver, db8500_prcmu_probe);
+       return platform_driver_register(&db8500_prcmu_driver);
 }
 
 arch_initcall(db8500_prcmu_init);
index b76657eb0c51044503e3c71b7345c98596d33cbd..59df5584cb58f54a25a424ca2551417a231fcb4d 100644 (file)
@@ -406,7 +406,7 @@ static int __devinit intel_msic_probe(struct platform_device *pdev)
                return -ENXIO;
        }
 
-       msic = kzalloc(sizeof(*msic), GFP_KERNEL);
+       msic = devm_kzalloc(&pdev->dev, sizeof(*msic), GFP_KERNEL);
        if (!msic)
                return -ENOMEM;
 
@@ -421,21 +421,13 @@ static int __devinit intel_msic_probe(struct platform_device *pdev)
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        if (!res) {
                dev_err(&pdev->dev, "failed to get SRAM iomem resource\n");
-               ret = -ENODEV;
-               goto fail_free_msic;
+               return -ENODEV;
        }
 
-       res = request_mem_region(res->start, resource_size(res), pdev->name);
-       if (!res) {
-               ret = -EBUSY;
-               goto fail_free_msic;
-       }
-
-       msic->irq_base = ioremap_nocache(res->start, resource_size(res));
+       msic->irq_base = devm_request_and_ioremap(&pdev->dev, res);
        if (!msic->irq_base) {
                dev_err(&pdev->dev, "failed to map SRAM memory\n");
-               ret = -ENOMEM;
-               goto fail_release_region;
+               return -ENOMEM;
        }
 
        platform_set_drvdata(pdev, msic);
@@ -443,7 +435,7 @@ static int __devinit intel_msic_probe(struct platform_device *pdev)
        ret = intel_msic_init_devices(msic);
        if (ret) {
                dev_err(&pdev->dev, "failed to initialize MSIC devices\n");
-               goto fail_unmap_mem;
+               return ret;
        }
 
        dev_info(&pdev->dev, "Intel MSIC version %c%d (vendor %#x)\n",
@@ -451,27 +443,14 @@ static int __devinit intel_msic_probe(struct platform_device *pdev)
                 msic->vendor);
 
        return 0;
-
-fail_unmap_mem:
-       iounmap(msic->irq_base);
-fail_release_region:
-       release_mem_region(res->start, resource_size(res));
-fail_free_msic:
-       kfree(msic);
-
-       return ret;
 }
 
 static int __devexit intel_msic_remove(struct platform_device *pdev)
 {
        struct intel_msic *msic = platform_get_drvdata(pdev);
-       struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 
        intel_msic_remove_devices(msic);
        platform_set_drvdata(pdev, NULL);
-       iounmap(msic->irq_base);
-       release_mem_region(res->start, resource_size(res));
-       kfree(msic);
 
        return 0;
 }
index a9223ed1b7c5d9a152a8533c72ac1faafd3b7385..2ea99989551af85a4796c69e5fc2b65ba7a951e9 100644 (file)
@@ -283,23 +283,8 @@ static struct pci_driver cmodio_pci_driver = {
        .remove   = __devexit_p(cmodio_pci_remove),
 };
 
-/*
- * Module Init / Exit
- */
-
-static int __init cmodio_init(void)
-{
-       return pci_register_driver(&cmodio_pci_driver);
-}
-
-static void __exit cmodio_exit(void)
-{
-       pci_unregister_driver(&cmodio_pci_driver);
-}
+module_pci_driver(cmodio_pci_driver);
 
 MODULE_AUTHOR("Ira W. Snyder <iws@ovro.caltech.edu>");
 MODULE_DESCRIPTION("Janz CMOD-IO PCI MODULbus Carrier Board Driver");
 MODULE_LICENSE("GPL");
-
-module_init(cmodio_init);
-module_exit(cmodio_exit);
diff --git a/drivers/mfd/lm3533-core.c b/drivers/mfd/lm3533-core.c
new file mode 100644 (file)
index 0000000..0b2879b
--- /dev/null
@@ -0,0 +1,667 @@
+/*
+ * lm3533-core.c -- LM3533 Core
+ *
+ * Copyright (C) 2011-2012 Texas Instruments
+ *
+ * Author: Johan Hovold <jhovold@gmail.com>
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/mfd/core.h>
+#include <linux/regmap.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+
+#include <linux/mfd/lm3533.h>
+
+
+#define LM3533_BOOST_OVP_MASK          0x06
+#define LM3533_BOOST_OVP_SHIFT         1
+
+#define LM3533_BOOST_FREQ_MASK         0x01
+#define LM3533_BOOST_FREQ_SHIFT                0
+
+#define LM3533_BL_ID_MASK              1
+#define LM3533_LED_ID_MASK             3
+#define LM3533_BL_ID_MAX               1
+#define LM3533_LED_ID_MAX              3
+
+#define LM3533_HVLED_ID_MAX            2
+#define LM3533_LVLED_ID_MAX            5
+
+#define LM3533_REG_OUTPUT_CONF1                0x10
+#define LM3533_REG_OUTPUT_CONF2                0x11
+#define LM3533_REG_BOOST_PWM           0x2c
+
+#define LM3533_REG_MAX                 0xb2
+
+
+static struct mfd_cell lm3533_als_devs[] = {
+       {
+               .name   = "lm3533-als",
+               .id     = -1,
+       },
+};
+
+static struct mfd_cell lm3533_bl_devs[] = {
+       {
+               .name   = "lm3533-backlight",
+               .id     = 0,
+       },
+       {
+               .name   = "lm3533-backlight",
+               .id     = 1,
+       },
+};
+
+static struct mfd_cell lm3533_led_devs[] = {
+       {
+               .name   = "lm3533-leds",
+               .id     = 0,
+       },
+       {
+               .name   = "lm3533-leds",
+               .id     = 1,
+       },
+       {
+               .name   = "lm3533-leds",
+               .id     = 2,
+       },
+       {
+               .name   = "lm3533-leds",
+               .id     = 3,
+       },
+};
+
+int lm3533_read(struct lm3533 *lm3533, u8 reg, u8 *val)
+{
+       int tmp;
+       int ret;
+
+       ret = regmap_read(lm3533->regmap, reg, &tmp);
+       if (ret < 0) {
+               dev_err(lm3533->dev, "failed to read register %02x: %d\n",
+                                                               reg, ret);
+               return ret;
+       }
+
+       *val = tmp;
+
+       dev_dbg(lm3533->dev, "read [%02x]: %02x\n", reg, *val);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(lm3533_read);
+
+int lm3533_write(struct lm3533 *lm3533, u8 reg, u8 val)
+{
+       int ret;
+
+       dev_dbg(lm3533->dev, "write [%02x]: %02x\n", reg, val);
+
+       ret = regmap_write(lm3533->regmap, reg, val);
+       if (ret < 0) {
+               dev_err(lm3533->dev, "failed to write register %02x: %d\n",
+                                                               reg, ret);
+       }
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(lm3533_write);
+
+int lm3533_update(struct lm3533 *lm3533, u8 reg, u8 val, u8 mask)
+{
+       int ret;
+
+       dev_dbg(lm3533->dev, "update [%02x]: %02x/%02x\n", reg, val, mask);
+
+       ret = regmap_update_bits(lm3533->regmap, reg, mask, val);
+       if (ret < 0) {
+               dev_err(lm3533->dev, "failed to update register %02x: %d\n",
+                                                               reg, ret);
+       }
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(lm3533_update);
+
+static int lm3533_set_boost_freq(struct lm3533 *lm3533,
+                                               enum lm3533_boost_freq freq)
+{
+       int ret;
+
+       ret = lm3533_update(lm3533, LM3533_REG_BOOST_PWM,
+                                       freq << LM3533_BOOST_FREQ_SHIFT,
+                                       LM3533_BOOST_FREQ_MASK);
+       if (ret)
+               dev_err(lm3533->dev, "failed to set boost frequency\n");
+
+       return ret;
+}
+
+
+static int lm3533_set_boost_ovp(struct lm3533 *lm3533,
+                                               enum lm3533_boost_ovp ovp)
+{
+       int ret;
+
+       ret = lm3533_update(lm3533, LM3533_REG_BOOST_PWM,
+                                       ovp << LM3533_BOOST_OVP_SHIFT,
+                                       LM3533_BOOST_OVP_MASK);
+       if (ret)
+               dev_err(lm3533->dev, "failed to set boost ovp\n");
+
+       return ret;
+}
+
+/*
+ * HVLED output config -- output hvled controlled by backlight bl
+ */
+static int lm3533_set_hvled_config(struct lm3533 *lm3533, u8 hvled, u8 bl)
+{
+       u8 val;
+       u8 mask;
+       int shift;
+       int ret;
+
+       if (hvled == 0 || hvled > LM3533_HVLED_ID_MAX)
+               return -EINVAL;
+
+       if (bl > LM3533_BL_ID_MAX)
+               return -EINVAL;
+
+       shift = hvled - 1;
+       mask = LM3533_BL_ID_MASK << shift;
+       val = bl << shift;
+
+       ret = lm3533_update(lm3533, LM3533_REG_OUTPUT_CONF1, val, mask);
+       if (ret)
+               dev_err(lm3533->dev, "failed to set hvled config\n");
+
+       return ret;
+}
+
+/*
+ * LVLED output config -- output lvled controlled by LED led
+ */
+static int lm3533_set_lvled_config(struct lm3533 *lm3533, u8 lvled, u8 led)
+{
+       u8 reg;
+       u8 val;
+       u8 mask;
+       int shift;
+       int ret;
+
+       if (lvled == 0 || lvled > LM3533_LVLED_ID_MAX)
+               return -EINVAL;
+
+       if (led > LM3533_LED_ID_MAX)
+               return -EINVAL;
+
+       if (lvled < 4) {
+               reg = LM3533_REG_OUTPUT_CONF1;
+               shift = 2 * lvled;
+       } else {
+               reg = LM3533_REG_OUTPUT_CONF2;
+               shift = 2 * (lvled - 4);
+       }
+
+       mask = LM3533_LED_ID_MASK << shift;
+       val = led << shift;
+
+       ret = lm3533_update(lm3533, reg, val, mask);
+       if (ret)
+               dev_err(lm3533->dev, "failed to set lvled config\n");
+
+       return ret;
+}
+
+static void lm3533_enable(struct lm3533 *lm3533)
+{
+       if (gpio_is_valid(lm3533->gpio_hwen))
+               gpio_set_value(lm3533->gpio_hwen, 1);
+}
+
+static void lm3533_disable(struct lm3533 *lm3533)
+{
+       if (gpio_is_valid(lm3533->gpio_hwen))
+               gpio_set_value(lm3533->gpio_hwen, 0);
+}
+
+enum lm3533_attribute_type {
+       LM3533_ATTR_TYPE_BACKLIGHT,
+       LM3533_ATTR_TYPE_LED,
+};
+
+struct lm3533_device_attribute {
+       struct device_attribute dev_attr;
+       enum lm3533_attribute_type type;
+       union {
+               struct {
+                       u8 id;
+               } output;
+       } u;
+};
+
+#define to_lm3533_dev_attr(_attr) \
+       container_of(_attr, struct lm3533_device_attribute, dev_attr)
+
+static ssize_t show_output(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       struct lm3533 *lm3533 = dev_get_drvdata(dev);
+       struct lm3533_device_attribute *lattr = to_lm3533_dev_attr(attr);
+       int id = lattr->u.output.id;
+       u8 reg;
+       u8 val;
+       u8 mask;
+       int shift;
+       int ret;
+
+       if (lattr->type == LM3533_ATTR_TYPE_BACKLIGHT) {
+               reg = LM3533_REG_OUTPUT_CONF1;
+               shift = id - 1;
+               mask = LM3533_BL_ID_MASK << shift;
+       } else {
+               if (id < 4) {
+                       reg = LM3533_REG_OUTPUT_CONF1;
+                       shift = 2 * id;
+               } else {
+                       reg = LM3533_REG_OUTPUT_CONF2;
+                       shift = 2 * (id - 4);
+               }
+               mask = LM3533_LED_ID_MASK << shift;
+       }
+
+       ret = lm3533_read(lm3533, reg, &val);
+       if (ret)
+               return ret;
+
+       val = (val & mask) >> shift;
+
+       return scnprintf(buf, PAGE_SIZE, "%u\n", val);
+}
+
+static ssize_t store_output(struct device *dev,
+                                       struct device_attribute *attr,
+                                       const char *buf, size_t len)
+{
+       struct lm3533 *lm3533 = dev_get_drvdata(dev);
+       struct lm3533_device_attribute *lattr = to_lm3533_dev_attr(attr);
+       int id = lattr->u.output.id;
+       u8 val;
+       int ret;
+
+       if (kstrtou8(buf, 0, &val))
+               return -EINVAL;
+
+       if (lattr->type == LM3533_ATTR_TYPE_BACKLIGHT)
+               ret = lm3533_set_hvled_config(lm3533, id, val);
+       else
+               ret = lm3533_set_lvled_config(lm3533, id, val);
+
+       if (ret)
+               return ret;
+
+       return len;
+}
+
+#define LM3533_OUTPUT_ATTR(_name, _mode, _show, _store, _type, _id) \
+       struct lm3533_device_attribute lm3533_dev_attr_##_name = \
+               { .dev_attr     = __ATTR(_name, _mode, _show, _store), \
+                 .type         = _type, \
+                 .u.output     = { .id = _id }, }
+
+#define LM3533_OUTPUT_ATTR_RW(_name, _type, _id) \
+       LM3533_OUTPUT_ATTR(output_##_name, S_IRUGO | S_IWUSR, \
+                                       show_output, store_output, _type, _id)
+
+#define LM3533_OUTPUT_HVLED_ATTR_RW(_nr) \
+       LM3533_OUTPUT_ATTR_RW(hvled##_nr, LM3533_ATTR_TYPE_BACKLIGHT, _nr)
+#define LM3533_OUTPUT_LVLED_ATTR_RW(_nr) \
+       LM3533_OUTPUT_ATTR_RW(lvled##_nr, LM3533_ATTR_TYPE_LED, _nr)
+/*
+ * Output config:
+ *
+ * output_hvled<nr>    0-1
+ * output_lvled<nr>    0-3
+ */
+static LM3533_OUTPUT_HVLED_ATTR_RW(1);
+static LM3533_OUTPUT_HVLED_ATTR_RW(2);
+static LM3533_OUTPUT_LVLED_ATTR_RW(1);
+static LM3533_OUTPUT_LVLED_ATTR_RW(2);
+static LM3533_OUTPUT_LVLED_ATTR_RW(3);
+static LM3533_OUTPUT_LVLED_ATTR_RW(4);
+static LM3533_OUTPUT_LVLED_ATTR_RW(5);
+
+static struct attribute *lm3533_attributes[] = {
+       &lm3533_dev_attr_output_hvled1.dev_attr.attr,
+       &lm3533_dev_attr_output_hvled2.dev_attr.attr,
+       &lm3533_dev_attr_output_lvled1.dev_attr.attr,
+       &lm3533_dev_attr_output_lvled2.dev_attr.attr,
+       &lm3533_dev_attr_output_lvled3.dev_attr.attr,
+       &lm3533_dev_attr_output_lvled4.dev_attr.attr,
+       &lm3533_dev_attr_output_lvled5.dev_attr.attr,
+       NULL,
+};
+
+#define to_dev_attr(_attr) \
+       container_of(_attr, struct device_attribute, attr)
+
+static umode_t lm3533_attr_is_visible(struct kobject *kobj,
+                                            struct attribute *attr, int n)
+{
+       struct device *dev = container_of(kobj, struct device, kobj);
+       struct lm3533 *lm3533 = dev_get_drvdata(dev);
+       struct device_attribute *dattr = to_dev_attr(attr);
+       struct lm3533_device_attribute *lattr = to_lm3533_dev_attr(dattr);
+       enum lm3533_attribute_type type = lattr->type;
+       umode_t mode = attr->mode;
+
+       if (!lm3533->have_backlights && type == LM3533_ATTR_TYPE_BACKLIGHT)
+               mode = 0;
+       else if (!lm3533->have_leds && type == LM3533_ATTR_TYPE_LED)
+               mode = 0;
+
+       return mode;
+};
+
+static struct attribute_group lm3533_attribute_group = {
+       .is_visible     = lm3533_attr_is_visible,
+       .attrs          = lm3533_attributes
+};
+
+static int __devinit lm3533_device_als_init(struct lm3533 *lm3533)
+{
+       struct lm3533_platform_data *pdata = lm3533->dev->platform_data;
+       int ret;
+
+       if (!pdata->als)
+               return 0;
+
+       lm3533_als_devs[0].platform_data = pdata->als;
+       lm3533_als_devs[0].pdata_size = sizeof(*pdata->als);
+
+       ret = mfd_add_devices(lm3533->dev, 0, lm3533_als_devs, 1, NULL, 0);
+       if (ret) {
+               dev_err(lm3533->dev, "failed to add ALS device\n");
+               return ret;
+       }
+
+       lm3533->have_als = 1;
+
+       return 0;
+}
+
+static int __devinit lm3533_device_bl_init(struct lm3533 *lm3533)
+{
+       struct lm3533_platform_data *pdata = lm3533->dev->platform_data;
+       int i;
+       int ret;
+
+       if (!pdata->backlights || pdata->num_backlights == 0)
+               return 0;
+
+       if (pdata->num_backlights > ARRAY_SIZE(lm3533_bl_devs))
+               pdata->num_backlights = ARRAY_SIZE(lm3533_bl_devs);
+
+       for (i = 0; i < pdata->num_backlights; ++i) {
+               lm3533_bl_devs[i].platform_data = &pdata->backlights[i];
+               lm3533_bl_devs[i].pdata_size = sizeof(pdata->backlights[i]);
+       }
+
+       ret = mfd_add_devices(lm3533->dev, 0, lm3533_bl_devs,
+                                       pdata->num_backlights, NULL, 0);
+       if (ret) {
+               dev_err(lm3533->dev, "failed to add backlight devices\n");
+               return ret;
+       }
+
+       lm3533->have_backlights = 1;
+
+       return 0;
+}
+
+static int __devinit lm3533_device_led_init(struct lm3533 *lm3533)
+{
+       struct lm3533_platform_data *pdata = lm3533->dev->platform_data;
+       int i;
+       int ret;
+
+       if (!pdata->leds || pdata->num_leds == 0)
+               return 0;
+
+       if (pdata->num_leds > ARRAY_SIZE(lm3533_led_devs))
+               pdata->num_leds = ARRAY_SIZE(lm3533_led_devs);
+
+       for (i = 0; i < pdata->num_leds; ++i) {
+               lm3533_led_devs[i].platform_data = &pdata->leds[i];
+               lm3533_led_devs[i].pdata_size = sizeof(pdata->leds[i]);
+       }
+
+       ret = mfd_add_devices(lm3533->dev, 0, lm3533_led_devs,
+                                               pdata->num_leds, NULL, 0);
+       if (ret) {
+               dev_err(lm3533->dev, "failed to add LED devices\n");
+               return ret;
+       }
+
+       lm3533->have_leds = 1;
+
+       return 0;
+}
+
+static int __devinit lm3533_device_setup(struct lm3533 *lm3533,
+                                       struct lm3533_platform_data *pdata)
+{
+       int ret;
+
+       ret = lm3533_set_boost_freq(lm3533, pdata->boost_freq);
+       if (ret)
+               return ret;
+
+       ret = lm3533_set_boost_ovp(lm3533, pdata->boost_ovp);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static int __devinit lm3533_device_init(struct lm3533 *lm3533)
+{
+       struct lm3533_platform_data *pdata = lm3533->dev->platform_data;
+       int ret;
+
+       dev_dbg(lm3533->dev, "%s\n", __func__);
+
+       if (!pdata) {
+               dev_err(lm3533->dev, "no platform data\n");
+               return -EINVAL;
+       }
+
+       lm3533->gpio_hwen = pdata->gpio_hwen;
+
+       dev_set_drvdata(lm3533->dev, lm3533);
+
+       if (gpio_is_valid(lm3533->gpio_hwen)) {
+               ret = gpio_request_one(lm3533->gpio_hwen, GPIOF_OUT_INIT_LOW,
+                                                               "lm3533-hwen");
+               if (ret < 0) {
+                       dev_err(lm3533->dev,
+                               "failed to request HWEN GPIO %d\n",
+                               lm3533->gpio_hwen);
+                       return ret;
+               }
+       }
+
+       lm3533_enable(lm3533);
+
+       ret = lm3533_device_setup(lm3533, pdata);
+       if (ret)
+               goto err_disable;
+
+       lm3533_device_als_init(lm3533);
+       lm3533_device_bl_init(lm3533);
+       lm3533_device_led_init(lm3533);
+
+       ret = sysfs_create_group(&lm3533->dev->kobj, &lm3533_attribute_group);
+       if (ret < 0) {
+               dev_err(lm3533->dev, "failed to create sysfs attributes\n");
+               goto err_unregister;
+       }
+
+       return 0;
+
+err_unregister:
+       mfd_remove_devices(lm3533->dev);
+err_disable:
+       lm3533_disable(lm3533);
+       if (gpio_is_valid(lm3533->gpio_hwen))
+               gpio_free(lm3533->gpio_hwen);
+
+       return ret;
+}
+
+static void __devexit lm3533_device_exit(struct lm3533 *lm3533)
+{
+       dev_dbg(lm3533->dev, "%s\n", __func__);
+
+       sysfs_remove_group(&lm3533->dev->kobj, &lm3533_attribute_group);
+
+       mfd_remove_devices(lm3533->dev);
+       lm3533_disable(lm3533);
+       if (gpio_is_valid(lm3533->gpio_hwen))
+               gpio_free(lm3533->gpio_hwen);
+}
+
+static bool lm3533_readable_register(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case 0x10 ... 0x2c:
+       case 0x30 ... 0x38:
+       case 0x40 ... 0x45:
+       case 0x50 ... 0x57:
+       case 0x60 ... 0x6e:
+       case 0x70 ... 0x75:
+       case 0x80 ... 0x85:
+       case 0x90 ... 0x95:
+       case 0xa0 ... 0xa5:
+       case 0xb0 ... 0xb2:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static bool lm3533_volatile_register(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case 0x34 ... 0x36:     /* zone */
+       case 0x37 ... 0x38:     /* adc */
+       case 0xb0 ... 0xb1:     /* fault */
+               return true;
+       default:
+               return false;
+       }
+}
+
+static bool lm3533_precious_register(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case 0x34:              /* zone */
+               return true;
+       default:
+               return false;
+       }
+}
+
+static struct regmap_config regmap_config = {
+       .reg_bits       = 8,
+       .val_bits       = 8,
+       .max_register   = LM3533_REG_MAX,
+       .readable_reg   = lm3533_readable_register,
+       .volatile_reg   = lm3533_volatile_register,
+       .precious_reg   = lm3533_precious_register,
+};
+
+static int __devinit lm3533_i2c_probe(struct i2c_client *i2c,
+                                       const struct i2c_device_id *id)
+{
+       struct lm3533 *lm3533;
+       int ret;
+
+       dev_dbg(&i2c->dev, "%s\n", __func__);
+
+       lm3533 = devm_kzalloc(&i2c->dev, sizeof(*lm3533), GFP_KERNEL);
+       if (!lm3533)
+               return -ENOMEM;
+
+       i2c_set_clientdata(i2c, lm3533);
+
+       lm3533->regmap = devm_regmap_init_i2c(i2c, &regmap_config);
+       if (IS_ERR(lm3533->regmap))
+               return PTR_ERR(lm3533->regmap);
+
+       lm3533->dev = &i2c->dev;
+       lm3533->irq = i2c->irq;
+
+       ret = lm3533_device_init(lm3533);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static int __devexit lm3533_i2c_remove(struct i2c_client *i2c)
+{
+       struct lm3533 *lm3533 = i2c_get_clientdata(i2c);
+
+       dev_dbg(&i2c->dev, "%s\n", __func__);
+
+       lm3533_device_exit(lm3533);
+
+       return 0;
+}
+
+static const struct i2c_device_id lm3533_i2c_ids[] = {
+       { "lm3533", 0 },
+       { },
+};
+MODULE_DEVICE_TABLE(i2c, lm3533_i2c_ids);
+
+static struct i2c_driver lm3533_i2c_driver = {
+       .driver = {
+                  .name = "lm3533",
+                  .owner = THIS_MODULE,
+       },
+       .id_table       = lm3533_i2c_ids,
+       .probe          = lm3533_i2c_probe,
+       .remove         = __devexit_p(lm3533_i2c_remove),
+};
+
+static int __init lm3533_i2c_init(void)
+{
+       return i2c_add_driver(&lm3533_i2c_driver);
+}
+subsys_initcall(lm3533_i2c_init);
+
+static void __exit lm3533_i2c_exit(void)
+{
+       i2c_del_driver(&lm3533_i2c_driver);
+}
+module_exit(lm3533_i2c_exit);
+
+MODULE_AUTHOR("Johan Hovold <jhovold@gmail.com>");
+MODULE_DESCRIPTION("LM3533 Core");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/lm3533-ctrlbank.c b/drivers/mfd/lm3533-ctrlbank.c
new file mode 100644 (file)
index 0000000..a4cb7a5
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * lm3533-ctrlbank.c -- LM3533 Generic Control Bank interface
+ *
+ * Copyright (C) 2011-2012 Texas Instruments
+ *
+ * Author: Johan Hovold <jhovold@gmail.com>
+ *
+ * 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.
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+
+#include <linux/mfd/lm3533.h>
+
+
+#define LM3533_MAX_CURRENT_MIN         5000
+#define LM3533_MAX_CURRENT_MAX         29800
+#define LM3533_MAX_CURRENT_STEP                800
+
+#define LM3533_BRIGHTNESS_MAX          255
+#define LM3533_PWM_MAX                 0x3f
+
+#define LM3533_REG_PWM_BASE            0x14
+#define LM3533_REG_MAX_CURRENT_BASE    0x1f
+#define LM3533_REG_CTRLBANK_ENABLE     0x27
+#define LM3533_REG_BRIGHTNESS_BASE     0x40
+
+
+static inline u8 lm3533_ctrlbank_get_reg(struct lm3533_ctrlbank *cb, u8 base)
+{
+       return base + cb->id;
+}
+
+int lm3533_ctrlbank_enable(struct lm3533_ctrlbank *cb)
+{
+       u8 mask;
+       int ret;
+
+       dev_dbg(cb->dev, "%s - %d\n", __func__, cb->id);
+
+       mask = 1 << cb->id;
+       ret = lm3533_update(cb->lm3533, LM3533_REG_CTRLBANK_ENABLE,
+                                                               mask, mask);
+       if (ret)
+               dev_err(cb->dev, "failed to enable ctrlbank %d\n", cb->id);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(lm3533_ctrlbank_enable);
+
+int lm3533_ctrlbank_disable(struct lm3533_ctrlbank *cb)
+{
+       u8 mask;
+       int ret;
+
+       dev_dbg(cb->dev, "%s - %d\n", __func__, cb->id);
+
+       mask = 1 << cb->id;
+       ret = lm3533_update(cb->lm3533, LM3533_REG_CTRLBANK_ENABLE, 0, mask);
+       if (ret)
+               dev_err(cb->dev, "failed to disable ctrlbank %d\n", cb->id);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(lm3533_ctrlbank_disable);
+
+/*
+ * Full-scale current.
+ *
+ * imax                5000 - 29800 uA (800 uA step)
+ */
+int lm3533_ctrlbank_set_max_current(struct lm3533_ctrlbank *cb, u16 imax)
+{
+       u8 reg;
+       u8 val;
+       int ret;
+
+       if (imax < LM3533_MAX_CURRENT_MIN || imax > LM3533_MAX_CURRENT_MAX)
+               return -EINVAL;
+
+       val = (imax - LM3533_MAX_CURRENT_MIN) / LM3533_MAX_CURRENT_STEP;
+
+       reg = lm3533_ctrlbank_get_reg(cb, LM3533_REG_MAX_CURRENT_BASE);
+       ret = lm3533_write(cb->lm3533, reg, val);
+       if (ret)
+               dev_err(cb->dev, "failed to set max current\n");
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(lm3533_ctrlbank_set_max_current);
+
+#define lm3533_ctrlbank_set(_name, _NAME)                              \
+int lm3533_ctrlbank_set_##_name(struct lm3533_ctrlbank *cb, u8 val)    \
+{                                                                      \
+       u8 reg;                                                         \
+       int ret;                                                        \
+                                                                       \
+       if (val > LM3533_##_NAME##_MAX)                                 \
+               return -EINVAL;                                         \
+                                                                       \
+       reg = lm3533_ctrlbank_get_reg(cb, LM3533_REG_##_NAME##_BASE);   \
+       ret = lm3533_write(cb->lm3533, reg, val);                       \
+       if (ret)                                                        \
+               dev_err(cb->dev, "failed to set " #_name "\n");         \
+                                                                       \
+       return ret;                                                     \
+}                                                                      \
+EXPORT_SYMBOL_GPL(lm3533_ctrlbank_set_##_name);
+
+#define lm3533_ctrlbank_get(_name, _NAME)                              \
+int lm3533_ctrlbank_get_##_name(struct lm3533_ctrlbank *cb, u8 *val)   \
+{                                                                      \
+       u8 reg;                                                         \
+       int ret;                                                        \
+                                                                       \
+       reg = lm3533_ctrlbank_get_reg(cb, LM3533_REG_##_NAME##_BASE);   \
+       ret = lm3533_read(cb->lm3533, reg, val);                        \
+       if (ret)                                                        \
+               dev_err(cb->dev, "failed to get " #_name "\n");         \
+                                                                       \
+       return ret;                                                     \
+}                                                                      \
+EXPORT_SYMBOL_GPL(lm3533_ctrlbank_get_##_name);
+
+lm3533_ctrlbank_set(brightness, BRIGHTNESS);
+lm3533_ctrlbank_get(brightness, BRIGHTNESS);
+
+/*
+ * PWM-input control mask:
+ *
+ *   bit 5 - PWM-input enabled in Zone 4
+ *   bit 4 - PWM-input enabled in Zone 3
+ *   bit 3 - PWM-input enabled in Zone 2
+ *   bit 2 - PWM-input enabled in Zone 1
+ *   bit 1 - PWM-input enabled in Zone 0
+ *   bit 0 - PWM-input enabled
+ */
+lm3533_ctrlbank_set(pwm, PWM);
+lm3533_ctrlbank_get(pwm, PWM);
+
+
+MODULE_AUTHOR("Johan Hovold <jhovold@gmail.com>");
+MODULE_DESCRIPTION("LM3533 Control Bank interface");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/lpc_ich.c b/drivers/mfd/lpc_ich.c
new file mode 100644 (file)
index 0000000..027cc8f
--- /dev/null
@@ -0,0 +1,888 @@
+/*
+ *  lpc_ich.c - LPC interface for Intel ICH
+ *
+ *  LPC bridge function of the Intel ICH contains many other
+ *  functional units, such as Interrupt controllers, Timers,
+ *  Power Management, System Management, GPIO, RTC, and LPC
+ *  Configuration Registers.
+ *
+ *  This driver is derived from lpc_sch.
+
+ *  Copyright (c) 2011 Extreme Engineering Solution, Inc.
+ *  Author: Aaron Sierra <asierra@xes-inc.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License 2 as published
+ *  by the Free Software Foundation.
+ *
+ *  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; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  This driver supports the following I/O Controller hubs:
+ *     (See the intel documentation on http://developer.intel.com.)
+ *     document number 290655-003, 290677-014: 82801AA (ICH), 82801AB (ICHO)
+ *     document number 290687-002, 298242-027: 82801BA (ICH2)
+ *     document number 290733-003, 290739-013: 82801CA (ICH3-S)
+ *     document number 290716-001, 290718-007: 82801CAM (ICH3-M)
+ *     document number 290744-001, 290745-025: 82801DB (ICH4)
+ *     document number 252337-001, 252663-008: 82801DBM (ICH4-M)
+ *     document number 273599-001, 273645-002: 82801E (C-ICH)
+ *     document number 252516-001, 252517-028: 82801EB (ICH5), 82801ER (ICH5R)
+ *     document number 300641-004, 300884-013: 6300ESB
+ *     document number 301473-002, 301474-026: 82801F (ICH6)
+ *     document number 313082-001, 313075-006: 631xESB, 632xESB
+ *     document number 307013-003, 307014-024: 82801G (ICH7)
+ *     document number 322896-001, 322897-001: NM10
+ *     document number 313056-003, 313057-017: 82801H (ICH8)
+ *     document number 316972-004, 316973-012: 82801I (ICH9)
+ *     document number 319973-002, 319974-002: 82801J (ICH10)
+ *     document number 322169-001, 322170-003: 5 Series, 3400 Series (PCH)
+ *     document number 320066-003, 320257-008: EP80597 (IICH)
+ *     document number 324645-001, 324646-001: Cougar Point (CPT)
+ *     document number TBD : Patsburg (PBG)
+ *     document number TBD : DH89xxCC
+ *     document number TBD : Panther Point
+ *     document number TBD : Lynx Point
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/acpi.h>
+#include <linux/pci.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/lpc_ich.h>
+
+#define ACPIBASE               0x40
+#define ACPIBASE_GPE_OFF       0x28
+#define ACPIBASE_GPE_END       0x2f
+#define ACPIBASE_SMI_OFF       0x30
+#define ACPIBASE_SMI_END       0x33
+#define ACPIBASE_TCO_OFF       0x60
+#define ACPIBASE_TCO_END       0x7f
+#define ACPICTRL               0x44
+
+#define ACPIBASE_GCS_OFF       0x3410
+#define ACPIBASE_GCS_END       0x3414
+
+#define GPIOBASE               0x48
+#define GPIOCTRL               0x4C
+
+#define RCBABASE               0xf0
+
+#define wdt_io_res(i) wdt_res(0, i)
+#define wdt_mem_res(i) wdt_res(ICH_RES_MEM_OFF, i)
+#define wdt_res(b, i) (&wdt_ich_res[(b) + (i)])
+
+static int lpc_ich_acpi_save = -1;
+static int lpc_ich_gpio_save = -1;
+
+static struct resource wdt_ich_res[] = {
+       /* ACPI - TCO */
+       {
+               .flags = IORESOURCE_IO,
+       },
+       /* ACPI - SMI */
+       {
+               .flags = IORESOURCE_IO,
+       },
+       /* GCS */
+       {
+               .flags = IORESOURCE_MEM,
+       },
+};
+
+static struct resource gpio_ich_res[] = {
+       /* GPIO */
+       {
+               .flags = IORESOURCE_IO,
+       },
+       /* ACPI - GPE0 */
+       {
+               .flags = IORESOURCE_IO,
+       },
+};
+
+enum lpc_cells {
+       LPC_WDT = 0,
+       LPC_GPIO,
+};
+
+static struct mfd_cell lpc_ich_cells[] = {
+       [LPC_WDT] = {
+               .name = "iTCO_wdt",
+               .num_resources = ARRAY_SIZE(wdt_ich_res),
+               .resources = wdt_ich_res,
+               .ignore_resource_conflicts = true,
+       },
+       [LPC_GPIO] = {
+               .name = "gpio_ich",
+               .num_resources = ARRAY_SIZE(gpio_ich_res),
+               .resources = gpio_ich_res,
+               .ignore_resource_conflicts = true,
+       },
+};
+
+/* chipset related info */
+enum lpc_chipsets {
+       LPC_ICH = 0,    /* ICH */
+       LPC_ICH0,       /* ICH0 */
+       LPC_ICH2,       /* ICH2 */
+       LPC_ICH2M,      /* ICH2-M */
+       LPC_ICH3,       /* ICH3-S */
+       LPC_ICH3M,      /* ICH3-M */
+       LPC_ICH4,       /* ICH4 */
+       LPC_ICH4M,      /* ICH4-M */
+       LPC_CICH,       /* C-ICH */
+       LPC_ICH5,       /* ICH5 & ICH5R */
+       LPC_6300ESB,    /* 6300ESB */
+       LPC_ICH6,       /* ICH6 & ICH6R */
+       LPC_ICH6M,      /* ICH6-M */
+       LPC_ICH6W,      /* ICH6W & ICH6RW */
+       LPC_631XESB,    /* 631xESB/632xESB */
+       LPC_ICH7,       /* ICH7 & ICH7R */
+       LPC_ICH7DH,     /* ICH7DH */
+       LPC_ICH7M,      /* ICH7-M & ICH7-U */
+       LPC_ICH7MDH,    /* ICH7-M DH */
+       LPC_NM10,       /* NM10 */
+       LPC_ICH8,       /* ICH8 & ICH8R */
+       LPC_ICH8DH,     /* ICH8DH */
+       LPC_ICH8DO,     /* ICH8DO */
+       LPC_ICH8M,      /* ICH8M */
+       LPC_ICH8ME,     /* ICH8M-E */
+       LPC_ICH9,       /* ICH9 */
+       LPC_ICH9R,      /* ICH9R */
+       LPC_ICH9DH,     /* ICH9DH */
+       LPC_ICH9DO,     /* ICH9DO */
+       LPC_ICH9M,      /* ICH9M */
+       LPC_ICH9ME,     /* ICH9M-E */
+       LPC_ICH10,      /* ICH10 */
+       LPC_ICH10R,     /* ICH10R */
+       LPC_ICH10D,     /* ICH10D */
+       LPC_ICH10DO,    /* ICH10DO */
+       LPC_PCH,        /* PCH Desktop Full Featured */
+       LPC_PCHM,       /* PCH Mobile Full Featured */
+       LPC_P55,        /* P55 */
+       LPC_PM55,       /* PM55 */
+       LPC_H55,        /* H55 */
+       LPC_QM57,       /* QM57 */
+       LPC_H57,        /* H57 */
+       LPC_HM55,       /* HM55 */
+       LPC_Q57,        /* Q57 */
+       LPC_HM57,       /* HM57 */
+       LPC_PCHMSFF,    /* PCH Mobile SFF Full Featured */
+       LPC_QS57,       /* QS57 */
+       LPC_3400,       /* 3400 */
+       LPC_3420,       /* 3420 */
+       LPC_3450,       /* 3450 */
+       LPC_EP80579,    /* EP80579 */
+       LPC_CPT,        /* Cougar Point */
+       LPC_CPTD,       /* Cougar Point Desktop */
+       LPC_CPTM,       /* Cougar Point Mobile */
+       LPC_PBG,        /* Patsburg */
+       LPC_DH89XXCC,   /* DH89xxCC */
+       LPC_PPT,        /* Panther Point */
+       LPC_LPT,        /* Lynx Point */
+};
+
+struct lpc_ich_info lpc_chipset_info[] __devinitdata = {
+       [LPC_ICH] = {
+               .name = "ICH",
+               .iTCO_version = 1,
+       },
+       [LPC_ICH0] = {
+               .name = "ICH0",
+               .iTCO_version = 1,
+       },
+       [LPC_ICH2] = {
+               .name = "ICH2",
+               .iTCO_version = 1,
+       },
+       [LPC_ICH2M] = {
+               .name = "ICH2-M",
+               .iTCO_version = 1,
+       },
+       [LPC_ICH3] = {
+               .name = "ICH3-S",
+               .iTCO_version = 1,
+       },
+       [LPC_ICH3M] = {
+               .name = "ICH3-M",
+               .iTCO_version = 1,
+       },
+       [LPC_ICH4] = {
+               .name = "ICH4",
+               .iTCO_version = 1,
+       },
+       [LPC_ICH4M] = {
+               .name = "ICH4-M",
+               .iTCO_version = 1,
+       },
+       [LPC_CICH] = {
+               .name = "C-ICH",
+               .iTCO_version = 1,
+       },
+       [LPC_ICH5] = {
+               .name = "ICH5 or ICH5R",
+               .iTCO_version = 1,
+       },
+       [LPC_6300ESB] = {
+               .name = "6300ESB",
+               .iTCO_version = 1,
+       },
+       [LPC_ICH6] = {
+               .name = "ICH6 or ICH6R",
+               .iTCO_version = 2,
+               .gpio_version = ICH_V6_GPIO,
+       },
+       [LPC_ICH6M] = {
+               .name = "ICH6-M",
+               .iTCO_version = 2,
+               .gpio_version = ICH_V6_GPIO,
+       },
+       [LPC_ICH6W] = {
+               .name = "ICH6W or ICH6RW",
+               .iTCO_version = 2,
+               .gpio_version = ICH_V6_GPIO,
+       },
+       [LPC_631XESB] = {
+               .name = "631xESB/632xESB",
+               .iTCO_version = 2,
+               .gpio_version = ICH_V6_GPIO,
+       },
+       [LPC_ICH7] = {
+               .name = "ICH7 or ICH7R",
+               .iTCO_version = 2,
+               .gpio_version = ICH_V7_GPIO,
+       },
+       [LPC_ICH7DH] = {
+               .name = "ICH7DH",
+               .iTCO_version = 2,
+               .gpio_version = ICH_V7_GPIO,
+       },
+       [LPC_ICH7M] = {
+               .name = "ICH7-M or ICH7-U",
+               .iTCO_version = 2,
+               .gpio_version = ICH_V7_GPIO,
+       },
+       [LPC_ICH7MDH] = {
+               .name = "ICH7-M DH",
+               .iTCO_version = 2,
+               .gpio_version = ICH_V7_GPIO,
+       },
+       [LPC_NM10] = {
+               .name = "NM10",
+               .iTCO_version = 2,
+       },
+       [LPC_ICH8] = {
+               .name = "ICH8 or ICH8R",
+               .iTCO_version = 2,
+               .gpio_version = ICH_V7_GPIO,
+       },
+       [LPC_ICH8DH] = {
+               .name = "ICH8DH",
+               .iTCO_version = 2,
+               .gpio_version = ICH_V7_GPIO,
+       },
+       [LPC_ICH8DO] = {
+               .name = "ICH8DO",
+               .iTCO_version = 2,
+               .gpio_version = ICH_V7_GPIO,
+       },
+       [LPC_ICH8M] = {
+               .name = "ICH8M",
+               .iTCO_version = 2,
+               .gpio_version = ICH_V7_GPIO,
+       },
+       [LPC_ICH8ME] = {
+               .name = "ICH8M-E",
+               .iTCO_version = 2,
+               .gpio_version = ICH_V7_GPIO,
+       },
+       [LPC_ICH9] = {
+               .name = "ICH9",
+               .iTCO_version = 2,
+               .gpio_version = ICH_V9_GPIO,
+       },
+       [LPC_ICH9R] = {
+               .name = "ICH9R",
+               .iTCO_version = 2,
+               .gpio_version = ICH_V9_GPIO,
+       },
+       [LPC_ICH9DH] = {
+               .name = "ICH9DH",
+               .iTCO_version = 2,
+               .gpio_version = ICH_V9_GPIO,
+       },
+       [LPC_ICH9DO] = {
+               .name = "ICH9DO",
+               .iTCO_version = 2,
+               .gpio_version = ICH_V9_GPIO,
+       },
+       [LPC_ICH9M] = {
+               .name = "ICH9M",
+               .iTCO_version = 2,
+               .gpio_version = ICH_V9_GPIO,
+       },
+       [LPC_ICH9ME] = {
+               .name = "ICH9M-E",
+               .iTCO_version = 2,
+               .gpio_version = ICH_V9_GPIO,
+       },
+       [LPC_ICH10] = {
+               .name = "ICH10",
+               .iTCO_version = 2,
+               .gpio_version = ICH_V10CONS_GPIO,
+       },
+       [LPC_ICH10R] = {
+               .name = "ICH10R",
+               .iTCO_version = 2,
+               .gpio_version = ICH_V10CONS_GPIO,
+       },
+       [LPC_ICH10D] = {
+               .name = "ICH10D",
+               .iTCO_version = 2,
+               .gpio_version = ICH_V10CORP_GPIO,
+       },
+       [LPC_ICH10DO] = {
+               .name = "ICH10DO",
+               .iTCO_version = 2,
+               .gpio_version = ICH_V10CORP_GPIO,
+       },
+       [LPC_PCH] = {
+               .name = "PCH Desktop Full Featured",
+               .iTCO_version = 2,
+               .gpio_version = ICH_V5_GPIO,
+       },
+       [LPC_PCHM] = {
+               .name = "PCH Mobile Full Featured",
+               .iTCO_version = 2,
+               .gpio_version = ICH_V5_GPIO,
+       },
+       [LPC_P55] = {
+               .name = "P55",
+               .iTCO_version = 2,
+               .gpio_version = ICH_V5_GPIO,
+       },
+       [LPC_PM55] = {
+               .name = "PM55",
+               .iTCO_version = 2,
+               .gpio_version = ICH_V5_GPIO,
+       },
+       [LPC_H55] = {
+               .name = "H55",
+               .iTCO_version = 2,
+               .gpio_version = ICH_V5_GPIO,
+       },
+       [LPC_QM57] = {
+               .name = "QM57",
+               .iTCO_version = 2,
+               .gpio_version = ICH_V5_GPIO,
+       },
+       [LPC_H57] = {
+               .name = "H57",
+               .iTCO_version = 2,
+               .gpio_version = ICH_V5_GPIO,
+       },
+       [LPC_HM55] = {
+               .name = "HM55",
+               .iTCO_version = 2,
+               .gpio_version = ICH_V5_GPIO,
+       },
+       [LPC_Q57] = {
+               .name = "Q57",
+               .iTCO_version = 2,
+               .gpio_version = ICH_V5_GPIO,
+       },
+       [LPC_HM57] = {
+               .name = "HM57",
+               .iTCO_version = 2,
+               .gpio_version = ICH_V5_GPIO,
+       },
+       [LPC_PCHMSFF] = {
+               .name = "PCH Mobile SFF Full Featured",
+               .iTCO_version = 2,
+               .gpio_version = ICH_V5_GPIO,
+       },
+       [LPC_QS57] = {
+               .name = "QS57",
+               .iTCO_version = 2,
+               .gpio_version = ICH_V5_GPIO,
+       },
+       [LPC_3400] = {
+               .name = "3400",
+               .iTCO_version = 2,
+               .gpio_version = ICH_V5_GPIO,
+       },
+       [LPC_3420] = {
+               .name = "3420",
+               .iTCO_version = 2,
+               .gpio_version = ICH_V5_GPIO,
+       },
+       [LPC_3450] = {
+               .name = "3450",
+               .iTCO_version = 2,
+               .gpio_version = ICH_V5_GPIO,
+       },
+       [LPC_EP80579] = {
+               .name = "EP80579",
+               .iTCO_version = 2,
+       },
+       [LPC_CPT] = {
+               .name = "Cougar Point",
+               .iTCO_version = 2,
+               .gpio_version = ICH_V5_GPIO,
+       },
+       [LPC_CPTD] = {
+               .name = "Cougar Point Desktop",
+               .iTCO_version = 2,
+               .gpio_version = ICH_V5_GPIO,
+       },
+       [LPC_CPTM] = {
+               .name = "Cougar Point Mobile",
+               .iTCO_version = 2,
+               .gpio_version = ICH_V5_GPIO,
+       },
+       [LPC_PBG] = {
+               .name = "Patsburg",
+               .iTCO_version = 2,
+       },
+       [LPC_DH89XXCC] = {
+               .name = "DH89xxCC",
+               .iTCO_version = 2,
+       },
+       [LPC_PPT] = {
+               .name = "Panther Point",
+               .iTCO_version = 2,
+       },
+       [LPC_LPT] = {
+               .name = "Lynx Point",
+               .iTCO_version = 2,
+       },
+};
+
+/*
+ * This data only exists for exporting the supported PCI ids
+ * via MODULE_DEVICE_TABLE.  We do not actually register a
+ * pci_driver, because the I/O Controller Hub has also other
+ * functions that probably will be registered by other drivers.
+ */
+static DEFINE_PCI_DEVICE_TABLE(lpc_ich_ids) = {
+       { PCI_VDEVICE(INTEL, 0x2410), LPC_ICH},
+       { PCI_VDEVICE(INTEL, 0x2420), LPC_ICH0},
+       { PCI_VDEVICE(INTEL, 0x2440), LPC_ICH2},
+       { PCI_VDEVICE(INTEL, 0x244c), LPC_ICH2M},
+       { PCI_VDEVICE(INTEL, 0x2480), LPC_ICH3},
+       { PCI_VDEVICE(INTEL, 0x248c), LPC_ICH3M},
+       { PCI_VDEVICE(INTEL, 0x24c0), LPC_ICH4},
+       { PCI_VDEVICE(INTEL, 0x24cc), LPC_ICH4M},
+       { PCI_VDEVICE(INTEL, 0x2450), LPC_CICH},
+       { PCI_VDEVICE(INTEL, 0x24d0), LPC_ICH5},
+       { PCI_VDEVICE(INTEL, 0x25a1), LPC_6300ESB},
+       { PCI_VDEVICE(INTEL, 0x2640), LPC_ICH6},
+       { PCI_VDEVICE(INTEL, 0x2641), LPC_ICH6M},
+       { PCI_VDEVICE(INTEL, 0x2642), LPC_ICH6W},
+       { PCI_VDEVICE(INTEL, 0x2670), LPC_631XESB},
+       { PCI_VDEVICE(INTEL, 0x2671), LPC_631XESB},
+       { PCI_VDEVICE(INTEL, 0x2672), LPC_631XESB},
+       { PCI_VDEVICE(INTEL, 0x2673), LPC_631XESB},
+       { PCI_VDEVICE(INTEL, 0x2674), LPC_631XESB},
+       { PCI_VDEVICE(INTEL, 0x2675), LPC_631XESB},
+       { PCI_VDEVICE(INTEL, 0x2676), LPC_631XESB},
+       { PCI_VDEVICE(INTEL, 0x2677), LPC_631XESB},
+       { PCI_VDEVICE(INTEL, 0x2678), LPC_631XESB},
+       { PCI_VDEVICE(INTEL, 0x2679), LPC_631XESB},
+       { PCI_VDEVICE(INTEL, 0x267a), LPC_631XESB},
+       { PCI_VDEVICE(INTEL, 0x267b), LPC_631XESB},
+       { PCI_VDEVICE(INTEL, 0x267c), LPC_631XESB},
+       { PCI_VDEVICE(INTEL, 0x267d), LPC_631XESB},
+       { PCI_VDEVICE(INTEL, 0x267e), LPC_631XESB},
+       { PCI_VDEVICE(INTEL, 0x267f), LPC_631XESB},
+       { PCI_VDEVICE(INTEL, 0x27b8), LPC_ICH7},
+       { PCI_VDEVICE(INTEL, 0x27b0), LPC_ICH7DH},
+       { PCI_VDEVICE(INTEL, 0x27b9), LPC_ICH7M},
+       { PCI_VDEVICE(INTEL, 0x27bd), LPC_ICH7MDH},
+       { PCI_VDEVICE(INTEL, 0x27bc), LPC_NM10},
+       { PCI_VDEVICE(INTEL, 0x2810), LPC_ICH8},
+       { PCI_VDEVICE(INTEL, 0x2812), LPC_ICH8DH},
+       { PCI_VDEVICE(INTEL, 0x2814), LPC_ICH8DO},
+       { PCI_VDEVICE(INTEL, 0x2815), LPC_ICH8M},
+       { PCI_VDEVICE(INTEL, 0x2811), LPC_ICH8ME},
+       { PCI_VDEVICE(INTEL, 0x2918), LPC_ICH9},
+       { PCI_VDEVICE(INTEL, 0x2916), LPC_ICH9R},
+       { PCI_VDEVICE(INTEL, 0x2912), LPC_ICH9DH},
+       { PCI_VDEVICE(INTEL, 0x2914), LPC_ICH9DO},
+       { PCI_VDEVICE(INTEL, 0x2919), LPC_ICH9M},
+       { PCI_VDEVICE(INTEL, 0x2917), LPC_ICH9ME},
+       { PCI_VDEVICE(INTEL, 0x3a18), LPC_ICH10},
+       { PCI_VDEVICE(INTEL, 0x3a16), LPC_ICH10R},
+       { PCI_VDEVICE(INTEL, 0x3a1a), LPC_ICH10D},
+       { PCI_VDEVICE(INTEL, 0x3a14), LPC_ICH10DO},
+       { PCI_VDEVICE(INTEL, 0x3b00), LPC_PCH},
+       { PCI_VDEVICE(INTEL, 0x3b01), LPC_PCHM},
+       { PCI_VDEVICE(INTEL, 0x3b02), LPC_P55},
+       { PCI_VDEVICE(INTEL, 0x3b03), LPC_PM55},
+       { PCI_VDEVICE(INTEL, 0x3b06), LPC_H55},
+       { PCI_VDEVICE(INTEL, 0x3b07), LPC_QM57},
+       { PCI_VDEVICE(INTEL, 0x3b08), LPC_H57},
+       { PCI_VDEVICE(INTEL, 0x3b09), LPC_HM55},
+       { PCI_VDEVICE(INTEL, 0x3b0a), LPC_Q57},
+       { PCI_VDEVICE(INTEL, 0x3b0b), LPC_HM57},
+       { PCI_VDEVICE(INTEL, 0x3b0d), LPC_PCHMSFF},
+       { PCI_VDEVICE(INTEL, 0x3b0f), LPC_QS57},
+       { PCI_VDEVICE(INTEL, 0x3b12), LPC_3400},
+       { PCI_VDEVICE(INTEL, 0x3b14), LPC_3420},
+       { PCI_VDEVICE(INTEL, 0x3b16), LPC_3450},
+       { PCI_VDEVICE(INTEL, 0x5031), LPC_EP80579},
+       { PCI_VDEVICE(INTEL, 0x1c41), LPC_CPT},
+       { PCI_VDEVICE(INTEL, 0x1c42), LPC_CPTD},
+       { PCI_VDEVICE(INTEL, 0x1c43), LPC_CPTM},
+       { PCI_VDEVICE(INTEL, 0x1c44), LPC_CPT},
+       { PCI_VDEVICE(INTEL, 0x1c45), LPC_CPT},
+       { PCI_VDEVICE(INTEL, 0x1c46), LPC_CPT},
+       { PCI_VDEVICE(INTEL, 0x1c47), LPC_CPT},
+       { PCI_VDEVICE(INTEL, 0x1c48), LPC_CPT},
+       { PCI_VDEVICE(INTEL, 0x1c49), LPC_CPT},
+       { PCI_VDEVICE(INTEL, 0x1c4a), LPC_CPT},
+       { PCI_VDEVICE(INTEL, 0x1c4b), LPC_CPT},
+       { PCI_VDEVICE(INTEL, 0x1c4c), LPC_CPT},
+       { PCI_VDEVICE(INTEL, 0x1c4d), LPC_CPT},
+       { PCI_VDEVICE(INTEL, 0x1c4e), LPC_CPT},
+       { PCI_VDEVICE(INTEL, 0x1c4f), LPC_CPT},
+       { PCI_VDEVICE(INTEL, 0x1c50), LPC_CPT},
+       { PCI_VDEVICE(INTEL, 0x1c51), LPC_CPT},
+       { PCI_VDEVICE(INTEL, 0x1c52), LPC_CPT},
+       { PCI_VDEVICE(INTEL, 0x1c53), LPC_CPT},
+       { PCI_VDEVICE(INTEL, 0x1c54), LPC_CPT},
+       { PCI_VDEVICE(INTEL, 0x1c55), LPC_CPT},
+       { PCI_VDEVICE(INTEL, 0x1c56), LPC_CPT},
+       { PCI_VDEVICE(INTEL, 0x1c57), LPC_CPT},
+       { PCI_VDEVICE(INTEL, 0x1c58), LPC_CPT},
+       { PCI_VDEVICE(INTEL, 0x1c59), LPC_CPT},
+       { PCI_VDEVICE(INTEL, 0x1c5a), LPC_CPT},
+       { PCI_VDEVICE(INTEL, 0x1c5b), LPC_CPT},
+       { PCI_VDEVICE(INTEL, 0x1c5c), LPC_CPT},
+       { PCI_VDEVICE(INTEL, 0x1c5d), LPC_CPT},
+       { PCI_VDEVICE(INTEL, 0x1c5e), LPC_CPT},
+       { PCI_VDEVICE(INTEL, 0x1c5f), LPC_CPT},
+       { PCI_VDEVICE(INTEL, 0x1d40), LPC_PBG},
+       { PCI_VDEVICE(INTEL, 0x1d41), LPC_PBG},
+       { PCI_VDEVICE(INTEL, 0x2310), LPC_DH89XXCC},
+       { PCI_VDEVICE(INTEL, 0x1e40), LPC_PPT},
+       { PCI_VDEVICE(INTEL, 0x1e41), LPC_PPT},
+       { PCI_VDEVICE(INTEL, 0x1e42), LPC_PPT},
+       { PCI_VDEVICE(INTEL, 0x1e43), LPC_PPT},
+       { PCI_VDEVICE(INTEL, 0x1e44), LPC_PPT},
+       { PCI_VDEVICE(INTEL, 0x1e45), LPC_PPT},
+       { PCI_VDEVICE(INTEL, 0x1e46), LPC_PPT},
+       { PCI_VDEVICE(INTEL, 0x1e47), LPC_PPT},
+       { PCI_VDEVICE(INTEL, 0x1e48), LPC_PPT},
+       { PCI_VDEVICE(INTEL, 0x1e49), LPC_PPT},
+       { PCI_VDEVICE(INTEL, 0x1e4a), LPC_PPT},
+       { PCI_VDEVICE(INTEL, 0x1e4b), LPC_PPT},
+       { PCI_VDEVICE(INTEL, 0x1e4c), LPC_PPT},
+       { PCI_VDEVICE(INTEL, 0x1e4d), LPC_PPT},
+       { PCI_VDEVICE(INTEL, 0x1e4e), LPC_PPT},
+       { PCI_VDEVICE(INTEL, 0x1e4f), LPC_PPT},
+       { PCI_VDEVICE(INTEL, 0x1e50), LPC_PPT},
+       { PCI_VDEVICE(INTEL, 0x1e51), LPC_PPT},
+       { PCI_VDEVICE(INTEL, 0x1e52), LPC_PPT},
+       { PCI_VDEVICE(INTEL, 0x1e53), LPC_PPT},
+       { PCI_VDEVICE(INTEL, 0x1e54), LPC_PPT},
+       { PCI_VDEVICE(INTEL, 0x1e55), LPC_PPT},
+       { PCI_VDEVICE(INTEL, 0x1e56), LPC_PPT},
+       { PCI_VDEVICE(INTEL, 0x1e57), LPC_PPT},
+       { PCI_VDEVICE(INTEL, 0x1e58), LPC_PPT},
+       { PCI_VDEVICE(INTEL, 0x1e59), LPC_PPT},
+       { PCI_VDEVICE(INTEL, 0x1e5a), LPC_PPT},
+       { PCI_VDEVICE(INTEL, 0x1e5b), LPC_PPT},
+       { PCI_VDEVICE(INTEL, 0x1e5c), LPC_PPT},
+       { PCI_VDEVICE(INTEL, 0x1e5d), LPC_PPT},
+       { PCI_VDEVICE(INTEL, 0x1e5e), LPC_PPT},
+       { PCI_VDEVICE(INTEL, 0x1e5f), LPC_PPT},
+       { PCI_VDEVICE(INTEL, 0x8c40), LPC_LPT},
+       { PCI_VDEVICE(INTEL, 0x8c41), LPC_LPT},
+       { PCI_VDEVICE(INTEL, 0x8c42), LPC_LPT},
+       { PCI_VDEVICE(INTEL, 0x8c43), LPC_LPT},
+       { PCI_VDEVICE(INTEL, 0x8c44), LPC_LPT},
+       { PCI_VDEVICE(INTEL, 0x8c45), LPC_LPT},
+       { PCI_VDEVICE(INTEL, 0x8c46), LPC_LPT},
+       { PCI_VDEVICE(INTEL, 0x8c47), LPC_LPT},
+       { PCI_VDEVICE(INTEL, 0x8c48), LPC_LPT},
+       { PCI_VDEVICE(INTEL, 0x8c49), LPC_LPT},
+       { PCI_VDEVICE(INTEL, 0x8c4a), LPC_LPT},
+       { PCI_VDEVICE(INTEL, 0x8c4b), LPC_LPT},
+       { PCI_VDEVICE(INTEL, 0x8c4c), LPC_LPT},
+       { PCI_VDEVICE(INTEL, 0x8c4d), LPC_LPT},
+       { PCI_VDEVICE(INTEL, 0x8c4e), LPC_LPT},
+       { PCI_VDEVICE(INTEL, 0x8c4f), LPC_LPT},
+       { PCI_VDEVICE(INTEL, 0x8c50), LPC_LPT},
+       { PCI_VDEVICE(INTEL, 0x8c51), LPC_LPT},
+       { PCI_VDEVICE(INTEL, 0x8c52), LPC_LPT},
+       { PCI_VDEVICE(INTEL, 0x8c53), LPC_LPT},
+       { PCI_VDEVICE(INTEL, 0x8c54), LPC_LPT},
+       { PCI_VDEVICE(INTEL, 0x8c55), LPC_LPT},
+       { PCI_VDEVICE(INTEL, 0x8c56), LPC_LPT},
+       { PCI_VDEVICE(INTEL, 0x8c57), LPC_LPT},
+       { PCI_VDEVICE(INTEL, 0x8c58), LPC_LPT},
+       { PCI_VDEVICE(INTEL, 0x8c59), LPC_LPT},
+       { PCI_VDEVICE(INTEL, 0x8c5a), LPC_LPT},
+       { PCI_VDEVICE(INTEL, 0x8c5b), LPC_LPT},
+       { PCI_VDEVICE(INTEL, 0x8c5c), LPC_LPT},
+       { PCI_VDEVICE(INTEL, 0x8c5d), LPC_LPT},
+       { PCI_VDEVICE(INTEL, 0x8c5e), LPC_LPT},
+       { PCI_VDEVICE(INTEL, 0x8c5f), LPC_LPT},
+       { 0, },                 /* End of list */
+};
+MODULE_DEVICE_TABLE(pci, lpc_ich_ids);
+
+static void lpc_ich_restore_config_space(struct pci_dev *dev)
+{
+       if (lpc_ich_acpi_save >= 0) {
+               pci_write_config_byte(dev, ACPICTRL, lpc_ich_acpi_save);
+               lpc_ich_acpi_save = -1;
+       }
+
+       if (lpc_ich_gpio_save >= 0) {
+               pci_write_config_byte(dev, GPIOCTRL, lpc_ich_gpio_save);
+               lpc_ich_gpio_save = -1;
+       }
+}
+
+static void __devinit lpc_ich_enable_acpi_space(struct pci_dev *dev)
+{
+       u8 reg_save;
+
+       pci_read_config_byte(dev, ACPICTRL, &reg_save);
+       pci_write_config_byte(dev, ACPICTRL, reg_save | 0x10);
+       lpc_ich_acpi_save = reg_save;
+}
+
+static void __devinit lpc_ich_enable_gpio_space(struct pci_dev *dev)
+{
+       u8 reg_save;
+
+       pci_read_config_byte(dev, GPIOCTRL, &reg_save);
+       pci_write_config_byte(dev, GPIOCTRL, reg_save | 0x10);
+       lpc_ich_gpio_save = reg_save;
+}
+
+static void __devinit lpc_ich_finalize_cell(struct mfd_cell *cell,
+                                       const struct pci_device_id *id)
+{
+       cell->platform_data = &lpc_chipset_info[id->driver_data];
+       cell->pdata_size = sizeof(struct lpc_ich_info);
+}
+
+static int __devinit lpc_ich_init_gpio(struct pci_dev *dev,
+                               const struct pci_device_id *id)
+{
+       u32 base_addr_cfg;
+       u32 base_addr;
+       int ret;
+       bool acpi_conflict = false;
+       struct resource *res;
+
+       /* Setup power management base register */
+       pci_read_config_dword(dev, ACPIBASE, &base_addr_cfg);
+       base_addr = base_addr_cfg & 0x0000ff80;
+       if (!base_addr) {
+               dev_err(&dev->dev, "I/O space for ACPI uninitialized\n");
+               lpc_ich_cells[LPC_GPIO].num_resources--;
+               goto gpe0_done;
+       }
+
+       res = &gpio_ich_res[ICH_RES_GPE0];
+       res->start = base_addr + ACPIBASE_GPE_OFF;
+       res->end = base_addr + ACPIBASE_GPE_END;
+       ret = acpi_check_resource_conflict(res);
+       if (ret) {
+               /*
+                * This isn't fatal for the GPIO, but we have to make sure that
+                * the platform_device subsystem doesn't see this resource
+                * or it will register an invalid region.
+                */
+               lpc_ich_cells[LPC_GPIO].num_resources--;
+               acpi_conflict = true;
+       } else {
+               lpc_ich_enable_acpi_space(dev);
+       }
+
+gpe0_done:
+       /* Setup GPIO base register */
+       pci_read_config_dword(dev, GPIOBASE, &base_addr_cfg);
+       base_addr = base_addr_cfg & 0x0000ff80;
+       if (!base_addr) {
+               dev_err(&dev->dev, "I/O space for GPIO uninitialized\n");
+               ret = -ENODEV;
+               goto gpio_done;
+       }
+
+       /* Older devices provide fewer GPIO and have a smaller resource size. */
+       res = &gpio_ich_res[ICH_RES_GPIO];
+       res->start = base_addr;
+       switch (lpc_chipset_info[id->driver_data].gpio_version) {
+       case ICH_V5_GPIO:
+       case ICH_V10CORP_GPIO:
+               res->end = res->start + 128 - 1;
+               break;
+       default:
+               res->end = res->start + 64 - 1;
+               break;
+       }
+
+       ret = acpi_check_resource_conflict(res);
+       if (ret) {
+               /* this isn't necessarily fatal for the GPIO */
+               acpi_conflict = true;
+               goto gpio_done;
+       }
+       lpc_ich_enable_gpio_space(dev);
+
+       lpc_ich_finalize_cell(&lpc_ich_cells[LPC_GPIO], id);
+       ret = mfd_add_devices(&dev->dev, -1, &lpc_ich_cells[LPC_GPIO],
+                               1, NULL, 0);
+
+gpio_done:
+       if (acpi_conflict)
+               pr_warn("Resource conflict(s) found affecting %s\n",
+                               lpc_ich_cells[LPC_GPIO].name);
+       return ret;
+}
+
+static int __devinit lpc_ich_init_wdt(struct pci_dev *dev,
+                               const struct pci_device_id *id)
+{
+       u32 base_addr_cfg;
+       u32 base_addr;
+       int ret;
+       bool acpi_conflict = false;
+       struct resource *res;
+
+       /* Setup power management base register */
+       pci_read_config_dword(dev, ACPIBASE, &base_addr_cfg);
+       base_addr = base_addr_cfg & 0x0000ff80;
+       if (!base_addr) {
+               dev_err(&dev->dev, "I/O space for ACPI uninitialized\n");
+               ret = -ENODEV;
+               goto wdt_done;
+       }
+
+       res = wdt_io_res(ICH_RES_IO_TCO);
+       res->start = base_addr + ACPIBASE_TCO_OFF;
+       res->end = base_addr + ACPIBASE_TCO_END;
+       ret = acpi_check_resource_conflict(res);
+       if (ret) {
+               acpi_conflict = true;
+               goto wdt_done;
+       }
+
+       res = wdt_io_res(ICH_RES_IO_SMI);
+       res->start = base_addr + ACPIBASE_SMI_OFF;
+       res->end = base_addr + ACPIBASE_SMI_END;
+       ret = acpi_check_resource_conflict(res);
+       if (ret) {
+               acpi_conflict = true;
+               goto wdt_done;
+       }
+       lpc_ich_enable_acpi_space(dev);
+
+       /*
+        * Get the Memory-Mapped GCS register. To get access to it
+        * we have to read RCBA from PCI Config space 0xf0 and use
+        * it as base. GCS = RCBA + ICH6_GCS(0x3410).
+        */
+       if (lpc_chipset_info[id->driver_data].iTCO_version == 2) {
+               pci_read_config_dword(dev, RCBABASE, &base_addr_cfg);
+               base_addr = base_addr_cfg & 0xffffc000;
+               if (!(base_addr_cfg & 1)) {
+                       pr_err("RCBA is disabled by hardware/BIOS, "
+                                       "device disabled\n");
+                       ret = -ENODEV;
+                       goto wdt_done;
+               }
+               res = wdt_mem_res(ICH_RES_MEM_GCS);
+               res->start = base_addr + ACPIBASE_GCS_OFF;
+               res->end = base_addr + ACPIBASE_GCS_END;
+               ret = acpi_check_resource_conflict(res);
+               if (ret) {
+                       acpi_conflict = true;
+                       goto wdt_done;
+               }
+       }
+
+       lpc_ich_finalize_cell(&lpc_ich_cells[LPC_WDT], id);
+       ret = mfd_add_devices(&dev->dev, -1, &lpc_ich_cells[LPC_WDT],
+                               1, NULL, 0);
+
+wdt_done:
+       if (acpi_conflict)
+               pr_warn("Resource conflict(s) found affecting %s\n",
+                               lpc_ich_cells[LPC_WDT].name);
+       return ret;
+}
+
+static int __devinit lpc_ich_probe(struct pci_dev *dev,
+                               const struct pci_device_id *id)
+{
+       int ret;
+       bool cell_added = false;
+
+       ret = lpc_ich_init_wdt(dev, id);
+       if (!ret)
+               cell_added = true;
+
+       ret = lpc_ich_init_gpio(dev, id);
+       if (!ret)
+               cell_added = true;
+
+       /*
+        * We only care if at least one or none of the cells registered
+        * successfully.
+        */
+       if (!cell_added) {
+               lpc_ich_restore_config_space(dev);
+               return -ENODEV;
+       }
+
+       return 0;
+}
+
+static void __devexit lpc_ich_remove(struct pci_dev *dev)
+{
+       mfd_remove_devices(&dev->dev);
+       lpc_ich_restore_config_space(dev);
+}
+
+static struct pci_driver lpc_ich_driver = {
+       .name           = "lpc_ich",
+       .id_table       = lpc_ich_ids,
+       .probe          = lpc_ich_probe,
+       .remove         = __devexit_p(lpc_ich_remove),
+};
+
+static int __init lpc_ich_init(void)
+{
+       return pci_register_driver(&lpc_ich_driver);
+}
+
+static void __exit lpc_ich_exit(void)
+{
+       pci_unregister_driver(&lpc_ich_driver);
+}
+
+module_init(lpc_ich_init);
+module_exit(lpc_ich_exit);
+
+MODULE_AUTHOR("Aaron Sierra <asierra@xes-inc.com>");
+MODULE_DESCRIPTION("LPC interface for Intel ICH");
+MODULE_LICENSE("GPL");
index abc421364a454f0f596f7e5be734e346838318cb..9f20abc5e3937065238ff1f3240c27cde9cbb4f6 100644 (file)
@@ -36,6 +36,7 @@
 
 #define GPIOBASE       0x44
 #define GPIO_IO_SIZE   64
+#define GPIO_IO_SIZE_CENTERTON 128
 
 #define WDTBASE                0x84
 #define WDT_IO_SIZE    64
@@ -68,7 +69,7 @@ static struct resource wdt_sch_resource = {
 
 static struct mfd_cell tunnelcreek_cells[] = {
        {
-               .name = "tunnelcreek_wdt",
+               .name = "ie6xx_wdt",
                .num_resources = 1,
                .resources = &wdt_sch_resource,
        },
@@ -77,6 +78,7 @@ static struct mfd_cell tunnelcreek_cells[] = {
 static DEFINE_PCI_DEVICE_TABLE(lpc_sch_ids) = {
        { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SCH_LPC) },
        { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ITC_LPC) },
+       { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CENTERTON_ILB) },
        { 0, }
 };
 MODULE_DEVICE_TABLE(pci, lpc_sch_ids);
@@ -115,7 +117,11 @@ static int __devinit lpc_sch_probe(struct pci_dev *dev,
        }
 
        gpio_sch_resource.start = base_addr;
-       gpio_sch_resource.end = base_addr + GPIO_IO_SIZE - 1;
+
+       if (id->device == PCI_DEVICE_ID_INTEL_CENTERTON_ILB)
+               gpio_sch_resource.end = base_addr + GPIO_IO_SIZE_CENTERTON - 1;
+       else
+               gpio_sch_resource.end = base_addr + GPIO_IO_SIZE - 1;
 
        for (i=0; i < ARRAY_SIZE(lpc_sch_cells); i++)
                lpc_sch_cells[i].id = id->device;
@@ -125,7 +131,8 @@ static int __devinit lpc_sch_probe(struct pci_dev *dev,
        if (ret)
                goto out_dev;
 
-       if (id->device == PCI_DEVICE_ID_INTEL_ITC_LPC) {
+       if (id->device == PCI_DEVICE_ID_INTEL_ITC_LPC
+        || id->device == PCI_DEVICE_ID_INTEL_CENTERTON_ILB) {
                pci_read_config_dword(dev, WDTBASE, &base_addr_cfg);
                if (!(base_addr_cfg & (1 << 31))) {
                        dev_err(&dev->dev, "Decode of the WDT I/O range disabled\n");
@@ -167,18 +174,7 @@ static struct pci_driver lpc_sch_driver = {
        .remove         = __devexit_p(lpc_sch_remove),
 };
 
-static int __init lpc_sch_init(void)
-{
-       return pci_register_driver(&lpc_sch_driver);
-}
-
-static void __exit lpc_sch_exit(void)
-{
-       pci_unregister_driver(&lpc_sch_driver);
-}
-
-module_init(lpc_sch_init);
-module_exit(lpc_sch_exit);
+module_pci_driver(lpc_sch_driver);
 
 MODULE_AUTHOR("Denis Turischev <denis@compulab.co.il>");
 MODULE_DESCRIPTION("LPC interface for Intel Poulsbo SCH");
diff --git a/drivers/mfd/max77693-irq.c b/drivers/mfd/max77693-irq.c
new file mode 100644 (file)
index 0000000..2b40356
--- /dev/null
@@ -0,0 +1,309 @@
+/*
+ * max77693-irq.c - Interrupt controller support for MAX77693
+ *
+ * Copyright (C) 2012 Samsung Electronics Co.Ltd
+ * SangYoung Son <hello.son@samsung.com>
+ *
+ * This program is not provided / owned by Maxim Integrated Products.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * This driver is based on max8997-irq.c
+ */
+
+#include <linux/err.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/irqdomain.h>
+#include <linux/mfd/max77693.h>
+#include <linux/mfd/max77693-private.h>
+
+static const u8 max77693_mask_reg[] = {
+       [LED_INT] = MAX77693_LED_REG_FLASH_INT_MASK,
+       [TOPSYS_INT] = MAX77693_PMIC_REG_TOPSYS_INT_MASK,
+       [CHG_INT] = MAX77693_CHG_REG_CHG_INT_MASK,
+       [MUIC_INT1] = MAX77693_MUIC_REG_INTMASK1,
+       [MUIC_INT2] = MAX77693_MUIC_REG_INTMASK2,
+       [MUIC_INT3] = MAX77693_MUIC_REG_INTMASK3,
+};
+
+static struct regmap *max77693_get_regmap(struct max77693_dev *max77693,
+                               enum max77693_irq_source src)
+{
+       switch (src) {
+       case LED_INT ... CHG_INT:
+               return max77693->regmap;
+       case MUIC_INT1 ... MUIC_INT3:
+               return max77693->regmap_muic;
+       default:
+               return ERR_PTR(-EINVAL);
+       }
+}
+
+struct max77693_irq_data {
+       int mask;
+       enum max77693_irq_source group;
+};
+
+#define DECLARE_IRQ(idx, _group, _mask)                \
+       [(idx)] = { .group = (_group), .mask = (_mask) }
+static const struct max77693_irq_data max77693_irqs[] = {
+       DECLARE_IRQ(MAX77693_LED_IRQ_FLED2_OPEN,        LED_INT, 1 << 0),
+       DECLARE_IRQ(MAX77693_LED_IRQ_FLED2_SHORT,       LED_INT, 1 << 1),
+       DECLARE_IRQ(MAX77693_LED_IRQ_FLED1_OPEN,        LED_INT, 1 << 2),
+       DECLARE_IRQ(MAX77693_LED_IRQ_FLED1_SHORT,       LED_INT, 1 << 3),
+       DECLARE_IRQ(MAX77693_LED_IRQ_MAX_FLASH,         LED_INT, 1 << 4),
+
+       DECLARE_IRQ(MAX77693_TOPSYS_IRQ_T120C_INT,      TOPSYS_INT, 1 << 0),
+       DECLARE_IRQ(MAX77693_TOPSYS_IRQ_T140C_INT,      TOPSYS_INT, 1 << 1),
+       DECLARE_IRQ(MAX77693_TOPSYS_IRQ_LOWSYS_INT,     TOPSYS_INT, 1 << 3),
+
+       DECLARE_IRQ(MAX77693_CHG_IRQ_BYP_I,             CHG_INT, 1 << 0),
+       DECLARE_IRQ(MAX77693_CHG_IRQ_THM_I,             CHG_INT, 1 << 2),
+       DECLARE_IRQ(MAX77693_CHG_IRQ_BAT_I,             CHG_INT, 1 << 3),
+       DECLARE_IRQ(MAX77693_CHG_IRQ_CHG_I,             CHG_INT, 1 << 4),
+       DECLARE_IRQ(MAX77693_CHG_IRQ_CHGIN_I,           CHG_INT, 1 << 6),
+
+       DECLARE_IRQ(MAX77693_MUIC_IRQ_INT1_ADC,         MUIC_INT1, 1 << 0),
+       DECLARE_IRQ(MAX77693_MUIC_IRQ_INT1_ADC_LOW,     MUIC_INT1, 1 << 1),
+       DECLARE_IRQ(MAX77693_MUIC_IRQ_INT1_ADC_ERR,     MUIC_INT1, 1 << 2),
+       DECLARE_IRQ(MAX77693_MUIC_IRQ_INT1_ADC1K,       MUIC_INT1, 1 << 3),
+
+       DECLARE_IRQ(MAX77693_MUIC_IRQ_INT2_CHGTYP,      MUIC_INT2, 1 << 0),
+       DECLARE_IRQ(MAX77693_MUIC_IRQ_INT2_CHGDETREUN,  MUIC_INT2, 1 << 1),
+       DECLARE_IRQ(MAX77693_MUIC_IRQ_INT2_DCDTMR,      MUIC_INT2, 1 << 2),
+       DECLARE_IRQ(MAX77693_MUIC_IRQ_INT2_DXOVP,       MUIC_INT2, 1 << 3),
+       DECLARE_IRQ(MAX77693_MUIC_IRQ_INT2_VBVOLT,      MUIC_INT2, 1 << 4),
+       DECLARE_IRQ(MAX77693_MUIC_IRQ_INT2_VIDRM,       MUIC_INT2, 1 << 5),
+
+       DECLARE_IRQ(MAX77693_MUIC_IRQ_INT3_EOC,         MUIC_INT3, 1 << 0),
+       DECLARE_IRQ(MAX77693_MUIC_IRQ_INT3_CGMBC,       MUIC_INT3, 1 << 1),
+       DECLARE_IRQ(MAX77693_MUIC_IRQ_INT3_OVP,         MUIC_INT3, 1 << 2),
+       DECLARE_IRQ(MAX77693_MUIC_IRQ_INT3_MBCCHG_ERR,  MUIC_INT3, 1 << 3),
+       DECLARE_IRQ(MAX77693_MUIC_IRQ_INT3_CHG_ENABLED, MUIC_INT3, 1 << 4),
+       DECLARE_IRQ(MAX77693_MUIC_IRQ_INT3_BAT_DET,     MUIC_INT3, 1 << 5),
+};
+
+static void max77693_irq_lock(struct irq_data *data)
+{
+       struct max77693_dev *max77693 = irq_get_chip_data(data->irq);
+
+       mutex_lock(&max77693->irqlock);
+}
+
+static void max77693_irq_sync_unlock(struct irq_data *data)
+{
+       struct max77693_dev *max77693 = irq_get_chip_data(data->irq);
+       int i;
+
+       for (i = 0; i < MAX77693_IRQ_GROUP_NR; i++) {
+               u8 mask_reg = max77693_mask_reg[i];
+               struct regmap *map = max77693_get_regmap(max77693, i);
+
+               if (mask_reg == MAX77693_REG_INVALID ||
+                               IS_ERR_OR_NULL(map))
+                       continue;
+               max77693->irq_masks_cache[i] = max77693->irq_masks_cur[i];
+
+               max77693_write_reg(map, max77693_mask_reg[i],
+                               max77693->irq_masks_cur[i]);
+       }
+
+       mutex_unlock(&max77693->irqlock);
+}
+
+static const inline struct max77693_irq_data *
+irq_to_max77693_irq(struct max77693_dev *max77693, int irq)
+{
+       return &max77693_irqs[irq];
+}
+
+static void max77693_irq_mask(struct irq_data *data)
+{
+       struct max77693_dev *max77693 = irq_get_chip_data(data->irq);
+       const struct max77693_irq_data *irq_data =
+                               irq_to_max77693_irq(max77693, data->irq);
+
+       if (irq_data->group >= MUIC_INT1 && irq_data->group <= MUIC_INT3)
+               max77693->irq_masks_cur[irq_data->group] &= ~irq_data->mask;
+       else
+               max77693->irq_masks_cur[irq_data->group] |= irq_data->mask;
+}
+
+static void max77693_irq_unmask(struct irq_data *data)
+{
+       struct max77693_dev *max77693 = irq_get_chip_data(data->irq);
+       const struct max77693_irq_data *irq_data =
+           irq_to_max77693_irq(max77693, data->irq);
+
+       if (irq_data->group >= MUIC_INT1 && irq_data->group <= MUIC_INT3)
+               max77693->irq_masks_cur[irq_data->group] |= irq_data->mask;
+       else
+               max77693->irq_masks_cur[irq_data->group] &= ~irq_data->mask;
+}
+
+static struct irq_chip max77693_irq_chip = {
+       .name                   = "max77693",
+       .irq_bus_lock           = max77693_irq_lock,
+       .irq_bus_sync_unlock    = max77693_irq_sync_unlock,
+       .irq_mask               = max77693_irq_mask,
+       .irq_unmask             = max77693_irq_unmask,
+};
+
+#define MAX77693_IRQSRC_CHG            (1 << 0)
+#define MAX77693_IRQSRC_TOP            (1 << 1)
+#define MAX77693_IRQSRC_FLASH          (1 << 2)
+#define MAX77693_IRQSRC_MUIC           (1 << 3)
+static irqreturn_t max77693_irq_thread(int irq, void *data)
+{
+       struct max77693_dev *max77693 = data;
+       u8 irq_reg[MAX77693_IRQ_GROUP_NR] = {};
+       u8 irq_src;
+       int ret;
+       int i, cur_irq;
+
+       ret = max77693_read_reg(max77693->regmap, MAX77693_PMIC_REG_INTSRC,
+                               &irq_src);
+       if (ret < 0) {
+               dev_err(max77693->dev, "Failed to read interrupt source: %d\n",
+                               ret);
+               return IRQ_NONE;
+       }
+
+       if (irq_src & MAX77693_IRQSRC_CHG)
+               /* CHG_INT */
+               ret = max77693_read_reg(max77693->regmap, MAX77693_CHG_REG_CHG_INT,
+                               &irq_reg[CHG_INT]);
+
+       if (irq_src & MAX77693_IRQSRC_TOP)
+               /* TOPSYS_INT */
+               ret = max77693_read_reg(max77693->regmap,
+                       MAX77693_PMIC_REG_TOPSYS_INT, &irq_reg[TOPSYS_INT]);
+
+       if (irq_src & MAX77693_IRQSRC_FLASH)
+               /* LED_INT */
+               ret = max77693_read_reg(max77693->regmap,
+                       MAX77693_LED_REG_FLASH_INT, &irq_reg[LED_INT]);
+
+       if (irq_src & MAX77693_IRQSRC_MUIC)
+               /* MUIC INT1 ~ INT3 */
+               max77693_bulk_read(max77693->regmap, MAX77693_MUIC_REG_INT1,
+                       MAX77693_NUM_IRQ_MUIC_REGS, &irq_reg[MUIC_INT1]);
+
+       /* Apply masking */
+       for (i = 0; i < MAX77693_IRQ_GROUP_NR; i++) {
+               if (i >= MUIC_INT1 && i <= MUIC_INT3)
+                       irq_reg[i] &= max77693->irq_masks_cur[i];
+               else
+                       irq_reg[i] &= ~max77693->irq_masks_cur[i];
+       }
+
+       /* Report */
+       for (i = 0; i < MAX77693_IRQ_NR; i++) {
+               if (irq_reg[max77693_irqs[i].group] & max77693_irqs[i].mask) {
+                       cur_irq = irq_find_mapping(max77693->irq_domain, i);
+                       if (cur_irq)
+                               handle_nested_irq(cur_irq);
+               }
+       }
+
+       return IRQ_HANDLED;
+}
+
+int max77693_irq_resume(struct max77693_dev *max77693)
+{
+       if (max77693->irq)
+               max77693_irq_thread(0, max77693);
+
+       return 0;
+}
+
+static int max77693_irq_domain_map(struct irq_domain *d, unsigned int irq,
+                               irq_hw_number_t hw)
+{
+       struct max77693_dev *max77693 = d->host_data;
+
+       irq_set_chip_data(irq, max77693);
+       irq_set_chip_and_handler(irq, &max77693_irq_chip, handle_edge_irq);
+       irq_set_nested_thread(irq, 1);
+#ifdef CONFIG_ARM
+       set_irq_flags(irq, IRQF_VALID);
+#else
+       irq_set_noprobe(irq);
+#endif
+       return 0;
+}
+
+static struct irq_domain_ops max77693_irq_domain_ops = {
+       .map = max77693_irq_domain_map,
+};
+
+int max77693_irq_init(struct max77693_dev *max77693)
+{
+       struct irq_domain *domain;
+       int i;
+       int ret;
+
+       mutex_init(&max77693->irqlock);
+
+       /* Mask individual interrupt sources */
+       for (i = 0; i < MAX77693_IRQ_GROUP_NR; i++) {
+               struct regmap *map;
+               /* MUIC IRQ  0:MASK 1:NOT MASK */
+               /* Other IRQ 1:MASK 0:NOT MASK */
+               if (i >= MUIC_INT1 && i <= MUIC_INT3) {
+                       max77693->irq_masks_cur[i] = 0x00;
+                       max77693->irq_masks_cache[i] = 0x00;
+               } else {
+                       max77693->irq_masks_cur[i] = 0xff;
+                       max77693->irq_masks_cache[i] = 0xff;
+               }
+               map = max77693_get_regmap(max77693, i);
+
+               if (IS_ERR_OR_NULL(map))
+                       continue;
+               if (max77693_mask_reg[i] == MAX77693_REG_INVALID)
+                       continue;
+               if (i >= MUIC_INT1 && i <= MUIC_INT3)
+                       max77693_write_reg(map, max77693_mask_reg[i], 0x00);
+               else
+                       max77693_write_reg(map, max77693_mask_reg[i], 0xff);
+       }
+
+       domain = irq_domain_add_linear(NULL, MAX77693_IRQ_NR,
+                                       &max77693_irq_domain_ops, max77693);
+       if (!domain) {
+               dev_err(max77693->dev, "could not create irq domain\n");
+               return -ENODEV;
+       }
+       max77693->irq_domain = domain;
+
+       ret = request_threaded_irq(max77693->irq, NULL, max77693_irq_thread,
+                                  IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+                                  "max77693-irq", max77693);
+
+       if (ret)
+               dev_err(max77693->dev, "Failed to request IRQ %d: %d\n",
+                       max77693->irq, ret);
+
+       return 0;
+}
+
+void max77693_irq_exit(struct max77693_dev *max77693)
+{
+       if (max77693->irq)
+               free_irq(max77693->irq, max77693);
+}
diff --git a/drivers/mfd/max77693.c b/drivers/mfd/max77693.c
new file mode 100644 (file)
index 0000000..e9e4278
--- /dev/null
@@ -0,0 +1,249 @@
+/*
+ * max77693.c - mfd core driver for the MAX 77693
+ *
+ * Copyright (C) 2012 Samsung Electronics
+ * SangYoung Son <hello.son@smasung.com>
+ *
+ * This program is not provided / owned by Maxim Integrated Products.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * This driver is based on max8997.c
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/pm_runtime.h>
+#include <linux/mutex.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/max77693.h>
+#include <linux/mfd/max77693-private.h>
+#include <linux/regulator/machine.h>
+#include <linux/regmap.h>
+
+#define I2C_ADDR_PMIC  (0xCC >> 1)     /* Charger, Flash LED */
+#define I2C_ADDR_MUIC  (0x4A >> 1)
+#define I2C_ADDR_HAPTIC        (0x90 >> 1)
+
+static struct mfd_cell max77693_devs[] = {
+       { .name = "max77693-pmic", },
+       { .name = "max77693-charger", },
+       { .name = "max77693-flash", },
+       { .name = "max77693-muic", },
+       { .name = "max77693-haptic", },
+};
+
+int max77693_read_reg(struct regmap *map, u8 reg, u8 *dest)
+{
+       unsigned int val;
+       int ret;
+
+       ret = regmap_read(map, reg, &val);
+       *dest = val;
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(max77693_read_reg);
+
+int max77693_bulk_read(struct regmap *map, u8 reg, int count, u8 *buf)
+{
+       int ret;
+
+       ret = regmap_bulk_read(map, reg, buf, count);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(max77693_bulk_read);
+
+int max77693_write_reg(struct regmap *map, u8 reg, u8 value)
+{
+       int ret;
+
+       ret = regmap_write(map, reg, value);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(max77693_write_reg);
+
+int max77693_bulk_write(struct regmap *map, u8 reg, int count, u8 *buf)
+{
+       int ret;
+
+       ret = regmap_bulk_write(map, reg, buf, count);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(max77693_bulk_write);
+
+int max77693_update_reg(struct regmap *map, u8 reg, u8 val, u8 mask)
+{
+       int ret;
+
+       ret = regmap_update_bits(map, reg, mask, val);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(max77693_update_reg);
+
+static const struct regmap_config max77693_regmap_config = {
+       .reg_bits = 8,
+       .val_bits = 8,
+       .max_register = MAX77693_PMIC_REG_END,
+};
+
+static int max77693_i2c_probe(struct i2c_client *i2c,
+                             const struct i2c_device_id *id)
+{
+       struct max77693_dev *max77693;
+       struct max77693_platform_data *pdata = i2c->dev.platform_data;
+       u8 reg_data;
+       int ret = 0;
+
+       max77693 = devm_kzalloc(&i2c->dev,
+                       sizeof(struct max77693_dev), GFP_KERNEL);
+       if (max77693 == NULL)
+               return -ENOMEM;
+
+       max77693->regmap = devm_regmap_init_i2c(i2c, &max77693_regmap_config);
+       if (IS_ERR(max77693->regmap)) {
+               ret = PTR_ERR(max77693->regmap);
+               dev_err(max77693->dev,"failed to allocate register map: %d\n",
+                               ret);
+               goto err_regmap;
+       }
+
+       i2c_set_clientdata(i2c, max77693);
+       max77693->dev = &i2c->dev;
+       max77693->i2c = i2c;
+       max77693->irq = i2c->irq;
+       max77693->type = id->driver_data;
+
+       if (!pdata)
+               goto err_regmap;
+
+       max77693->wakeup = pdata->wakeup;
+
+       mutex_init(&max77693->iolock);
+
+       if (max77693_read_reg(max77693->regmap,
+                               MAX77693_PMIC_REG_PMIC_ID2, &reg_data) < 0) {
+               dev_err(max77693->dev, "device not found on this channel\n");
+               ret = -ENODEV;
+               goto err_regmap;
+       } else
+               dev_info(max77693->dev, "device ID: 0x%x\n", reg_data);
+
+       max77693->muic = i2c_new_dummy(i2c->adapter, I2C_ADDR_MUIC);
+       i2c_set_clientdata(max77693->muic, max77693);
+
+       max77693->haptic = i2c_new_dummy(i2c->adapter, I2C_ADDR_HAPTIC);
+       i2c_set_clientdata(max77693->haptic, max77693);
+
+       ret = max77693_irq_init(max77693);
+       if (ret < 0)
+               goto err_mfd;
+
+       pm_runtime_set_active(max77693->dev);
+
+       ret = mfd_add_devices(max77693->dev, -1, max77693_devs,
+                       ARRAY_SIZE(max77693_devs), NULL, 0);
+       if (ret < 0)
+               goto err_mfd;
+
+       device_init_wakeup(max77693->dev, pdata->wakeup);
+
+       return ret;
+
+err_mfd:
+       i2c_unregister_device(max77693->muic);
+       i2c_unregister_device(max77693->haptic);
+err_regmap:
+       kfree(max77693);
+
+       return ret;
+}
+
+static int max77693_i2c_remove(struct i2c_client *i2c)
+{
+       struct max77693_dev *max77693 = i2c_get_clientdata(i2c);
+
+       mfd_remove_devices(max77693->dev);
+       i2c_unregister_device(max77693->muic);
+       i2c_unregister_device(max77693->haptic);
+
+       return 0;
+}
+
+static const struct i2c_device_id max77693_i2c_id[] = {
+       { "max77693", TYPE_MAX77693 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, max77693_i2c_id);
+
+static int max77693_suspend(struct device *dev)
+{
+       struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
+       struct max77693_dev *max77693 = i2c_get_clientdata(i2c);
+
+       if (device_may_wakeup(dev))
+               irq_set_irq_wake(max77693->irq, 1);
+       return 0;
+}
+
+static int max77693_resume(struct device *dev)
+{
+       struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
+       struct max77693_dev *max77693 = i2c_get_clientdata(i2c);
+
+       if (device_may_wakeup(dev))
+               irq_set_irq_wake(max77693->irq, 0);
+       return max77693_irq_resume(max77693);
+}
+
+const struct dev_pm_ops max77693_pm = {
+       .suspend = max77693_suspend,
+       .resume = max77693_resume,
+};
+
+static struct i2c_driver max77693_i2c_driver = {
+       .driver = {
+                  .name = "max77693",
+                  .owner = THIS_MODULE,
+                  .pm = &max77693_pm,
+       },
+       .probe = max77693_i2c_probe,
+       .remove = max77693_i2c_remove,
+       .id_table = max77693_i2c_id,
+};
+
+static int __init max77693_i2c_init(void)
+{
+       return i2c_add_driver(&max77693_i2c_driver);
+}
+/* init early so consumer devices can complete system boot */
+subsys_initcall(max77693_i2c_init);
+
+static void __exit max77693_i2c_exit(void)
+{
+       i2c_del_driver(&max77693_i2c_driver);
+}
+module_exit(max77693_i2c_exit);
+
+MODULE_DESCRIPTION("MAXIM 77693 multi-function core driver");
+MODULE_AUTHOR("SangYoung, Son <hello.son@samsung.com>");
+MODULE_LICENSE("GPL");
index 738722cdecaaa23e075b25cf2407357cea372ee7..f0ea3b8b3e4ad979d5e1d5fe53bde83a7392dfd2 100644 (file)
 #include <linux/platform_device.h>
 #include <linux/mutex.h>
 #include <linux/interrupt.h>
-#include <linux/spi/spi.h>
 #include <linux/mfd/core.h>
 #include <linux/mfd/mc13xxx.h>
 #include <linux/of.h>
 #include <linux/of_device.h>
 #include <linux/of_gpio.h>
 
-struct mc13xxx {
-       struct spi_device *spidev;
-       struct mutex lock;
-       int irq;
-       int flags;
-
-       irq_handler_t irqhandler[MC13XXX_NUM_IRQ];
-       void *irqdata[MC13XXX_NUM_IRQ];
-
-       int adcflags;
-};
+#include "mc13xxx.h"
 
 #define MC13XXX_IRQSTAT0       0
 #define MC13XXX_IRQSTAT0_ADCDONEI      (1 << 0)
@@ -139,34 +128,29 @@ struct mc13xxx {
 
 #define MC13XXX_ADC2           45
 
-#define MC13XXX_NUMREGS 0x3f
-
 void mc13xxx_lock(struct mc13xxx *mc13xxx)
 {
        if (!mutex_trylock(&mc13xxx->lock)) {
-               dev_dbg(&mc13xxx->spidev->dev, "wait for %s from %pf\n",
+               dev_dbg(mc13xxx->dev, "wait for %s from %pf\n",
                                __func__, __builtin_return_address(0));
 
                mutex_lock(&mc13xxx->lock);
        }
-       dev_dbg(&mc13xxx->spidev->dev, "%s from %pf\n",
+       dev_dbg(mc13xxx->dev, "%s from %pf\n",
                        __func__, __builtin_return_address(0));
 }
 EXPORT_SYMBOL(mc13xxx_lock);
 
 void mc13xxx_unlock(struct mc13xxx *mc13xxx)
 {
-       dev_dbg(&mc13xxx->spidev->dev, "%s from %pf\n",
+       dev_dbg(mc13xxx->dev, "%s from %pf\n",
                        __func__, __builtin_return_address(0));
        mutex_unlock(&mc13xxx->lock);
 }
 EXPORT_SYMBOL(mc13xxx_unlock);
 
-#define MC13XXX_REGOFFSET_SHIFT 25
 int mc13xxx_reg_read(struct mc13xxx *mc13xxx, unsigned int offset, u32 *val)
 {
-       struct spi_transfer t;
-       struct spi_message m;
        int ret;
 
        BUG_ON(!mutex_is_locked(&mc13xxx->lock));
@@ -174,84 +158,35 @@ int mc13xxx_reg_read(struct mc13xxx *mc13xxx, unsigned int offset, u32 *val)
        if (offset > MC13XXX_NUMREGS)
                return -EINVAL;
 
-       *val = offset << MC13XXX_REGOFFSET_SHIFT;
-
-       memset(&t, 0, sizeof(t));
-
-       t.tx_buf = val;
-       t.rx_buf = val;
-       t.len = sizeof(u32);
-
-       spi_message_init(&m);
-       spi_message_add_tail(&t, &m);
-
-       ret = spi_sync(mc13xxx->spidev, &m);
-
-       /* error in message.status implies error return from spi_sync */
-       BUG_ON(!ret && m.status);
+       ret = regmap_read(mc13xxx->regmap, offset, val);
+       dev_vdbg(mc13xxx->dev, "[0x%02x] -> 0x%06x\n", offset, *val);
 
-       if (ret)
-               return ret;
-
-       *val &= 0xffffff;
-
-       dev_vdbg(&mc13xxx->spidev->dev, "[0x%02x] -> 0x%06x\n", offset, *val);
-
-       return 0;
+       return ret;
 }
 EXPORT_SYMBOL(mc13xxx_reg_read);
 
 int mc13xxx_reg_write(struct mc13xxx *mc13xxx, unsigned int offset, u32 val)
 {
-       u32 buf;
-       struct spi_transfer t;
-       struct spi_message m;
-       int ret;
-
        BUG_ON(!mutex_is_locked(&mc13xxx->lock));
 
-       dev_vdbg(&mc13xxx->spidev->dev, "[0x%02x] <- 0x%06x\n", offset, val);
+       dev_vdbg(mc13xxx->dev, "[0x%02x] <- 0x%06x\n", offset, val);
 
        if (offset > MC13XXX_NUMREGS || val > 0xffffff)
                return -EINVAL;
 
-       buf = 1 << 31 | offset << MC13XXX_REGOFFSET_SHIFT | val;
-
-       memset(&t, 0, sizeof(t));
-
-       t.tx_buf = &buf;
-       t.rx_buf = &buf;
-       t.len = sizeof(u32);
-
-       spi_message_init(&m);
-       spi_message_add_tail(&t, &m);
-
-       ret = spi_sync(mc13xxx->spidev, &m);
-
-       BUG_ON(!ret && m.status);
-
-       if (ret)
-               return ret;
-
-       return 0;
+       return regmap_write(mc13xxx->regmap, offset, val);
 }
 EXPORT_SYMBOL(mc13xxx_reg_write);
 
 int mc13xxx_reg_rmw(struct mc13xxx *mc13xxx, unsigned int offset,
                u32 mask, u32 val)
 {
-       int ret;
-       u32 valread;
-
+       BUG_ON(!mutex_is_locked(&mc13xxx->lock));
        BUG_ON(val & ~mask);
+       dev_vdbg(mc13xxx->dev, "[0x%02x] <- 0x%06x (mask: 0x%06x)\n",
+                       offset, val, mask);
 
-       ret = mc13xxx_reg_read(mc13xxx, offset, &valread);
-       if (ret)
-               return ret;
-
-       valread = (valread & ~mask) | val;
-
-       return mc13xxx_reg_write(mc13xxx, offset, valread);
+       return regmap_update_bits(mc13xxx->regmap, offset, mask, val);
 }
 EXPORT_SYMBOL(mc13xxx_reg_rmw);
 
@@ -439,7 +374,7 @@ static int mc13xxx_irq_handle(struct mc13xxx *mc13xxx,
                        if (handled == IRQ_HANDLED)
                                num_handled++;
                } else {
-                       dev_err(&mc13xxx->spidev->dev,
+                       dev_err(mc13xxx->dev,
                                        "BUG: irq %u but no handler\n",
                                        baseirq + irq);
 
@@ -475,25 +410,23 @@ static irqreturn_t mc13xxx_irq_thread(int irq, void *data)
        return IRQ_RETVAL(handled);
 }
 
-enum mc13xxx_id {
-       MC13XXX_ID_MC13783,
-       MC13XXX_ID_MC13892,
-       MC13XXX_ID_INVALID,
-};
-
 static const char *mc13xxx_chipname[] = {
        [MC13XXX_ID_MC13783] = "mc13783",
        [MC13XXX_ID_MC13892] = "mc13892",
 };
 
 #define maskval(reg, mask)     (((reg) & (mask)) >> __ffs(mask))
-static int mc13xxx_identify(struct mc13xxx *mc13xxx, enum mc13xxx_id *id)
+static int mc13xxx_identify(struct mc13xxx *mc13xxx)
 {
        u32 icid;
        u32 revision;
-       const char *name;
        int ret;
 
+       /*
+        * Get the generation ID from register 46, as apparently some older
+        * IC revisions only have this info at this location. Newer ICs seem to
+        * have both.
+        */
        ret = mc13xxx_reg_read(mc13xxx, 46, &icid);
        if (ret)
                return ret;
@@ -502,26 +435,23 @@ static int mc13xxx_identify(struct mc13xxx *mc13xxx, enum mc13xxx_id *id)
 
        switch (icid) {
        case 2:
-               *id = MC13XXX_ID_MC13783;
-               name = "mc13783";
+               mc13xxx->ictype = MC13XXX_ID_MC13783;
                break;
        case 7:
-               *id = MC13XXX_ID_MC13892;
-               name = "mc13892";
+               mc13xxx->ictype = MC13XXX_ID_MC13892;
                break;
        default:
-               *id = MC13XXX_ID_INVALID;
+               mc13xxx->ictype = MC13XXX_ID_INVALID;
                break;
        }
 
-       if (*id == MC13XXX_ID_MC13783 || *id == MC13XXX_ID_MC13892) {
+       if (mc13xxx->ictype == MC13XXX_ID_MC13783 ||
+                       mc13xxx->ictype == MC13XXX_ID_MC13892) {
                ret = mc13xxx_reg_read(mc13xxx, MC13XXX_REVISION, &revision);
-               if (ret)
-                       return ret;
 
-               dev_info(&mc13xxx->spidev->dev, "%s: rev: %d.%d, "
+               dev_info(mc13xxx->dev, "%s: rev: %d.%d, "
                                "fin: %d, fab: %d, icid: %d/%d\n",
-                               mc13xxx_chipname[*id],
+                               mc13xxx_chipname[mc13xxx->ictype],
                                maskval(revision, MC13XXX_REVISION_REVFULL),
                                maskval(revision, MC13XXX_REVISION_REVMETAL),
                                maskval(revision, MC13XXX_REVISION_FIN),
@@ -530,26 +460,12 @@ static int mc13xxx_identify(struct mc13xxx *mc13xxx, enum mc13xxx_id *id)
                                maskval(revision, MC13XXX_REVISION_ICIDCODE));
        }
 
-       if (*id != MC13XXX_ID_INVALID) {
-               const struct spi_device_id *devid =
-                       spi_get_device_id(mc13xxx->spidev);
-               if (!devid || devid->driver_data != *id)
-                       dev_warn(&mc13xxx->spidev->dev, "device id doesn't "
-                                       "match auto detection!\n");
-       }
-
-       return 0;
+       return (mc13xxx->ictype == MC13XXX_ID_INVALID) ? -ENODEV : 0;
 }
 
 static const char *mc13xxx_get_chipname(struct mc13xxx *mc13xxx)
 {
-       const struct spi_device_id *devid =
-               spi_get_device_id(mc13xxx->spidev);
-
-       if (!devid)
-               return NULL;
-
-       return mc13xxx_chipname[devid->driver_data];
+       return mc13xxx_chipname[mc13xxx->ictype];
 }
 
 int mc13xxx_get_flags(struct mc13xxx *mc13xxx)
@@ -592,7 +508,7 @@ int mc13xxx_adc_do_conversion(struct mc13xxx *mc13xxx, unsigned int mode,
        };
        init_completion(&adcdone_data.done);
 
-       dev_dbg(&mc13xxx->spidev->dev, "%s\n", __func__);
+       dev_dbg(mc13xxx->dev, "%s\n", __func__);
 
        mc13xxx_lock(mc13xxx);
 
@@ -637,7 +553,8 @@ int mc13xxx_adc_do_conversion(struct mc13xxx *mc13xxx, unsigned int mode,
        adc1 |= ato << MC13783_ADC1_ATO_SHIFT;
        if (atox)
                adc1 |= MC13783_ADC1_ATOX;
-       dev_dbg(&mc13xxx->spidev->dev, "%s: request irq\n", __func__);
+
+       dev_dbg(mc13xxx->dev, "%s: request irq\n", __func__);
        mc13xxx_irq_request(mc13xxx, MC13XXX_IRQ_ADCDONE,
                        mc13xxx_handler_adcdone, __func__, &adcdone_data);
        mc13xxx_irq_ack(mc13xxx, MC13XXX_IRQ_ADCDONE);
@@ -695,7 +612,7 @@ static int mc13xxx_add_subdevice_pdata(struct mc13xxx *mc13xxx,
        if (!cell.name)
                return -ENOMEM;
 
-       return mfd_add_devices(&mc13xxx->spidev->dev, -1, &cell, 1, NULL, 0);
+       return mfd_add_devices(mc13xxx->dev, -1, &cell, 1, NULL, 0);
 }
 
 static int mc13xxx_add_subdevice(struct mc13xxx *mc13xxx, const char *format)
@@ -706,7 +623,7 @@ static int mc13xxx_add_subdevice(struct mc13xxx *mc13xxx, const char *format)
 #ifdef CONFIG_OF
 static int mc13xxx_probe_flags_dt(struct mc13xxx *mc13xxx)
 {
-       struct device_node *np = mc13xxx->spidev->dev.of_node;
+       struct device_node *np = mc13xxx->dev->of_node;
 
        if (!np)
                return -ENODEV;
@@ -732,55 +649,15 @@ static inline int mc13xxx_probe_flags_dt(struct mc13xxx *mc13xxx)
 }
 #endif
 
-static const struct spi_device_id mc13xxx_device_id[] = {
-       {
-               .name = "mc13783",
-               .driver_data = MC13XXX_ID_MC13783,
-       }, {
-               .name = "mc13892",
-               .driver_data = MC13XXX_ID_MC13892,
-       }, {
-               /* sentinel */
-       }
-};
-MODULE_DEVICE_TABLE(spi, mc13xxx_device_id);
-
-static const struct of_device_id mc13xxx_dt_ids[] = {
-       { .compatible = "fsl,mc13783", .data = (void *) MC13XXX_ID_MC13783, },
-       { .compatible = "fsl,mc13892", .data = (void *) MC13XXX_ID_MC13892, },
-       { /* sentinel */ }
-};
-MODULE_DEVICE_TABLE(of, mc13xxx_dt_ids);
-
-static int mc13xxx_probe(struct spi_device *spi)
+int mc13xxx_common_init(struct mc13xxx *mc13xxx,
+               struct mc13xxx_platform_data *pdata, int irq)
 {
-       const struct of_device_id *of_id;
-       struct spi_driver *sdrv = to_spi_driver(spi->dev.driver);
-       struct mc13xxx *mc13xxx;
-       struct mc13xxx_platform_data *pdata = dev_get_platdata(&spi->dev);
-       enum mc13xxx_id id;
        int ret;
 
-       of_id = of_match_device(mc13xxx_dt_ids, &spi->dev);
-       if (of_id)
-               sdrv->id_table = &mc13xxx_device_id[(enum mc13xxx_id) of_id->data];
-
-       mc13xxx = kzalloc(sizeof(*mc13xxx), GFP_KERNEL);
-       if (!mc13xxx)
-               return -ENOMEM;
-
-       dev_set_drvdata(&spi->dev, mc13xxx);
-       spi->mode = SPI_MODE_0 | SPI_CS_HIGH;
-       spi->bits_per_word = 32;
-       spi_setup(spi);
-
-       mc13xxx->spidev = spi;
-
-       mutex_init(&mc13xxx->lock);
        mc13xxx_lock(mc13xxx);
 
-       ret = mc13xxx_identify(mc13xxx, &id);
-       if (ret || id == MC13XXX_ID_INVALID)
+       ret = mc13xxx_identify(mc13xxx);
+       if (ret)
                goto err_revision;
 
        /* mask all irqs */
@@ -792,18 +669,19 @@ static int mc13xxx_probe(struct spi_device *spi)
        if (ret)
                goto err_mask;
 
-       ret = request_threaded_irq(spi->irq, NULL, mc13xxx_irq_thread,
+       ret = request_threaded_irq(irq, NULL, mc13xxx_irq_thread,
                        IRQF_ONESHOT | IRQF_TRIGGER_HIGH, "mc13xxx", mc13xxx);
 
        if (ret) {
 err_mask:
 err_revision:
                mc13xxx_unlock(mc13xxx);
-               dev_set_drvdata(&spi->dev, NULL);
                kfree(mc13xxx);
                return ret;
        }
 
+       mc13xxx->irq = irq;
+
        mc13xxx_unlock(mc13xxx);
 
        if (mc13xxx_probe_flags_dt(mc13xxx) < 0 && pdata)
@@ -838,42 +716,19 @@ err_revision:
 
        return 0;
 }
+EXPORT_SYMBOL_GPL(mc13xxx_common_init);
 
-static int __devexit mc13xxx_remove(struct spi_device *spi)
+void mc13xxx_common_cleanup(struct mc13xxx *mc13xxx)
 {
-       struct mc13xxx *mc13xxx = dev_get_drvdata(&spi->dev);
+       free_irq(mc13xxx->irq, mc13xxx);
 
-       free_irq(mc13xxx->spidev->irq, mc13xxx);
+       mfd_remove_devices(mc13xxx->dev);
 
-       mfd_remove_devices(&spi->dev);
+       regmap_exit(mc13xxx->regmap);
 
        kfree(mc13xxx);
-
-       return 0;
-}
-
-static struct spi_driver mc13xxx_driver = {
-       .id_table = mc13xxx_device_id,
-       .driver = {
-               .name = "mc13xxx",
-               .owner = THIS_MODULE,
-               .of_match_table = mc13xxx_dt_ids,
-       },
-       .probe = mc13xxx_probe,
-       .remove = __devexit_p(mc13xxx_remove),
-};
-
-static int __init mc13xxx_init(void)
-{
-       return spi_register_driver(&mc13xxx_driver);
-}
-subsys_initcall(mc13xxx_init);
-
-static void __exit mc13xxx_exit(void)
-{
-       spi_unregister_driver(&mc13xxx_driver);
 }
-module_exit(mc13xxx_exit);
+EXPORT_SYMBOL_GPL(mc13xxx_common_cleanup);
 
 MODULE_DESCRIPTION("Core driver for Freescale MC13XXX PMIC");
 MODULE_AUTHOR("Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>");
diff --git a/drivers/mfd/mc13xxx-i2c.c b/drivers/mfd/mc13xxx-i2c.c
new file mode 100644 (file)
index 0000000..d22501d
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2009-2010 Creative Product Design
+ * Marc Reilly marc@cpdesign.com.au
+ *
+ * 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/module.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/mc13xxx.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+
+#include "mc13xxx.h"
+
+static const struct i2c_device_id mc13xxx_i2c_device_id[] = {
+       {
+               .name = "mc13892",
+               .driver_data = MC13XXX_ID_MC13892,
+       }, {
+               /* sentinel */
+       }
+};
+MODULE_DEVICE_TABLE(i2c, mc13xxx_i2c_device_id);
+
+static const struct of_device_id mc13xxx_dt_ids[] = {
+       {
+               .compatible = "fsl,mc13892",
+               .data = (void *) &mc13xxx_i2c_device_id[0],
+       }, {
+               /* sentinel */
+       }
+};
+MODULE_DEVICE_TABLE(of, mc13xxx_dt_ids);
+
+static struct regmap_config mc13xxx_regmap_i2c_config = {
+       .reg_bits = 8,
+       .val_bits = 24,
+
+       .max_register = MC13XXX_NUMREGS,
+
+       .cache_type = REGCACHE_NONE,
+};
+
+static int mc13xxx_i2c_probe(struct i2c_client *client,
+               const struct i2c_device_id *id)
+{
+       const struct of_device_id *of_id;
+       struct i2c_driver *idrv = to_i2c_driver(client->dev.driver);
+       struct mc13xxx *mc13xxx;
+       struct mc13xxx_platform_data *pdata = dev_get_platdata(&client->dev);
+       int ret;
+
+       of_id = of_match_device(mc13xxx_dt_ids, &client->dev);
+       if (of_id)
+               idrv->id_table = (const struct i2c_device_id*) of_id->data;
+
+       mc13xxx = kzalloc(sizeof(*mc13xxx), GFP_KERNEL);
+       if (!mc13xxx)
+               return -ENOMEM;
+
+       dev_set_drvdata(&client->dev, mc13xxx);
+
+       mc13xxx->dev = &client->dev;
+       mutex_init(&mc13xxx->lock);
+
+       mc13xxx->regmap = regmap_init_i2c(client, &mc13xxx_regmap_i2c_config);
+       if (IS_ERR(mc13xxx->regmap)) {
+               ret = PTR_ERR(mc13xxx->regmap);
+               dev_err(mc13xxx->dev, "Failed to initialize register map: %d\n",
+                               ret);
+               dev_set_drvdata(&client->dev, NULL);
+               kfree(mc13xxx);
+               return ret;
+       }
+
+       ret = mc13xxx_common_init(mc13xxx, pdata, client->irq);
+
+       if (ret == 0 && (id->driver_data != mc13xxx->ictype))
+               dev_warn(mc13xxx->dev,
+                               "device id doesn't match auto detection!\n");
+
+       return ret;
+}
+
+static int __devexit mc13xxx_i2c_remove(struct i2c_client *client)
+{
+       struct mc13xxx *mc13xxx = dev_get_drvdata(&client->dev);
+
+       mc13xxx_common_cleanup(mc13xxx);
+
+       return 0;
+}
+
+static struct i2c_driver mc13xxx_i2c_driver = {
+       .id_table = mc13xxx_i2c_device_id,
+       .driver = {
+               .owner = THIS_MODULE,
+               .name = "mc13xxx",
+               .of_match_table = mc13xxx_dt_ids,
+       },
+       .probe = mc13xxx_i2c_probe,
+       .remove = __devexit_p(mc13xxx_i2c_remove),
+};
+
+static int __init mc13xxx_i2c_init(void)
+{
+       return i2c_add_driver(&mc13xxx_i2c_driver);
+}
+subsys_initcall(mc13xxx_i2c_init);
+
+static void __exit mc13xxx_i2c_exit(void)
+{
+       i2c_del_driver(&mc13xxx_i2c_driver);
+}
+module_exit(mc13xxx_i2c_exit);
+
+MODULE_DESCRIPTION("i2c driver for Freescale MC13XXX PMIC");
+MODULE_AUTHOR("Marc Reilly <marc@cpdesign.com.au");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/mc13xxx-spi.c b/drivers/mfd/mc13xxx-spi.c
new file mode 100644 (file)
index 0000000..3fcdab3
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2009-2010 Pengutronix
+ * Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>
+ *
+ * loosely based on an earlier driver that has
+ * Copyright 2009 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de>
+ *
+ * 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/module.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/mc13xxx.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/err.h>
+#include <linux/spi/spi.h>
+
+#include "mc13xxx.h"
+
+static const struct spi_device_id mc13xxx_device_id[] = {
+       {
+               .name = "mc13783",
+               .driver_data = MC13XXX_ID_MC13783,
+       }, {
+               .name = "mc13892",
+               .driver_data = MC13XXX_ID_MC13892,
+       }, {
+               /* sentinel */
+       }
+};
+MODULE_DEVICE_TABLE(spi, mc13xxx_device_id);
+
+static const struct of_device_id mc13xxx_dt_ids[] = {
+       { .compatible = "fsl,mc13783", .data = (void *) MC13XXX_ID_MC13783, },
+       { .compatible = "fsl,mc13892", .data = (void *) MC13XXX_ID_MC13892, },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mc13xxx_dt_ids);
+
+static struct regmap_config mc13xxx_regmap_spi_config = {
+       .reg_bits = 7,
+       .pad_bits = 1,
+       .val_bits = 24,
+
+       .max_register = MC13XXX_NUMREGS,
+
+       .cache_type = REGCACHE_NONE,
+};
+
+static int mc13xxx_spi_probe(struct spi_device *spi)
+{
+       const struct of_device_id *of_id;
+       struct spi_driver *sdrv = to_spi_driver(spi->dev.driver);
+       struct mc13xxx *mc13xxx;
+       struct mc13xxx_platform_data *pdata = dev_get_platdata(&spi->dev);
+       int ret;
+
+       of_id = of_match_device(mc13xxx_dt_ids, &spi->dev);
+       if (of_id)
+               sdrv->id_table = &mc13xxx_device_id[(enum mc13xxx_id) of_id->data];
+
+       mc13xxx = kzalloc(sizeof(*mc13xxx), GFP_KERNEL);
+       if (!mc13xxx)
+               return -ENOMEM;
+
+       dev_set_drvdata(&spi->dev, mc13xxx);
+       spi->mode = SPI_MODE_0 | SPI_CS_HIGH;
+       spi->bits_per_word = 32;
+
+       mc13xxx->dev = &spi->dev;
+       mutex_init(&mc13xxx->lock);
+
+       mc13xxx->regmap = regmap_init_spi(spi, &mc13xxx_regmap_spi_config);
+       if (IS_ERR(mc13xxx->regmap)) {
+               ret = PTR_ERR(mc13xxx->regmap);
+               dev_err(mc13xxx->dev, "Failed to initialize register map: %d\n",
+                               ret);
+               dev_set_drvdata(&spi->dev, NULL);
+               kfree(mc13xxx);
+               return ret;
+       }
+
+       ret = mc13xxx_common_init(mc13xxx, pdata, spi->irq);
+
+       if (ret) {
+               dev_set_drvdata(&spi->dev, NULL);
+       } else {
+               const struct spi_device_id *devid =
+                       spi_get_device_id(spi);
+               if (!devid || devid->driver_data != mc13xxx->ictype)
+                       dev_warn(mc13xxx->dev,
+                               "device id doesn't match auto detection!\n");
+       }
+
+       return ret;
+}
+
+static int __devexit mc13xxx_spi_remove(struct spi_device *spi)
+{
+       struct mc13xxx *mc13xxx = dev_get_drvdata(&spi->dev);
+
+       mc13xxx_common_cleanup(mc13xxx);
+
+       return 0;
+}
+
+static struct spi_driver mc13xxx_spi_driver = {
+       .id_table = mc13xxx_device_id,
+       .driver = {
+               .name = "mc13xxx",
+               .owner = THIS_MODULE,
+               .of_match_table = mc13xxx_dt_ids,
+       },
+       .probe = mc13xxx_spi_probe,
+       .remove = __devexit_p(mc13xxx_spi_remove),
+};
+
+static int __init mc13xxx_init(void)
+{
+       return spi_register_driver(&mc13xxx_spi_driver);
+}
+subsys_initcall(mc13xxx_init);
+
+static void __exit mc13xxx_exit(void)
+{
+       spi_unregister_driver(&mc13xxx_spi_driver);
+}
+module_exit(mc13xxx_exit);
+
+MODULE_DESCRIPTION("Core driver for Freescale MC13XXX PMIC");
+MODULE_AUTHOR("Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/mc13xxx.h b/drivers/mfd/mc13xxx.h
new file mode 100644 (file)
index 0000000..bbba06f
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2012 Creative Product Design
+ * Marc Reilly <marc@cpdesign.com.au>
+ *
+ * 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 __DRIVERS_MFD_MC13XXX_H
+#define __DRIVERS_MFD_MC13XXX_H
+
+#include <linux/mutex.h>
+#include <linux/regmap.h>
+#include <linux/mfd/mc13xxx.h>
+
+enum mc13xxx_id {
+       MC13XXX_ID_MC13783,
+       MC13XXX_ID_MC13892,
+       MC13XXX_ID_INVALID,
+};
+
+#define MC13XXX_NUMREGS 0x3f
+
+struct mc13xxx {
+       struct regmap *regmap;
+
+       struct device *dev;
+       enum mc13xxx_id ictype;
+
+       struct mutex lock;
+       int irq;
+       int flags;
+
+       irq_handler_t irqhandler[MC13XXX_NUM_IRQ];
+       void *irqdata[MC13XXX_NUM_IRQ];
+
+       int adcflags;
+};
+
+int mc13xxx_common_init(struct mc13xxx *mc13xxx,
+               struct mc13xxx_platform_data *pdata, int irq);
+
+void mc13xxx_common_cleanup(struct mc13xxx *mc13xxx);
+
+#endif /* __DRIVERS_MFD_MC13XXX_H */
index 189c2f07b83f4331af92528886864e0f5c5fa1d7..29c122bf28ea723e36cafccda86eb09319d595ac 100644 (file)
@@ -204,7 +204,7 @@ static int __devinit pcf50633_probe(struct i2c_client *client,
                return -ENOENT;
        }
 
-       pcf = kzalloc(sizeof(*pcf), GFP_KERNEL);
+       pcf = devm_kzalloc(&client->dev, sizeof(*pcf), GFP_KERNEL);
        if (!pcf)
                return -ENOMEM;
 
@@ -212,12 +212,11 @@ static int __devinit pcf50633_probe(struct i2c_client *client,
 
        mutex_init(&pcf->lock);
 
-       pcf->regmap = regmap_init_i2c(client, &pcf50633_regmap_config);
+       pcf->regmap = devm_regmap_init_i2c(client, &pcf50633_regmap_config);
        if (IS_ERR(pcf->regmap)) {
                ret = PTR_ERR(pcf->regmap);
-               dev_err(pcf->dev, "Failed to allocate register map: %d\n",
-                       ret);
-               goto err_free;
+               dev_err(pcf->dev, "Failed to allocate register map: %d\n", ret);
+               return ret;
        }
 
        i2c_set_clientdata(client, pcf);
@@ -228,7 +227,7 @@ static int __devinit pcf50633_probe(struct i2c_client *client,
        if (version < 0 || variant < 0) {
                dev_err(pcf->dev, "Unable to probe pcf50633\n");
                ret = -ENODEV;
-               goto err_regmap;
+               return ret;
        }
 
        dev_info(pcf->dev, "Probed device version %d variant %d\n",
@@ -237,16 +236,11 @@ static int __devinit pcf50633_probe(struct i2c_client *client,
        pcf50633_irq_init(pcf, client->irq);
 
        /* Create sub devices */
-       pcf50633_client_dev_register(pcf, "pcf50633-input",
-                                               &pcf->input_pdev);
-       pcf50633_client_dev_register(pcf, "pcf50633-rtc",
-                                               &pcf->rtc_pdev);
-       pcf50633_client_dev_register(pcf, "pcf50633-mbc",
-                                               &pcf->mbc_pdev);
-       pcf50633_client_dev_register(pcf, "pcf50633-adc",
-                                               &pcf->adc_pdev);
-       pcf50633_client_dev_register(pcf, "pcf50633-backlight",
-                                               &pcf->bl_pdev);
+       pcf50633_client_dev_register(pcf, "pcf50633-input", &pcf->input_pdev);
+       pcf50633_client_dev_register(pcf, "pcf50633-rtc", &pcf->rtc_pdev);
+       pcf50633_client_dev_register(pcf, "pcf50633-mbc", &pcf->mbc_pdev);
+       pcf50633_client_dev_register(pcf, "pcf50633-adc", &pcf->adc_pdev);
+       pcf50633_client_dev_register(pcf, "pcf50633-backlight", &pcf->bl_pdev);
 
 
        for (i = 0; i < PCF50633_NUM_REGULATORS; i++) {
@@ -274,13 +268,6 @@ static int __devinit pcf50633_probe(struct i2c_client *client,
                pdata->probe_done(pcf);
 
        return 0;
-
-err_regmap:
-       regmap_exit(pcf->regmap);
-err_free:
-       kfree(pcf);
-
-       return ret;
 }
 
 static int __devexit pcf50633_remove(struct i2c_client *client)
@@ -300,9 +287,6 @@ static int __devexit pcf50633_remove(struct i2c_client *client)
        for (i = 0; i < PCF50633_NUM_REGULATORS; i++)
                platform_device_unregister(pcf->regulator_pdev[i]);
 
-       regmap_exit(pcf->regmap);
-       kfree(pcf);
-
        return 0;
 }
 
index 44afae0a69ce75a5fada79d48acae246db1dfdb0..cdc1df7fa0e94d10a26059c18dd347c045dcf2cd 100644 (file)
@@ -75,6 +75,7 @@ static struct deepsleep_control_data deepsleep_data[] = {
        (RC5T583_EXT_PWRREQ1_CONTROL | RC5T583_EXT_PWRREQ2_CONTROL)
 
 static struct mfd_cell rc5t583_subdevs[] = {
+       {.name = "rc5t583-gpio",},
        {.name = "rc5t583-regulator",},
        {.name = "rc5t583-rtc",      },
        {.name = "rc5t583-key",      }
@@ -267,7 +268,7 @@ static int __devinit rc5t583_i2c_probe(struct i2c_client *i2c,
        rc5t583->dev = &i2c->dev;
        i2c_set_clientdata(i2c, rc5t583);
 
-       rc5t583->regmap = regmap_init_i2c(i2c, &rc5t583_regmap_config);
+       rc5t583->regmap = devm_regmap_init_i2c(i2c, &rc5t583_regmap_config);
        if (IS_ERR(rc5t583->regmap)) {
                ret = PTR_ERR(rc5t583->regmap);
                dev_err(&i2c->dev, "regmap initialization failed: %d\n", ret);
@@ -276,7 +277,7 @@ static int __devinit rc5t583_i2c_probe(struct i2c_client *i2c,
 
        ret = rc5t583_clear_ext_power_req(rc5t583, pdata);
        if (ret < 0)
-               goto err_irq_init;
+               return ret;
 
        if (i2c->irq) {
                ret = rc5t583_irq_init(rc5t583, i2c->irq, pdata->irq_base);
@@ -299,8 +300,6 @@ static int __devinit rc5t583_i2c_probe(struct i2c_client *i2c,
 err_add_devs:
        if (irq_init_success)
                rc5t583_irq_exit(rc5t583);
-err_irq_init:
-       regmap_exit(rc5t583->regmap);
        return ret;
 }
 
@@ -310,7 +309,6 @@ static int  __devexit rc5t583_i2c_remove(struct i2c_client *i2c)
 
        mfd_remove_devices(rc5t583->dev);
        rc5t583_irq_exit(rc5t583);
-       regmap_exit(rc5t583->regmap);
        return 0;
 }
 
index 809bd4a610895c75baf469ceb1d3bec417aa64c8..685d61e431adfa4f8b733a13bd5c93cfb71c9a44 100644 (file)
@@ -108,18 +108,7 @@ static struct pci_driver rdc321x_sb_driver = {
        .remove         = __devexit_p(rdc321x_sb_remove),
 };
 
-static int __init rdc321x_sb_init(void)
-{
-       return pci_register_driver(&rdc321x_sb_driver);
-}
-
-static void __exit rdc321x_sb_exit(void)
-{
-       pci_unregister_driver(&rdc321x_sb_driver);
-}
-
-module_init(rdc321x_sb_init);
-module_exit(rdc321x_sb_exit);
+module_pci_driver(rdc321x_sb_driver);
 
 MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>");
 MODULE_LICENSE("GPL");
index 48949d998d105f62172dae5697d3e4e3dd99819a..dd170307e60e7a2642c825df88f28fdd7d568d51 100644 (file)
@@ -114,12 +114,12 @@ static int s5m87xx_i2c_probe(struct i2c_client *i2c,
                s5m87xx->wakeup = pdata->wakeup;
        }
 
-       s5m87xx->regmap = regmap_init_i2c(i2c, &s5m_regmap_config);
+       s5m87xx->regmap = devm_regmap_init_i2c(i2c, &s5m_regmap_config);
        if (IS_ERR(s5m87xx->regmap)) {
                ret = PTR_ERR(s5m87xx->regmap);
                dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
                        ret);
-               goto err;
+               return ret;
        }
 
        s5m87xx->rtc = i2c_new_dummy(i2c->adapter, RTC_I2C_ADDR);
@@ -159,7 +159,6 @@ err:
        mfd_remove_devices(s5m87xx->dev);
        s5m_irq_exit(s5m87xx);
        i2c_unregister_device(s5m87xx->rtc);
-       regmap_exit(s5m87xx->regmap);
        return ret;
 }
 
@@ -170,7 +169,6 @@ static int s5m87xx_i2c_remove(struct i2c_client *i2c)
        mfd_remove_devices(s5m87xx->dev);
        s5m_irq_exit(s5m87xx);
        i2c_unregister_device(s5m87xx->rtc);
-       regmap_exit(s5m87xx->regmap);
        return 0;
 }
 
diff --git a/drivers/mfd/sta2x11-mfd.c b/drivers/mfd/sta2x11-mfd.c
new file mode 100644 (file)
index 0000000..d31fed0
--- /dev/null
@@ -0,0 +1,467 @@
+/*
+ * Copyright (c) 2009-2011 Wind River Systems, Inc.
+ * Copyright (c) 2011 ST Microelectronics (Alessandro Rubini)
+ *
+ * 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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/errno.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/sta2x11-mfd.h>
+
+#include <asm/sta2x11.h>
+
+/* This describes STA2X11 MFD chip for us, we may have several */
+struct sta2x11_mfd {
+       struct sta2x11_instance *instance;
+       spinlock_t lock;
+       struct list_head list;
+       void __iomem *sctl_regs;
+       void __iomem *apbreg_regs;
+};
+
+static LIST_HEAD(sta2x11_mfd_list);
+
+/* Three functions to act on the list */
+static struct sta2x11_mfd *sta2x11_mfd_find(struct pci_dev *pdev)
+{
+       struct sta2x11_instance *instance;
+       struct sta2x11_mfd *mfd;
+
+       if (!pdev && !list_empty(&sta2x11_mfd_list)) {
+               pr_warning("%s: Unspecified device, "
+                           "using first instance\n", __func__);
+               return list_entry(sta2x11_mfd_list.next,
+                                 struct sta2x11_mfd, list);
+       }
+
+       instance = sta2x11_get_instance(pdev);
+       if (!instance)
+               return NULL;
+       list_for_each_entry(mfd, &sta2x11_mfd_list, list) {
+               if (mfd->instance == instance)
+                       return mfd;
+       }
+       return NULL;
+}
+
+static int __devinit sta2x11_mfd_add(struct pci_dev *pdev, gfp_t flags)
+{
+       struct sta2x11_mfd *mfd = sta2x11_mfd_find(pdev);
+       struct sta2x11_instance *instance;
+
+       if (mfd)
+               return -EBUSY;
+       instance = sta2x11_get_instance(pdev);
+       if (!instance)
+               return -EINVAL;
+       mfd = kzalloc(sizeof(*mfd), flags);
+       if (!mfd)
+               return -ENOMEM;
+       INIT_LIST_HEAD(&mfd->list);
+       spin_lock_init(&mfd->lock);
+       mfd->instance = instance;
+       list_add(&mfd->list, &sta2x11_mfd_list);
+       return 0;
+}
+
+static int __devexit mfd_remove(struct pci_dev *pdev)
+{
+       struct sta2x11_mfd *mfd = sta2x11_mfd_find(pdev);
+
+       if (!mfd)
+               return -ENODEV;
+       list_del(&mfd->list);
+       kfree(mfd);
+       return 0;
+}
+
+/* These two functions are exported and are not expected to fail */
+u32 sta2x11_sctl_mask(struct pci_dev *pdev, u32 reg, u32 mask, u32 val)
+{
+       struct sta2x11_mfd *mfd = sta2x11_mfd_find(pdev);
+       u32 r;
+       unsigned long flags;
+
+       if (!mfd) {
+               dev_warn(&pdev->dev, ": can't access sctl regs\n");
+               return 0;
+       }
+       if (!mfd->sctl_regs) {
+               dev_warn(&pdev->dev, ": system ctl not initialized\n");
+               return 0;
+       }
+       spin_lock_irqsave(&mfd->lock, flags);
+       r = readl(mfd->sctl_regs + reg);
+       r &= ~mask;
+       r |= val;
+       if (mask)
+               writel(r, mfd->sctl_regs + reg);
+       spin_unlock_irqrestore(&mfd->lock, flags);
+       return r;
+}
+EXPORT_SYMBOL(sta2x11_sctl_mask);
+
+u32 sta2x11_apbreg_mask(struct pci_dev *pdev, u32 reg, u32 mask, u32 val)
+{
+       struct sta2x11_mfd *mfd = sta2x11_mfd_find(pdev);
+       u32 r;
+       unsigned long flags;
+
+       if (!mfd) {
+               dev_warn(&pdev->dev, ": can't access apb regs\n");
+               return 0;
+       }
+       if (!mfd->apbreg_regs) {
+               dev_warn(&pdev->dev, ": apb bridge not initialized\n");
+               return 0;
+       }
+       spin_lock_irqsave(&mfd->lock, flags);
+       r = readl(mfd->apbreg_regs + reg);
+       r &= ~mask;
+       r |= val;
+       if (mask)
+               writel(r, mfd->apbreg_regs + reg);
+       spin_unlock_irqrestore(&mfd->lock, flags);
+       return r;
+}
+EXPORT_SYMBOL(sta2x11_apbreg_mask);
+
+/* Two debugfs files, for our registers (FIXME: one instance only) */
+#define REG(regname) {.name = #regname, .offset = SCTL_ ## regname}
+static struct debugfs_reg32 sta2x11_sctl_regs[] = {
+       REG(SCCTL), REG(ARMCFG), REG(SCPLLCTL), REG(SCPLLFCTRL),
+       REG(SCRESFRACT), REG(SCRESCTRL1), REG(SCRESXTRL2), REG(SCPEREN0),
+       REG(SCPEREN1), REG(SCPEREN2), REG(SCGRST), REG(SCPCIPMCR1),
+       REG(SCPCIPMCR2), REG(SCPCIPMSR1), REG(SCPCIPMSR2), REG(SCPCIPMSR3),
+       REG(SCINTREN), REG(SCRISR), REG(SCCLKSTAT0), REG(SCCLKSTAT1),
+       REG(SCCLKSTAT2), REG(SCRSTSTA),
+};
+#undef REG
+
+static struct debugfs_regset32 sctl_regset = {
+       .regs = sta2x11_sctl_regs,
+       .nregs = ARRAY_SIZE(sta2x11_sctl_regs),
+};
+
+#define REG(regname) {.name = #regname, .offset = regname}
+static struct debugfs_reg32 sta2x11_apbreg_regs[] = {
+       REG(APBREG_BSR), REG(APBREG_PAER), REG(APBREG_PWAC), REG(APBREG_PRAC),
+       REG(APBREG_PCG), REG(APBREG_PUR), REG(APBREG_EMU_PCG),
+};
+#undef REG
+
+static struct debugfs_regset32 apbreg_regset = {
+       .regs = sta2x11_apbreg_regs,
+       .nregs = ARRAY_SIZE(sta2x11_apbreg_regs),
+};
+
+static struct dentry *sta2x11_sctl_debugfs;
+static struct dentry *sta2x11_apbreg_debugfs;
+
+/* Probe for the two platform devices */
+static int sta2x11_sctl_probe(struct platform_device *dev)
+{
+       struct pci_dev **pdev;
+       struct sta2x11_mfd *mfd;
+       struct resource *res;
+
+       pdev = dev->dev.platform_data;
+       mfd = sta2x11_mfd_find(*pdev);
+       if (!mfd)
+               return -ENODEV;
+
+       res = platform_get_resource(dev, IORESOURCE_MEM, 0);
+       if (!res)
+               return -ENOMEM;
+
+       if (!request_mem_region(res->start, resource_size(res),
+                               "sta2x11-sctl"))
+               return -EBUSY;
+
+       mfd->sctl_regs = ioremap(res->start, resource_size(res));
+       if (!mfd->sctl_regs) {
+               release_mem_region(res->start, resource_size(res));
+               return -ENOMEM;
+       }
+       sctl_regset.base = mfd->sctl_regs;
+       sta2x11_sctl_debugfs = debugfs_create_regset32("sta2x11-sctl",
+                                                 S_IFREG | S_IRUGO,
+                                                 NULL, &sctl_regset);
+       return 0;
+}
+
+static int sta2x11_apbreg_probe(struct platform_device *dev)
+{
+       struct pci_dev **pdev;
+       struct sta2x11_mfd *mfd;
+       struct resource *res;
+
+       pdev = dev->dev.platform_data;
+       dev_dbg(&dev->dev, "%s: pdata is %p\n", __func__, pdev);
+       dev_dbg(&dev->dev, "%s: *pdata is %p\n", __func__, *pdev);
+
+       mfd = sta2x11_mfd_find(*pdev);
+       if (!mfd)
+               return -ENODEV;
+
+       res = platform_get_resource(dev, IORESOURCE_MEM, 0);
+       if (!res)
+               return -ENOMEM;
+
+       if (!request_mem_region(res->start, resource_size(res),
+                               "sta2x11-apbreg"))
+               return -EBUSY;
+
+       mfd->apbreg_regs = ioremap(res->start, resource_size(res));
+       if (!mfd->apbreg_regs) {
+               release_mem_region(res->start, resource_size(res));
+               return -ENOMEM;
+       }
+       dev_dbg(&dev->dev, "%s: regbase %p\n", __func__, mfd->apbreg_regs);
+
+       apbreg_regset.base = mfd->apbreg_regs;
+       sta2x11_apbreg_debugfs = debugfs_create_regset32("sta2x11-apbreg",
+                                                 S_IFREG | S_IRUGO,
+                                                 NULL, &apbreg_regset);
+       return 0;
+}
+
+/* The two platform drivers */
+static struct platform_driver sta2x11_sctl_platform_driver = {
+       .driver = {
+               .name   = "sta2x11-sctl",
+               .owner  = THIS_MODULE,
+       },
+       .probe          = sta2x11_sctl_probe,
+};
+
+static int __init sta2x11_sctl_init(void)
+{
+       pr_info("%s\n", __func__);
+       return platform_driver_register(&sta2x11_sctl_platform_driver);
+}
+
+static struct platform_driver sta2x11_platform_driver = {
+       .driver = {
+               .name   = "sta2x11-apbreg",
+               .owner  = THIS_MODULE,
+       },
+       .probe          = sta2x11_apbreg_probe,
+};
+
+static int __init sta2x11_apbreg_init(void)
+{
+       pr_info("%s\n", __func__);
+       return platform_driver_register(&sta2x11_platform_driver);
+}
+
+/*
+ * What follows is the PCI device that hosts the above two pdevs.
+ * Each logic block is 4kB and they are all consecutive: we use this info.
+ */
+
+/* Bar 0 */
+enum bar0_cells {
+       STA2X11_GPIO_0 = 0,
+       STA2X11_GPIO_1,
+       STA2X11_GPIO_2,
+       STA2X11_GPIO_3,
+       STA2X11_SCTL,
+       STA2X11_SCR,
+       STA2X11_TIME,
+};
+/* Bar 1 */
+enum bar1_cells {
+       STA2X11_APBREG = 0,
+};
+#define CELL_4K(_name, _cell) { \
+               .name = _name, \
+               .start = _cell * 4096, .end = _cell * 4096 + 4095, \
+               .flags = IORESOURCE_MEM, \
+               }
+
+static const __devinitconst struct resource gpio_resources[] = {
+       {
+               .name = "sta2x11_gpio", /* 4 consecutive cells, 1 driver */
+               .start = 0,
+               .end = (4 * 4096) - 1,
+               .flags = IORESOURCE_MEM,
+       }
+};
+static const __devinitconst struct resource sctl_resources[] = {
+       CELL_4K("sta2x11-sctl", STA2X11_SCTL),
+};
+static const __devinitconst struct resource scr_resources[] = {
+       CELL_4K("sta2x11-scr", STA2X11_SCR),
+};
+static const __devinitconst struct resource time_resources[] = {
+       CELL_4K("sta2x11-time", STA2X11_TIME),
+};
+
+static const __devinitconst struct resource apbreg_resources[] = {
+       CELL_4K("sta2x11-apbreg", STA2X11_APBREG),
+};
+
+#define DEV(_name, _r) \
+       { .name = _name, .num_resources = ARRAY_SIZE(_r), .resources = _r, }
+
+static __devinitdata struct mfd_cell sta2x11_mfd_bar0[] = {
+       DEV("sta2x11-gpio", gpio_resources), /* offset 0: we add pdata later */
+       DEV("sta2x11-sctl", sctl_resources),
+       DEV("sta2x11-scr", scr_resources),
+       DEV("sta2x11-time", time_resources),
+};
+
+static __devinitdata struct mfd_cell sta2x11_mfd_bar1[] = {
+       DEV("sta2x11-apbreg", apbreg_resources),
+};
+
+static int sta2x11_mfd_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+       pci_save_state(pdev);
+       pci_disable_device(pdev);
+       pci_set_power_state(pdev, pci_choose_state(pdev, state));
+
+       return 0;
+}
+
+static int sta2x11_mfd_resume(struct pci_dev *pdev)
+{
+       int err;
+
+       pci_set_power_state(pdev, 0);
+       err = pci_enable_device(pdev);
+       if (err)
+               return err;
+       pci_restore_state(pdev);
+
+       return 0;
+}
+
+static int __devinit sta2x11_mfd_probe(struct pci_dev *pdev,
+                                      const struct pci_device_id *pci_id)
+{
+       int err, i;
+       struct sta2x11_gpio_pdata *gpio_data;
+
+       dev_info(&pdev->dev, "%s\n", __func__);
+
+       err = pci_enable_device(pdev);
+       if (err) {
+               dev_err(&pdev->dev, "Can't enable device.\n");
+               return err;
+       }
+
+       err = pci_enable_msi(pdev);
+       if (err)
+               dev_info(&pdev->dev, "Enable msi failed\n");
+
+       /* Read gpio config data as pci device's platform data */
+       gpio_data = dev_get_platdata(&pdev->dev);
+       if (!gpio_data)
+               dev_warn(&pdev->dev, "no gpio configuration\n");
+
+       dev_dbg(&pdev->dev, "%s, gpio_data = %p (%p)\n", __func__,
+               gpio_data, &gpio_data);
+       dev_dbg(&pdev->dev, "%s, pdev = %p (%p)\n", __func__,
+               pdev, &pdev);
+
+       /* platform data is the pci device for all of them */
+       for (i = 0; i < ARRAY_SIZE(sta2x11_mfd_bar0); i++) {
+               sta2x11_mfd_bar0[i].pdata_size = sizeof(pdev);
+               sta2x11_mfd_bar0[i].platform_data = &pdev;
+       }
+       sta2x11_mfd_bar1[0].pdata_size = sizeof(pdev);
+       sta2x11_mfd_bar1[0].platform_data = &pdev;
+
+       /* Record this pdev before mfd_add_devices: their probe looks for it */
+       sta2x11_mfd_add(pdev, GFP_ATOMIC);
+
+
+       err = mfd_add_devices(&pdev->dev, -1,
+                             sta2x11_mfd_bar0,
+                             ARRAY_SIZE(sta2x11_mfd_bar0),
+                             &pdev->resource[0],
+                             0);
+       if (err) {
+               dev_err(&pdev->dev, "mfd_add_devices[0] failed: %d\n", err);
+               goto err_disable;
+       }
+
+       err = mfd_add_devices(&pdev->dev, -1,
+                             sta2x11_mfd_bar1,
+                             ARRAY_SIZE(sta2x11_mfd_bar1),
+                             &pdev->resource[1],
+                             0);
+       if (err) {
+               dev_err(&pdev->dev, "mfd_add_devices[1] failed: %d\n", err);
+               goto err_disable;
+       }
+
+       return 0;
+
+err_disable:
+       mfd_remove_devices(&pdev->dev);
+       pci_disable_device(pdev);
+       pci_disable_msi(pdev);
+       return err;
+}
+
+static DEFINE_PCI_DEVICE_TABLE(sta2x11_mfd_tbl) = {
+       {PCI_DEVICE(PCI_VENDOR_ID_STMICRO, PCI_DEVICE_ID_STMICRO_GPIO)},
+       {0,},
+};
+
+static struct pci_driver sta2x11_mfd_driver = {
+       .name =         "sta2x11-mfd",
+       .id_table =     sta2x11_mfd_tbl,
+       .probe =        sta2x11_mfd_probe,
+       .suspend =      sta2x11_mfd_suspend,
+       .resume =       sta2x11_mfd_resume,
+};
+
+static int __init sta2x11_mfd_init(void)
+{
+       pr_info("%s\n", __func__);
+       return pci_register_driver(&sta2x11_mfd_driver);
+}
+
+/*
+ * All of this must be ready before "normal" devices like MMCI appear.
+ * But MFD (the pci device) can't be too early. The following choice
+ * prepares platform drivers very early and probe the PCI device later,
+ * but before other PCI devices.
+ */
+subsys_initcall(sta2x11_apbreg_init);
+subsys_initcall(sta2x11_sctl_init);
+rootfs_initcall(sta2x11_mfd_init);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Wind River");
+MODULE_DESCRIPTION("STA2x11 mfd for GPIO, SCTL and APBREG");
+MODULE_DEVICE_TABLE(pci, sta2x11_mfd_tbl);
index b58c43c7ea93bf8f913f62a0774eb109175ebc45..afd459013ecbb1e89485cf37f643f6d7d3c91a20 100644 (file)
@@ -122,7 +122,6 @@ MODULE_DEVICE_TABLE(spi, stmpe_id);
 static struct spi_driver stmpe_spi_driver = {
        .driver = {
                .name   = "stmpe-spi",
-               .bus    = &spi_bus_type,
                .owner  = THIS_MODULE,
 #ifdef CONFIG_PM
                .pm     = &stmpe_dev_pm_ops,
index 47f802bf184818c81d9d0c9baeba9225ec63a603..396b9d1b6bd68bc80ee0120693949461688b8b9f 100644 (file)
@@ -283,27 +283,24 @@ static int __devinit tps65090_i2c_probe(struct i2c_client *client,
                }
        }
 
-       tps65090->rmap = regmap_init_i2c(tps65090->client,
-               &tps65090_regmap_config);
+       tps65090->rmap = devm_regmap_init_i2c(tps65090->client,
+                                             &tps65090_regmap_config);
        if (IS_ERR(tps65090->rmap)) {
-               dev_err(&client->dev, "regmap_init failed with err: %ld\n",
-                       PTR_ERR(tps65090->rmap));
+               ret = PTR_ERR(tps65090->rmap);
+               dev_err(&client->dev, "regmap_init failed with err: %d\n", ret);
                goto err_irq_exit;
-       };
+       }
 
        ret = mfd_add_devices(tps65090->dev, -1, tps65090s,
                ARRAY_SIZE(tps65090s), NULL, 0);
        if (ret) {
                dev_err(&client->dev, "add mfd devices failed with err: %d\n",
                        ret);
-               goto err_regmap_exit;
+               goto err_irq_exit;
        }
 
        return 0;
 
-err_regmap_exit:
-       regmap_exit(tps65090->rmap);
-
 err_irq_exit:
        if (client->irq)
                free_irq(client->irq, tps65090);
@@ -316,29 +313,34 @@ static int __devexit tps65090_i2c_remove(struct i2c_client *client)
        struct tps65090 *tps65090 = i2c_get_clientdata(client);
 
        mfd_remove_devices(tps65090->dev);
-       regmap_exit(tps65090->rmap);
        if (client->irq)
                free_irq(client->irq, tps65090);
 
        return 0;
 }
 
-#ifdef CONFIG_PM
-static int tps65090_i2c_suspend(struct i2c_client *client, pm_message_t state)
+#ifdef CONFIG_PM_SLEEP
+static int tps65090_suspend(struct device *dev)
 {
+       struct i2c_client *client = to_i2c_client(dev);
        if (client->irq)
                disable_irq(client->irq);
        return 0;
 }
 
-static int tps65090_i2c_resume(struct i2c_client *client)
+static int tps65090_resume(struct device *dev)
 {
+       struct i2c_client *client = to_i2c_client(dev);
        if (client->irq)
                enable_irq(client->irq);
        return 0;
 }
 #endif
 
+static const struct dev_pm_ops tps65090_pm_ops = {
+       SET_SYSTEM_SLEEP_PM_OPS(tps65090_suspend, tps65090_resume)
+};
+
 static const struct i2c_device_id tps65090_id_table[] = {
        { "tps65090", 0 },
        { },
@@ -349,13 +351,10 @@ static struct i2c_driver tps65090_driver = {
        .driver = {
                .name   = "tps65090",
                .owner  = THIS_MODULE,
+               .pm     = &tps65090_pm_ops,
        },
        .probe          = tps65090_i2c_probe,
        .remove         = __devexit_p(tps65090_i2c_remove),
-#ifdef CONFIG_PM
-       .suspend        = tps65090_i2c_suspend,
-       .resume         = tps65090_i2c_resume,
-#endif
        .id_table       = tps65090_id_table,
 };
 
index f7d854e4cc62dd1df9728d3b0a3a6366aa12dc75..db194e433c085358b8d4716f062af3700bba1956 100644 (file)
@@ -96,7 +96,7 @@ EXPORT_SYMBOL_GPL(tps65217_reg_write);
  * @val: Value to write.
  * @level: Password protected level
  */
-int tps65217_update_bits(struct tps65217 *tps, unsigned int reg,
+static int tps65217_update_bits(struct tps65217 *tps, unsigned int reg,
                unsigned int mask, unsigned int val, unsigned int level)
 {
        int ret;
@@ -150,7 +150,7 @@ static int __devinit tps65217_probe(struct i2c_client *client,
                return -ENOMEM;
 
        tps->pdata = pdata;
-       tps->regmap = regmap_init_i2c(client, &tps65217_regmap_config);
+       tps->regmap = devm_regmap_init_i2c(client, &tps65217_regmap_config);
        if (IS_ERR(tps->regmap)) {
                ret = PTR_ERR(tps->regmap);
                dev_err(tps->dev, "Failed to allocate register map: %d\n",
@@ -163,9 +163,9 @@ static int __devinit tps65217_probe(struct i2c_client *client,
 
        ret = tps65217_reg_read(tps, TPS65217_REG_CHIPID, &version);
        if (ret < 0) {
-               dev_err(tps->dev, "Failed to read revision"
-                                       " register: %d\n", ret);
-               goto err_regmap;
+               dev_err(tps->dev, "Failed to read revision register: %d\n",
+                       ret);
+               return ret;
        }
 
        dev_info(tps->dev, "TPS65217 ID %#x version 1.%d\n",
@@ -190,11 +190,6 @@ static int __devinit tps65217_probe(struct i2c_client *client,
        }
 
        return 0;
-
-err_regmap:
-       regmap_exit(tps->regmap);
-
-       return ret;
 }
 
 static int __devexit tps65217_remove(struct i2c_client *client)
@@ -205,8 +200,6 @@ static int __devexit tps65217_remove(struct i2c_client *client)
        for (i = 0; i < TPS65217_NUM_REGULATOR; i++)
                platform_device_unregister(tps->regulator_pdev[i]);
 
-       regmap_exit(tps->regmap);
-
        return 0;
 }
 
index c9ed5c00a6211bb2349858ef76161eb880e45396..09aab3e4776dac883e67e4dcb71bffcc4eddb8ec 100644 (file)
 #include <linux/device.h>
 #include <linux/interrupt.h>
 #include <linux/irq.h>
+#include <linux/irqdomain.h>
 #include <linux/gpio.h>
 #include <linux/mfd/tps65910.h>
 
-static inline int irq_to_tps65910_irq(struct tps65910 *tps65910,
-                                                       int irq)
-{
-       return (irq - tps65910->irq_base);
-}
-
 /*
  * This is a threaded IRQ handler so can access I2C/SPI.  Since all
  * interrupts are clear on read the IRQ line will be reasserted and
@@ -41,28 +36,28 @@ static inline int irq_to_tps65910_irq(struct tps65910 *tps65910,
 static irqreturn_t tps65910_irq(int irq, void *irq_data)
 {
        struct tps65910 *tps65910 = irq_data;
+       unsigned int reg;
        u32 irq_sts;
        u32 irq_mask;
-       u8 reg;
        int i;
 
-       tps65910->read(tps65910, TPS65910_INT_STS, 1, &reg);
+       tps65910_reg_read(tps65910, TPS65910_INT_STS, &reg);
        irq_sts = reg;
-       tps65910->read(tps65910, TPS65910_INT_STS2, 1, &reg);
+       tps65910_reg_read(tps65910, TPS65910_INT_STS2, &reg);
        irq_sts |= reg << 8;
        switch (tps65910_chip_id(tps65910)) {
        case TPS65911:
-               tps65910->read(tps65910, TPS65910_INT_STS3, 1, &reg);
+               tps65910_reg_read(tps65910, TPS65910_INT_STS3, &reg);
                irq_sts |= reg << 16;
        }
 
-       tps65910->read(tps65910, TPS65910_INT_MSK, 1, &reg);
+       tps65910_reg_read(tps65910, TPS65910_INT_MSK, &reg);
        irq_mask = reg;
-       tps65910->read(tps65910, TPS65910_INT_MSK2, 1, &reg);
+       tps65910_reg_read(tps65910, TPS65910_INT_MSK2, &reg);
        irq_mask |= reg << 8;
        switch (tps65910_chip_id(tps65910)) {
        case TPS65911:
-               tps65910->read(tps65910, TPS65910_INT_MSK3, 1, &reg);
+               tps65910_reg_read(tps65910, TPS65910_INT_MSK3, &reg);
                irq_mask |= reg << 16;
        }
 
@@ -76,19 +71,19 @@ static irqreturn_t tps65910_irq(int irq, void *irq_data)
                if (!(irq_sts & (1 << i)))
                        continue;
 
-               handle_nested_irq(tps65910->irq_base + i);
+               handle_nested_irq(irq_find_mapping(tps65910->domain, i));
        }
 
        /* Write the STS register back to clear IRQs we handled */
        reg = irq_sts & 0xFF;
        irq_sts >>= 8;
-       tps65910->write(tps65910, TPS65910_INT_STS, 1, &reg);
+       tps65910_reg_write(tps65910, TPS65910_INT_STS, reg);
        reg = irq_sts & 0xFF;
-       tps65910->write(tps65910, TPS65910_INT_STS2, 1, &reg);
+       tps65910_reg_write(tps65910, TPS65910_INT_STS2, reg);
        switch (tps65910_chip_id(tps65910)) {
        case TPS65911:
                reg = irq_sts >> 8;
-               tps65910->write(tps65910, TPS65910_INT_STS3, 1, &reg);
+               tps65910_reg_write(tps65910, TPS65910_INT_STS3, reg);
        }
 
        return IRQ_HANDLED;
@@ -105,27 +100,27 @@ static void tps65910_irq_sync_unlock(struct irq_data *data)
 {
        struct tps65910 *tps65910 = irq_data_get_irq_chip_data(data);
        u32 reg_mask;
-       u8 reg;
+       unsigned int reg;
 
-       tps65910->read(tps65910, TPS65910_INT_MSK, 1, &reg);
+       tps65910_reg_read(tps65910, TPS65910_INT_MSK, &reg);
        reg_mask = reg;
-       tps65910->read(tps65910, TPS65910_INT_MSK2, 1, &reg);
+       tps65910_reg_read(tps65910, TPS65910_INT_MSK2, &reg);
        reg_mask |= reg << 8;
        switch (tps65910_chip_id(tps65910)) {
        case TPS65911:
-               tps65910->read(tps65910, TPS65910_INT_MSK3, 1, &reg);
+               tps65910_reg_read(tps65910, TPS65910_INT_MSK3, &reg);
                reg_mask |= reg << 16;
        }
 
        if (tps65910->irq_mask != reg_mask) {
                reg = tps65910->irq_mask & 0xFF;
-               tps65910->write(tps65910, TPS65910_INT_MSK, 1, &reg);
+               tps65910_reg_write(tps65910, TPS65910_INT_MSK, reg);
                reg = tps65910->irq_mask >> 8 & 0xFF;
-               tps65910->write(tps65910, TPS65910_INT_MSK2, 1, &reg);
+               tps65910_reg_write(tps65910, TPS65910_INT_MSK2, reg);
                switch (tps65910_chip_id(tps65910)) {
                case TPS65911:
                        reg = tps65910->irq_mask >> 16;
-                       tps65910->write(tps65910, TPS65910_INT_MSK3, 1, &reg);
+                       tps65910_reg_write(tps65910, TPS65910_INT_MSK3, reg);
                }
        }
        mutex_unlock(&tps65910->irq_lock);
@@ -135,14 +130,14 @@ static void tps65910_irq_enable(struct irq_data *data)
 {
        struct tps65910 *tps65910 = irq_data_get_irq_chip_data(data);
 
-       tps65910->irq_mask &= ~( 1 << irq_to_tps65910_irq(tps65910, data->irq));
+       tps65910->irq_mask &= ~(1 << data->hwirq);
 }
 
 static void tps65910_irq_disable(struct irq_data *data)
 {
        struct tps65910 *tps65910 = irq_data_get_irq_chip_data(data);
 
-       tps65910->irq_mask |= ( 1 << irq_to_tps65910_irq(tps65910, data->irq));
+       tps65910->irq_mask |= (1 << data->hwirq);
 }
 
 #ifdef CONFIG_PM_SLEEP
@@ -164,10 +159,35 @@ static struct irq_chip tps65910_irq_chip = {
        .irq_set_wake = tps65910_irq_set_wake,
 };
 
+static int tps65910_irq_map(struct irq_domain *h, unsigned int virq,
+                               irq_hw_number_t hw)
+{
+       struct tps65910 *tps65910 = h->host_data;
+
+       irq_set_chip_data(virq, tps65910);
+       irq_set_chip_and_handler(virq, &tps65910_irq_chip, handle_edge_irq);
+       irq_set_nested_thread(virq, 1);
+
+       /* ARM needs us to explicitly flag the IRQ as valid
+        * and will set them noprobe when we do so. */
+#ifdef CONFIG_ARM
+       set_irq_flags(virq, IRQF_VALID);
+#else
+       irq_set_noprobe(virq);
+#endif
+
+       return 0;
+}
+
+static struct irq_domain_ops tps65910_domain_ops = {
+       .map    = tps65910_irq_map,
+       .xlate  = irq_domain_xlate_twocell,
+};
+
 int tps65910_irq_init(struct tps65910 *tps65910, int irq,
                    struct tps65910_platform_data *pdata)
 {
-       int ret, cur_irq;
+       int ret;
        int flags = IRQF_ONESHOT;
 
        if (!irq) {
@@ -175,17 +195,11 @@ int tps65910_irq_init(struct tps65910 *tps65910, int irq,
                return -EINVAL;
        }
 
-       if (!pdata || !pdata->irq_base) {
-               dev_warn(tps65910->dev, "No interrupt support, no IRQ base\n");
+       if (!pdata) {
+               dev_warn(tps65910->dev, "No interrupt support, no pdata\n");
                return -EINVAL;
        }
 
-       tps65910->irq_mask = 0xFFFFFF;
-
-       mutex_init(&tps65910->irq_lock);
-       tps65910->chip_irq = irq;
-       tps65910->irq_base = pdata->irq_base;
-
        switch (tps65910_chip_id(tps65910)) {
        case TPS65910:
                tps65910->irq_num = TPS65910_NUM_IRQ;
@@ -195,22 +209,36 @@ int tps65910_irq_init(struct tps65910 *tps65910, int irq,
                break;
        }
 
-       /* Register with genirq */
-       for (cur_irq = tps65910->irq_base;
-            cur_irq < tps65910->irq_num + tps65910->irq_base;
-            cur_irq++) {
-               irq_set_chip_data(cur_irq, tps65910);
-               irq_set_chip_and_handler(cur_irq, &tps65910_irq_chip,
-                                        handle_edge_irq);
-               irq_set_nested_thread(cur_irq, 1);
-
-               /* ARM needs us to explicitly flag the IRQ as valid
-                * and will set them noprobe when we do so. */
-#ifdef CONFIG_ARM
-               set_irq_flags(cur_irq, IRQF_VALID);
-#else
-               irq_set_noprobe(cur_irq);
-#endif
+       if (pdata->irq_base > 0) {
+               pdata->irq_base = irq_alloc_descs(pdata->irq_base, 0,
+                                       tps65910->irq_num, -1);
+               if (pdata->irq_base < 0) {
+                       dev_warn(tps65910->dev, "Failed to alloc IRQs: %d\n",
+                                       pdata->irq_base);
+                       return pdata->irq_base;
+               }
+       }
+
+       tps65910->irq_mask = 0xFFFFFF;
+
+       mutex_init(&tps65910->irq_lock);
+       tps65910->chip_irq = irq;
+       tps65910->irq_base = pdata->irq_base;
+
+       if (pdata->irq_base > 0)
+               tps65910->domain = irq_domain_add_legacy(tps65910->dev->of_node,
+                                       tps65910->irq_num,
+                                       pdata->irq_base,
+                                       0,
+                                       &tps65910_domain_ops, tps65910);
+       else
+               tps65910->domain = irq_domain_add_linear(tps65910->dev->of_node,
+                                       tps65910->irq_num,
+                                       &tps65910_domain_ops, tps65910);
+
+       if (!tps65910->domain) {
+               dev_err(tps65910->dev, "Failed to create IRQ domain\n");
+               return -ENOMEM;
        }
 
        ret = request_threaded_irq(irq, NULL, tps65910_irq, flags,
index bf2b25ebf2ca7d4c9c2777158518bd2a3185a39c..be9e07b77325a6afef8ed64253bc4a51e5cb4cc9 100644 (file)
 #include <linux/err.h>
 #include <linux/slab.h>
 #include <linux/i2c.h>
-#include <linux/gpio.h>
 #include <linux/mfd/core.h>
 #include <linux/regmap.h>
 #include <linux/mfd/tps65910.h>
+#include <linux/of_device.h>
 
 static struct mfd_cell tps65910s[] = {
+       {
+               .name = "tps65910-gpio",
+       },
        {
                .name = "tps65910-pmic",
        },
@@ -37,30 +40,6 @@ static struct mfd_cell tps65910s[] = {
 };
 
 
-static int tps65910_i2c_read(struct tps65910 *tps65910, u8 reg,
-                                 int bytes, void *dest)
-{
-       return regmap_bulk_read(tps65910->regmap, reg, dest, bytes);
-}
-
-static int tps65910_i2c_write(struct tps65910 *tps65910, u8 reg,
-                                 int bytes, void *src)
-{
-       return regmap_bulk_write(tps65910->regmap, reg, src, bytes);
-}
-
-int tps65910_set_bits(struct tps65910 *tps65910, u8 reg, u8 mask)
-{
-       return regmap_update_bits(tps65910->regmap, reg, mask, mask);
-}
-EXPORT_SYMBOL_GPL(tps65910_set_bits);
-
-int tps65910_clear_bits(struct tps65910 *tps65910, u8 reg, u8 mask)
-{
-       return regmap_update_bits(tps65910->regmap, reg, mask, 0);
-}
-EXPORT_SYMBOL_GPL(tps65910_clear_bits);
-
 static bool is_volatile_reg(struct device *dev, unsigned int reg)
 {
        struct tps65910 *tps65910 = dev_get_drvdata(dev);
@@ -85,80 +64,197 @@ static const struct regmap_config tps65910_regmap_config = {
        .reg_bits = 8,
        .val_bits = 8,
        .volatile_reg = is_volatile_reg,
-       .max_register = TPS65910_MAX_REGISTER,
-       .num_reg_defaults_raw = TPS65910_MAX_REGISTER,
+       .max_register = TPS65910_MAX_REGISTER - 1,
        .cache_type = REGCACHE_RBTREE,
 };
 
-static int tps65910_i2c_probe(struct i2c_client *i2c,
-                           const struct i2c_device_id *id)
+static int __devinit tps65910_sleepinit(struct tps65910 *tps65910,
+               struct tps65910_board *pmic_pdata)
+{
+       struct device *dev = NULL;
+       int ret = 0;
+
+       dev = tps65910->dev;
+
+       if (!pmic_pdata->en_dev_slp)
+               return 0;
+
+       /* enabling SLEEP device state */
+       ret = tps65910_reg_set_bits(tps65910, TPS65910_DEVCTRL,
+                               DEVCTRL_DEV_SLP_MASK);
+       if (ret < 0) {
+               dev_err(dev, "set dev_slp failed: %d\n", ret);
+               goto err_sleep_init;
+       }
+
+       /* Return if there is no sleep keepon data. */
+       if (!pmic_pdata->slp_keepon)
+               return 0;
+
+       if (pmic_pdata->slp_keepon->therm_keepon) {
+               ret = tps65910_reg_set_bits(tps65910,
+                               TPS65910_SLEEP_KEEP_RES_ON,
+                               SLEEP_KEEP_RES_ON_THERM_KEEPON_MASK);
+               if (ret < 0) {
+                       dev_err(dev, "set therm_keepon failed: %d\n", ret);
+                       goto disable_dev_slp;
+               }
+       }
+
+       if (pmic_pdata->slp_keepon->clkout32k_keepon) {
+               ret = tps65910_reg_set_bits(tps65910,
+                               TPS65910_SLEEP_KEEP_RES_ON,
+                               SLEEP_KEEP_RES_ON_CLKOUT32K_KEEPON_MASK);
+               if (ret < 0) {
+                       dev_err(dev, "set clkout32k_keepon failed: %d\n", ret);
+                       goto disable_dev_slp;
+               }
+       }
+
+       if (pmic_pdata->slp_keepon->i2chs_keepon) {
+               ret = tps65910_reg_set_bits(tps65910,
+                               TPS65910_SLEEP_KEEP_RES_ON,
+                               SLEEP_KEEP_RES_ON_I2CHS_KEEPON_MASK);
+               if (ret < 0) {
+                       dev_err(dev, "set i2chs_keepon failed: %d\n", ret);
+                       goto disable_dev_slp;
+               }
+       }
+
+       return 0;
+
+disable_dev_slp:
+       tps65910_reg_clear_bits(tps65910, TPS65910_DEVCTRL,
+                               DEVCTRL_DEV_SLP_MASK);
+
+err_sleep_init:
+       return ret;
+}
+
+#ifdef CONFIG_OF
+static struct of_device_id tps65910_of_match[] = {
+       { .compatible = "ti,tps65910", .data = (void *)TPS65910},
+       { .compatible = "ti,tps65911", .data = (void *)TPS65911},
+       { },
+};
+MODULE_DEVICE_TABLE(of, tps65910_of_match);
+
+static struct tps65910_board *tps65910_parse_dt(struct i2c_client *client,
+                                               int *chip_id)
+{
+       struct device_node *np = client->dev.of_node;
+       struct tps65910_board *board_info;
+       unsigned int prop;
+       const struct of_device_id *match;
+       int ret = 0;
+
+       match = of_match_device(tps65910_of_match, &client->dev);
+       if (!match) {
+               dev_err(&client->dev, "Failed to find matching dt id\n");
+               return NULL;
+       }
+
+       *chip_id  = (int)match->data;
+
+       board_info = devm_kzalloc(&client->dev, sizeof(*board_info),
+                       GFP_KERNEL);
+       if (!board_info) {
+               dev_err(&client->dev, "Failed to allocate pdata\n");
+               return NULL;
+       }
+
+       ret = of_property_read_u32(np, "ti,vmbch-threshold", &prop);
+       if (!ret)
+               board_info->vmbch_threshold = prop;
+       else if (*chip_id == TPS65911)
+               dev_warn(&client->dev, "VMBCH-Threshold not specified");
+
+       ret = of_property_read_u32(np, "ti,vmbch2-threshold", &prop);
+       if (!ret)
+               board_info->vmbch2_threshold = prop;
+       else if (*chip_id == TPS65911)
+               dev_warn(&client->dev, "VMBCH2-Threshold not specified");
+
+       board_info->irq = client->irq;
+       board_info->irq_base = -1;
+
+       return board_info;
+}
+#else
+static inline
+struct tps65910_board *tps65910_parse_dt(struct i2c_client *client,
+                                        int *chip_id)
+{
+       return NULL;
+}
+#endif
+
+static __devinit int tps65910_i2c_probe(struct i2c_client *i2c,
+                                       const struct i2c_device_id *id)
 {
        struct tps65910 *tps65910;
        struct tps65910_board *pmic_plat_data;
+       struct tps65910_board *of_pmic_plat_data = NULL;
        struct tps65910_platform_data *init_data;
        int ret = 0;
+       int chip_id = id->driver_data;
 
        pmic_plat_data = dev_get_platdata(&i2c->dev);
+
+       if (!pmic_plat_data && i2c->dev.of_node) {
+               pmic_plat_data = tps65910_parse_dt(i2c, &chip_id);
+               of_pmic_plat_data = pmic_plat_data;
+       }
+
        if (!pmic_plat_data)
                return -EINVAL;
 
-       init_data = kzalloc(sizeof(struct tps65910_platform_data), GFP_KERNEL);
+       init_data = devm_kzalloc(&i2c->dev, sizeof(*init_data), GFP_KERNEL);
        if (init_data == NULL)
                return -ENOMEM;
 
-       tps65910 = kzalloc(sizeof(struct tps65910), GFP_KERNEL);
-       if (tps65910 == NULL) {
-               kfree(init_data);
+       tps65910 = devm_kzalloc(&i2c->dev, sizeof(*tps65910), GFP_KERNEL);
+       if (tps65910 == NULL)
                return -ENOMEM;
-       }
 
+       tps65910->of_plat_data = of_pmic_plat_data;
        i2c_set_clientdata(i2c, tps65910);
        tps65910->dev = &i2c->dev;
        tps65910->i2c_client = i2c;
-       tps65910->id = id->driver_data;
-       tps65910->read = tps65910_i2c_read;
-       tps65910->write = tps65910_i2c_write;
+       tps65910->id = chip_id;
        mutex_init(&tps65910->io_mutex);
 
-       tps65910->regmap = regmap_init_i2c(i2c, &tps65910_regmap_config);
+       tps65910->regmap = devm_regmap_init_i2c(i2c, &tps65910_regmap_config);
        if (IS_ERR(tps65910->regmap)) {
                ret = PTR_ERR(tps65910->regmap);
                dev_err(&i2c->dev, "regmap initialization failed: %d\n", ret);
-               goto regmap_err;
+               return ret;
        }
 
        ret = mfd_add_devices(tps65910->dev, -1,
                              tps65910s, ARRAY_SIZE(tps65910s),
                              NULL, 0);
-       if (ret < 0)
-               goto err;
+       if (ret < 0) {
+               dev_err(&i2c->dev, "mfd_add_devices failed: %d\n", ret);
+               return ret;
+       }
 
        init_data->irq = pmic_plat_data->irq;
        init_data->irq_base = pmic_plat_data->irq_base;
 
-       tps65910_gpio_init(tps65910, pmic_plat_data->gpio_base);
-
        tps65910_irq_init(tps65910, init_data->irq, init_data);
 
-       kfree(init_data);
-       return ret;
+       tps65910_sleepinit(tps65910, pmic_plat_data);
 
-err:
-       regmap_exit(tps65910->regmap);
-regmap_err:
-       kfree(tps65910);
-       kfree(init_data);
        return ret;
 }
 
-static int tps65910_i2c_remove(struct i2c_client *i2c)
+static __devexit int tps65910_i2c_remove(struct i2c_client *i2c)
 {
        struct tps65910 *tps65910 = i2c_get_clientdata(i2c);
 
        tps65910_irq_exit(tps65910);
        mfd_remove_devices(tps65910->dev);
-       regmap_exit(tps65910->regmap);
-       kfree(tps65910);
 
        return 0;
 }
@@ -175,9 +271,10 @@ static struct i2c_driver tps65910_i2c_driver = {
        .driver = {
                   .name = "tps65910",
                   .owner = THIS_MODULE,
+                  .of_match_table = of_match_ptr(tps65910_of_match),
        },
        .probe = tps65910_i2c_probe,
-       .remove = tps65910_i2c_remove,
+       .remove = __devexit_p(tps65910_i2c_remove),
        .id_table = tps65910_i2c_id,
 };
 
index 5d656e8143583634196061e709dcd1944d0b1121..ad733d76207ac11320f69603ea1a3235f81b9495 100644 (file)
@@ -757,6 +757,7 @@ int twl4030_init_irq(struct device *dev, int irq_num)
                dev_err(dev, "could not claim irq%d: %d\n", irq_num, status);
                goto fail_rqirq;
        }
+       enable_irq_wake(irq_num);
 
        return irq_base;
 fail_rqirq:
index 2d6bedadca096d68eec4ce61332b57d6f08140a8..4ded9e7aa246efdc0e38347fab044f46ef2dc7ef 100644 (file)
 #include <linux/types.h>
 #include <linux/slab.h>
 #include <linux/kernel.h>
+#include <linux/err.h>
 #include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_gpio.h>
+#include <linux/of_platform.h>
 #include <linux/gpio.h>
 #include <linux/delay.h>
 #include <linux/i2c.h>
 #include <linux/err.h>
 #include <linux/mfd/core.h>
 #include <linux/mfd/twl6040.h>
+#include <linux/regulator/consumer.h>
 
 #define VIBRACTRL_MEMBER(reg) ((reg == TWL6040_REG_VIBCTLL) ? 0 : 1)
+#define TWL6040_NUM_SUPPLIES   (2)
+
+static bool twl6040_has_vibra(struct twl6040_platform_data *pdata,
+                             struct device_node *node)
+{
+       if (pdata && pdata->vibra)
+               return true;
+
+#ifdef CONFIG_OF
+       if (of_find_node_by_name(node, "vibra"))
+               return true;
+#endif
+
+       return false;
+}
 
 int twl6040_reg_read(struct twl6040 *twl6040, unsigned int reg)
 {
@@ -502,17 +523,18 @@ static int __devinit twl6040_probe(struct i2c_client *client,
                                     const struct i2c_device_id *id)
 {
        struct twl6040_platform_data *pdata = client->dev.platform_data;
+       struct device_node *node = client->dev.of_node;
        struct twl6040 *twl6040;
        struct mfd_cell *cell = NULL;
-       int ret, children = 0;
+       int irq, ret, children = 0;
 
-       if (!pdata) {
+       if (!pdata && !node) {
                dev_err(&client->dev, "Platform data is missing\n");
                return -EINVAL;
        }
 
        /* In order to operate correctly we need valid interrupt config */
-       if (!client->irq || !pdata->irq_base) {
+       if (!client->irq) {
                dev_err(&client->dev, "Invalid IRQ configuration\n");
                return -EINVAL;
        }
@@ -524,7 +546,7 @@ static int __devinit twl6040_probe(struct i2c_client *client,
                goto err;
        }
 
-       twl6040->regmap = regmap_init_i2c(client, &twl6040_regmap_config);
+       twl6040->regmap = devm_regmap_init_i2c(client, &twl6040_regmap_config);
        if (IS_ERR(twl6040->regmap)) {
                ret = PTR_ERR(twl6040->regmap);
                goto err;
@@ -532,9 +554,23 @@ static int __devinit twl6040_probe(struct i2c_client *client,
 
        i2c_set_clientdata(client, twl6040);
 
+       twl6040->supplies[0].supply = "vio";
+       twl6040->supplies[1].supply = "v2v1";
+       ret = regulator_bulk_get(&client->dev, TWL6040_NUM_SUPPLIES,
+                                twl6040->supplies);
+       if (ret != 0) {
+               dev_err(&client->dev, "Failed to get supplies: %d\n", ret);
+               goto regulator_get_err;
+       }
+
+       ret = regulator_bulk_enable(TWL6040_NUM_SUPPLIES, twl6040->supplies);
+       if (ret != 0) {
+               dev_err(&client->dev, "Failed to enable supplies: %d\n", ret);
+               goto power_err;
+       }
+
        twl6040->dev = &client->dev;
        twl6040->irq = client->irq;
-       twl6040->irq_base = pdata->irq_base;
 
        mutex_init(&twl6040->mutex);
        mutex_init(&twl6040->io_mutex);
@@ -543,22 +579,26 @@ static int __devinit twl6040_probe(struct i2c_client *client,
        twl6040->rev = twl6040_reg_read(twl6040, TWL6040_REG_ASICREV);
 
        /* ERRATA: Automatic power-up is not possible in ES1.0 */
-       if (twl6040_get_revid(twl6040) > TWL6040_REV_ES1_0)
-               twl6040->audpwron = pdata->audpwron_gpio;
-       else
+       if (twl6040_get_revid(twl6040) > TWL6040_REV_ES1_0) {
+               if (pdata)
+                       twl6040->audpwron = pdata->audpwron_gpio;
+               else
+                       twl6040->audpwron = of_get_named_gpio(node,
+                                               "ti,audpwron-gpio", 0);
+       } else
                twl6040->audpwron = -EINVAL;
 
        if (gpio_is_valid(twl6040->audpwron)) {
                ret = gpio_request_one(twl6040->audpwron, GPIOF_OUT_INIT_LOW,
                                       "audpwron");
                if (ret)
-                       goto gpio1_err;
+                       goto gpio_err;
        }
 
        /* codec interrupt */
        ret = twl6040_irq_init(twl6040);
        if (ret)
-               goto gpio2_err;
+               goto irq_init_err;
 
        ret = request_threaded_irq(twl6040->irq_base + TWL6040_IRQ_READY,
                                   NULL, twl6040_naudint_handler, 0,
@@ -572,22 +612,27 @@ static int __devinit twl6040_probe(struct i2c_client *client,
        /* dual-access registers controlled by I2C only */
        twl6040_set_bits(twl6040, TWL6040_REG_ACCCTL, TWL6040_I2CSEL);
 
-       if (pdata->codec) {
-               int irq = twl6040->irq_base + TWL6040_IRQ_PLUG;
-
-               cell = &twl6040->cells[children];
-               cell->name = "twl6040-codec";
-               twl6040_codec_rsrc[0].start = irq;
-               twl6040_codec_rsrc[0].end = irq;
-               cell->resources = twl6040_codec_rsrc;
-               cell->num_resources = ARRAY_SIZE(twl6040_codec_rsrc);
+       /*
+        * The main functionality of twl6040 to provide audio on OMAP4+ systems.
+        * We can add the ASoC codec child whenever this driver has been loaded.
+        * The ASoC codec can work without pdata, pass the platform_data only if
+        * it has been provided.
+        */
+       irq = twl6040->irq_base + TWL6040_IRQ_PLUG;
+       cell = &twl6040->cells[children];
+       cell->name = "twl6040-codec";
+       twl6040_codec_rsrc[0].start = irq;
+       twl6040_codec_rsrc[0].end = irq;
+       cell->resources = twl6040_codec_rsrc;
+       cell->num_resources = ARRAY_SIZE(twl6040_codec_rsrc);
+       if (pdata && pdata->codec) {
                cell->platform_data = pdata->codec;
                cell->pdata_size = sizeof(*pdata->codec);
-               children++;
        }
+       children++;
 
-       if (pdata->vibra) {
-               int irq = twl6040->irq_base + TWL6040_IRQ_VIB;
+       if (twl6040_has_vibra(pdata, node)) {
+               irq = twl6040->irq_base + TWL6040_IRQ_VIB;
 
                cell = &twl6040->cells[children];
                cell->name = "twl6040-vibra";
@@ -596,21 +641,17 @@ static int __devinit twl6040_probe(struct i2c_client *client,
                cell->resources = twl6040_vibra_rsrc;
                cell->num_resources = ARRAY_SIZE(twl6040_vibra_rsrc);
 
-               cell->platform_data = pdata->vibra;
-               cell->pdata_size = sizeof(*pdata->vibra);
+               if (pdata && pdata->vibra) {
+                       cell->platform_data = pdata->vibra;
+                       cell->pdata_size = sizeof(*pdata->vibra);
+               }
                children++;
        }
 
-       if (children) {
-               ret = mfd_add_devices(&client->dev, -1, twl6040->cells,
-                                     children, NULL, 0);
-               if (ret)
-                       goto mfd_err;
-       } else {
-               dev_err(&client->dev, "No platform data found for children\n");
-               ret = -ENODEV;
+       ret = mfd_add_devices(&client->dev, -1, twl6040->cells, children,
+                             NULL, 0);
+       if (ret)
                goto mfd_err;
-       }
 
        return 0;
 
@@ -618,12 +659,15 @@ mfd_err:
        free_irq(twl6040->irq_base + TWL6040_IRQ_READY, twl6040);
 irq_err:
        twl6040_irq_exit(twl6040);
-gpio2_err:
+irq_init_err:
        if (gpio_is_valid(twl6040->audpwron))
                gpio_free(twl6040->audpwron);
-gpio1_err:
+gpio_err:
+       regulator_bulk_disable(TWL6040_NUM_SUPPLIES, twl6040->supplies);
+power_err:
+       regulator_bulk_free(TWL6040_NUM_SUPPLIES, twl6040->supplies);
+regulator_get_err:
        i2c_set_clientdata(client, NULL);
-       regmap_exit(twl6040->regmap);
 err:
        return ret;
 }
@@ -643,7 +687,9 @@ static int __devexit twl6040_remove(struct i2c_client *client)
 
        mfd_remove_devices(&client->dev);
        i2c_set_clientdata(client, NULL);
-       regmap_exit(twl6040->regmap);
+
+       regulator_bulk_disable(TWL6040_NUM_SUPPLIES, twl6040->supplies);
+       regulator_bulk_free(TWL6040_NUM_SUPPLIES, twl6040->supplies);
 
        return 0;
 }
index b3f8ddaa28a8b792c14d2b5c785ffed4b099a735..4b42543da2282cae615fbc77583491b486903542 100644 (file)
 
 #include <linux/kernel.h>
 #include <linux/module.h>
+#include <linux/err.h>
 #include <linux/irq.h>
+#include <linux/of.h>
+#include <linux/irqdomain.h>
 #include <linux/interrupt.h>
 #include <linux/mfd/core.h>
 #include <linux/mfd/twl6040.h>
@@ -138,7 +141,8 @@ static irqreturn_t twl6040_irq_thread(int irq, void *data)
 
 int twl6040_irq_init(struct twl6040 *twl6040)
 {
-       int cur_irq, ret;
+       struct device_node *node = twl6040->dev->of_node;
+       int i, nr_irqs, irq_base, ret;
        u8 val;
 
        mutex_init(&twl6040->irq_mutex);
@@ -148,21 +152,31 @@ int twl6040_irq_init(struct twl6040 *twl6040)
        twl6040->irq_masks_cache = TWL6040_ALLINT_MSK;
        twl6040_reg_write(twl6040, TWL6040_REG_INTMR, TWL6040_ALLINT_MSK);
 
+       nr_irqs = ARRAY_SIZE(twl6040_irqs);
+
+       irq_base = irq_alloc_descs(-1, 0, nr_irqs, 0);
+       if (IS_ERR_VALUE(irq_base)) {
+               dev_err(twl6040->dev, "Fail to allocate IRQ descs\n");
+               return irq_base;
+       }
+       twl6040->irq_base = irq_base;
+
+       irq_domain_add_legacy(node, ARRAY_SIZE(twl6040_irqs), irq_base, 0,
+                             &irq_domain_simple_ops, NULL);
+
        /* Register them with genirq */
-       for (cur_irq = twl6040->irq_base;
-            cur_irq < twl6040->irq_base + ARRAY_SIZE(twl6040_irqs);
-            cur_irq++) {
-               irq_set_chip_data(cur_irq, twl6040);
-               irq_set_chip_and_handler(cur_irq, &twl6040_irq_chip,
+       for (i = irq_base; i < irq_base + nr_irqs; i++) {
+               irq_set_chip_data(i, twl6040);
+               irq_set_chip_and_handler(i, &twl6040_irq_chip,
                                         handle_level_irq);
-               irq_set_nested_thread(cur_irq, 1);
+               irq_set_nested_thread(i, 1);
 
                /* ARM needs us to explicitly flag the IRQ as valid
                 * and will set them noprobe when we do so. */
 #ifdef CONFIG_ARM
-               set_irq_flags(cur_irq, IRQF_VALID);
+               set_irq_flags(i, IRQF_VALID);
 #else
-               irq_set_noprobe(cur_irq);
+               irq_set_noprobe(i);
 #endif
        }
 
index b73cc15e00818d4e15f2f44f9fe0a9f12e6196a6..872aff21e4be6fa682eb01aad72b04492efd622e 100644 (file)
@@ -131,17 +131,7 @@ static struct pci_driver vx855_pci_driver = {
        .remove         = __devexit_p(vx855_remove),
 };
 
-static int vx855_init(void)
-{
-       return pci_register_driver(&vx855_pci_driver);
-}
-module_init(vx855_init);
-
-static void vx855_exit(void)
-{
-       pci_unregister_driver(&vx855_pci_driver);
-}
-module_exit(vx855_exit);
+module_pci_driver(vx855_pci_driver);
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Harald Welte <HaraldWelte@viatech.com>");
index 87210954a066f9598cc40b0dd7b2acfb00017f34..6ee3018d8653f6d8b73b92d70d296d2ed01e8f62 100644 (file)
@@ -280,11 +280,11 @@ void wm831x_auxadc_init(struct wm831x *wm831x)
        mutex_init(&wm831x->auxadc_lock);
        INIT_LIST_HEAD(&wm831x->auxadc_pending);
 
-       if (wm831x->irq && wm831x->irq_base) {
+       if (wm831x->irq) {
                wm831x->auxadc_read = wm831x_auxadc_read_irq;
 
-               ret = request_threaded_irq(wm831x->irq_base +
-                                          WM831X_IRQ_AUXADC_DATA,
+               ret = request_threaded_irq(wm831x_irq(wm831x,
+                                                     WM831X_IRQ_AUXADC_DATA),
                                           NULL, wm831x_auxadc_irq, 0,
                                           "auxadc", wm831x);
                if (ret < 0) {
index 838056c3493a75e848a4b3ba88b2416c9d98a846..946698fd2dc6a4dc9b3a7868bc7f44c920a46201 100644 (file)
@@ -614,8 +614,15 @@ int wm831x_set_bits(struct wm831x *wm831x, unsigned short reg,
 }
 EXPORT_SYMBOL_GPL(wm831x_set_bits);
 
+static struct resource wm831x_io_parent = {
+       .start = 0,
+       .end   = 0xffffffff,
+       .flags = IORESOURCE_IO,
+};
+
 static struct resource wm831x_dcdc1_resources[] = {
        {
+               .parent = &wm831x_io_parent,
                .start = WM831X_DC1_CONTROL_1,
                .end   = WM831X_DC1_DVS_CONTROL,
                .flags = IORESOURCE_IO,
@@ -637,6 +644,7 @@ static struct resource wm831x_dcdc1_resources[] = {
 
 static struct resource wm831x_dcdc2_resources[] = {
        {
+               .parent = &wm831x_io_parent,
                .start = WM831X_DC2_CONTROL_1,
                .end   = WM831X_DC2_DVS_CONTROL,
                .flags = IORESOURCE_IO,
@@ -657,6 +665,7 @@ static struct resource wm831x_dcdc2_resources[] = {
 
 static struct resource wm831x_dcdc3_resources[] = {
        {
+               .parent = &wm831x_io_parent,
                .start = WM831X_DC3_CONTROL_1,
                .end   = WM831X_DC3_SLEEP_CONTROL,
                .flags = IORESOURCE_IO,
@@ -671,6 +680,7 @@ static struct resource wm831x_dcdc3_resources[] = {
 
 static struct resource wm831x_dcdc4_resources[] = {
        {
+               .parent = &wm831x_io_parent,
                .start = WM831X_DC4_CONTROL,
                .end   = WM831X_DC4_SLEEP_CONTROL,
                .flags = IORESOURCE_IO,
@@ -685,6 +695,7 @@ static struct resource wm831x_dcdc4_resources[] = {
 
 static struct resource wm8320_dcdc4_buck_resources[] = {
        {
+               .parent = &wm831x_io_parent,
                .start = WM831X_DC4_CONTROL,
                .end   = WM832X_DC4_SLEEP_CONTROL,
                .flags = IORESOURCE_IO,
@@ -707,6 +718,7 @@ static struct resource wm831x_gpio_resources[] = {
 
 static struct resource wm831x_isink1_resources[] = {
        {
+               .parent = &wm831x_io_parent,
                .start = WM831X_CURRENT_SINK_1,
                .end   = WM831X_CURRENT_SINK_1,
                .flags = IORESOURCE_IO,
@@ -720,6 +732,7 @@ static struct resource wm831x_isink1_resources[] = {
 
 static struct resource wm831x_isink2_resources[] = {
        {
+               .parent = &wm831x_io_parent,
                .start = WM831X_CURRENT_SINK_2,
                .end   = WM831X_CURRENT_SINK_2,
                .flags = IORESOURCE_IO,
@@ -733,6 +746,7 @@ static struct resource wm831x_isink2_resources[] = {
 
 static struct resource wm831x_ldo1_resources[] = {
        {
+               .parent = &wm831x_io_parent,
                .start = WM831X_LDO1_CONTROL,
                .end   = WM831X_LDO1_SLEEP_CONTROL,
                .flags = IORESOURCE_IO,
@@ -747,6 +761,7 @@ static struct resource wm831x_ldo1_resources[] = {
 
 static struct resource wm831x_ldo2_resources[] = {
        {
+               .parent = &wm831x_io_parent,
                .start = WM831X_LDO2_CONTROL,
                .end   = WM831X_LDO2_SLEEP_CONTROL,
                .flags = IORESOURCE_IO,
@@ -761,6 +776,7 @@ static struct resource wm831x_ldo2_resources[] = {
 
 static struct resource wm831x_ldo3_resources[] = {
        {
+               .parent = &wm831x_io_parent,
                .start = WM831X_LDO3_CONTROL,
                .end   = WM831X_LDO3_SLEEP_CONTROL,
                .flags = IORESOURCE_IO,
@@ -775,6 +791,7 @@ static struct resource wm831x_ldo3_resources[] = {
 
 static struct resource wm831x_ldo4_resources[] = {
        {
+               .parent = &wm831x_io_parent,
                .start = WM831X_LDO4_CONTROL,
                .end   = WM831X_LDO4_SLEEP_CONTROL,
                .flags = IORESOURCE_IO,
@@ -789,6 +806,7 @@ static struct resource wm831x_ldo4_resources[] = {
 
 static struct resource wm831x_ldo5_resources[] = {
        {
+               .parent = &wm831x_io_parent,
                .start = WM831X_LDO5_CONTROL,
                .end   = WM831X_LDO5_SLEEP_CONTROL,
                .flags = IORESOURCE_IO,
@@ -803,6 +821,7 @@ static struct resource wm831x_ldo5_resources[] = {
 
 static struct resource wm831x_ldo6_resources[] = {
        {
+               .parent = &wm831x_io_parent,
                .start = WM831X_LDO6_CONTROL,
                .end   = WM831X_LDO6_SLEEP_CONTROL,
                .flags = IORESOURCE_IO,
@@ -817,6 +836,7 @@ static struct resource wm831x_ldo6_resources[] = {
 
 static struct resource wm831x_ldo7_resources[] = {
        {
+               .parent = &wm831x_io_parent,
                .start = WM831X_LDO7_CONTROL,
                .end   = WM831X_LDO7_SLEEP_CONTROL,
                .flags = IORESOURCE_IO,
@@ -831,6 +851,7 @@ static struct resource wm831x_ldo7_resources[] = {
 
 static struct resource wm831x_ldo8_resources[] = {
        {
+               .parent = &wm831x_io_parent,
                .start = WM831X_LDO8_CONTROL,
                .end   = WM831X_LDO8_SLEEP_CONTROL,
                .flags = IORESOURCE_IO,
@@ -845,6 +866,7 @@ static struct resource wm831x_ldo8_resources[] = {
 
 static struct resource wm831x_ldo9_resources[] = {
        {
+               .parent = &wm831x_io_parent,
                .start = WM831X_LDO9_CONTROL,
                .end   = WM831X_LDO9_SLEEP_CONTROL,
                .flags = IORESOURCE_IO,
@@ -859,6 +881,7 @@ static struct resource wm831x_ldo9_resources[] = {
 
 static struct resource wm831x_ldo10_resources[] = {
        {
+               .parent = &wm831x_io_parent,
                .start = WM831X_LDO10_CONTROL,
                .end   = WM831X_LDO10_SLEEP_CONTROL,
                .flags = IORESOURCE_IO,
@@ -873,6 +896,7 @@ static struct resource wm831x_ldo10_resources[] = {
 
 static struct resource wm831x_ldo11_resources[] = {
        {
+               .parent = &wm831x_io_parent,
                .start = WM831X_LDO11_ON_CONTROL,
                .end   = WM831X_LDO11_SLEEP_CONTROL,
                .flags = IORESOURCE_IO,
@@ -974,6 +998,7 @@ static struct resource wm831x_rtc_resources[] = {
 
 static struct resource wm831x_status1_resources[] = {
        {
+               .parent = &wm831x_io_parent,
                .start = WM831X_STATUS_LED_1,
                .end   = WM831X_STATUS_LED_1,
                .flags = IORESOURCE_IO,
@@ -982,6 +1007,7 @@ static struct resource wm831x_status1_resources[] = {
 
 static struct resource wm831x_status2_resources[] = {
        {
+               .parent = &wm831x_io_parent,
                .start = WM831X_STATUS_LED_2,
                .end   = WM831X_STATUS_LED_2,
                .flags = IORESOURCE_IO,
@@ -1787,27 +1813,27 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
        case WM8310:
                ret = mfd_add_devices(wm831x->dev, wm831x_num,
                                      wm8310_devs, ARRAY_SIZE(wm8310_devs),
-                                     NULL, wm831x->irq_base);
+                                     NULL, 0);
                break;
 
        case WM8311:
                ret = mfd_add_devices(wm831x->dev, wm831x_num,
                                      wm8311_devs, ARRAY_SIZE(wm8311_devs),
-                                     NULL, wm831x->irq_base);
+                                     NULL, 0);
                if (!pdata || !pdata->disable_touch)
                        mfd_add_devices(wm831x->dev, wm831x_num,
                                        touch_devs, ARRAY_SIZE(touch_devs),
-                                       NULL, wm831x->irq_base);
+                                       NULL, 0);
                break;
 
        case WM8312:
                ret = mfd_add_devices(wm831x->dev, wm831x_num,
                                      wm8312_devs, ARRAY_SIZE(wm8312_devs),
-                                     NULL, wm831x->irq_base);
+                                     NULL, 0);
                if (!pdata || !pdata->disable_touch)
                        mfd_add_devices(wm831x->dev, wm831x_num,
                                        touch_devs, ARRAY_SIZE(touch_devs),
-                                       NULL, wm831x->irq_base);
+                                       NULL, 0);
                break;
 
        case WM8320:
@@ -1816,7 +1842,7 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
        case WM8326:
                ret = mfd_add_devices(wm831x->dev, wm831x_num,
                                      wm8320_devs, ARRAY_SIZE(wm8320_devs),
-                                     NULL, wm831x->irq_base);
+                                     NULL, 0);
                break;
 
        default:
@@ -1841,7 +1867,7 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
        if (ret & WM831X_XTAL_ENA) {
                ret = mfd_add_devices(wm831x->dev, wm831x_num,
                                      rtc_devs, ARRAY_SIZE(rtc_devs),
-                                     NULL, wm831x->irq_base);
+                                     NULL, 0);
                if (ret != 0) {
                        dev_err(wm831x->dev, "Failed to add RTC: %d\n", ret);
                        goto err_irq;
@@ -1854,7 +1880,7 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
                /* Treat errors as non-critical */
                ret = mfd_add_devices(wm831x->dev, wm831x_num, backlight_devs,
                                      ARRAY_SIZE(backlight_devs), NULL,
-                                     wm831x->irq_base);
+                                     0);
                if (ret < 0)
                        dev_err(wm831x->dev, "Failed to add backlight: %d\n",
                                ret);
@@ -1883,8 +1909,7 @@ void wm831x_device_exit(struct wm831x *wm831x)
 {
        wm831x_otp_exit(wm831x);
        mfd_remove_devices(wm831x->dev);
-       if (wm831x->irq_base)
-               free_irq(wm831x->irq_base + WM831X_IRQ_AUXADC_DATA, wm831x);
+       free_irq(wm831x_irq(wm831x, WM831X_IRQ_AUXADC_DATA), wm831x);
        wm831x_irq_exit(wm831x);
 }
 
index bec4d053916012a696442fff39b847be647b9db0..804e56ec99eb284f4c32e1f44f8b7a143d4f340c 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/irq.h>
 #include <linux/mfd/core.h>
 #include <linux/interrupt.h>
+#include <linux/irqdomain.h>
 
 #include <linux/mfd/wm831x/core.h>
 #include <linux/mfd/wm831x/pdata.h>
@@ -328,7 +329,7 @@ static inline int irq_data_to_status_reg(struct wm831x_irq_data *irq_data)
 static inline struct wm831x_irq_data *irq_to_wm831x_irq(struct wm831x *wm831x,
                                                        int irq)
 {
-       return &wm831x_irqs[irq - wm831x->irq_base];
+       return &wm831x_irqs[irq];
 }
 
 static void wm831x_irq_lock(struct irq_data *data)
@@ -374,7 +375,7 @@ static void wm831x_irq_enable(struct irq_data *data)
 {
        struct wm831x *wm831x = irq_data_get_irq_chip_data(data);
        struct wm831x_irq_data *irq_data = irq_to_wm831x_irq(wm831x,
-                                                            data->irq);
+                                                            data->hwirq);
 
        wm831x->irq_masks_cur[irq_data->reg - 1] &= ~irq_data->mask;
 }
@@ -383,7 +384,7 @@ static void wm831x_irq_disable(struct irq_data *data)
 {
        struct wm831x *wm831x = irq_data_get_irq_chip_data(data);
        struct wm831x_irq_data *irq_data = irq_to_wm831x_irq(wm831x,
-                                                            data->irq);
+                                                            data->hwirq);
 
        wm831x->irq_masks_cur[irq_data->reg - 1] |= irq_data->mask;
 }
@@ -393,7 +394,7 @@ static int wm831x_irq_set_type(struct irq_data *data, unsigned int type)
        struct wm831x *wm831x = irq_data_get_irq_chip_data(data);
        int irq;
 
-       irq = data->irq - wm831x->irq_base;
+       irq = data->hwirq;
 
        if (irq < WM831X_IRQ_GPIO_1 || irq > WM831X_IRQ_GPIO_11) {
                /* Ignore internal-only IRQs */
@@ -412,22 +413,25 @@ static int wm831x_irq_set_type(struct irq_data *data, unsigned int type)
         * do the update here as we can be called with the bus lock
         * held.
         */
+       wm831x->gpio_level_low[irq] = false;
+       wm831x->gpio_level_high[irq] = false;
        switch (type) {
        case IRQ_TYPE_EDGE_BOTH:
                wm831x->gpio_update[irq] = 0x10000 | WM831X_GPN_INT_MODE;
-               wm831x->gpio_level[irq] = false;
                break;
        case IRQ_TYPE_EDGE_RISING:
                wm831x->gpio_update[irq] = 0x10000 | WM831X_GPN_POL;
-               wm831x->gpio_level[irq] = false;
                break;
        case IRQ_TYPE_EDGE_FALLING:
                wm831x->gpio_update[irq] = 0x10000;
-               wm831x->gpio_level[irq] = false;
                break;
        case IRQ_TYPE_LEVEL_HIGH:
                wm831x->gpio_update[irq] = 0x10000 | WM831X_GPN_POL;
-               wm831x->gpio_level[irq] = true;
+               wm831x->gpio_level_high[irq] = true;
+               break;
+       case IRQ_TYPE_LEVEL_LOW:
+               wm831x->gpio_update[irq] = 0x10000;
+               wm831x->gpio_level_low[irq] = true;
                break;
        default:
                return -EINVAL;
@@ -469,9 +473,11 @@ static irqreturn_t wm831x_irq_thread(int irq, void *data)
         * descriptors.
         */
        if (primary & WM831X_TCHPD_INT)
-               handle_nested_irq(wm831x->irq_base + WM831X_IRQ_TCHPD);
+               handle_nested_irq(irq_find_mapping(wm831x->irq_domain,
+                                                  WM831X_IRQ_TCHPD));
        if (primary & WM831X_TCHDATA_INT)
-               handle_nested_irq(wm831x->irq_base + WM831X_IRQ_TCHDATA);
+               handle_nested_irq(irq_find_mapping(wm831x->irq_domain,
+                                                  WM831X_IRQ_TCHDATA));
        primary &= ~(WM831X_TCHDATA_EINT | WM831X_TCHPD_EINT);
 
        for (i = 0; i < ARRAY_SIZE(wm831x_irqs); i++) {
@@ -507,16 +513,29 @@ static irqreturn_t wm831x_irq_thread(int irq, void *data)
                }
 
                if (*status & wm831x_irqs[i].mask)
-                       handle_nested_irq(wm831x->irq_base + i);
+                       handle_nested_irq(irq_find_mapping(wm831x->irq_domain,
+                                                          i));
 
                /* Simulate an edge triggered IRQ by polling the input
                 * status.  This is sucky but improves interoperability.
                 */
                if (primary == WM831X_GP_INT &&
-                   wm831x->gpio_level[i - WM831X_IRQ_GPIO_1]) {
+                   wm831x->gpio_level_high[i - WM831X_IRQ_GPIO_1]) {
                        ret = wm831x_reg_read(wm831x, WM831X_GPIO_LEVEL);
                        while (ret & 1 << (i - WM831X_IRQ_GPIO_1)) {
-                               handle_nested_irq(wm831x->irq_base + i);
+                               handle_nested_irq(irq_find_mapping(wm831x->irq_domain,
+                                                                  i));
+                               ret = wm831x_reg_read(wm831x,
+                                                     WM831X_GPIO_LEVEL);
+                       }
+               }
+
+               if (primary == WM831X_GP_INT &&
+                   wm831x->gpio_level_low[i - WM831X_IRQ_GPIO_1]) {
+                       ret = wm831x_reg_read(wm831x, WM831X_GPIO_LEVEL);
+                       while (!(ret & 1 << (i - WM831X_IRQ_GPIO_1))) {
+                               handle_nested_irq(irq_find_mapping(wm831x->irq_domain,
+                                                                  i));
                                ret = wm831x_reg_read(wm831x,
                                                      WM831X_GPIO_LEVEL);
                        }
@@ -527,10 +546,34 @@ out:
        return IRQ_HANDLED;
 }
 
+static int wm831x_irq_map(struct irq_domain *h, unsigned int virq,
+                         irq_hw_number_t hw)
+{
+       irq_set_chip_data(virq, h->host_data);
+       irq_set_chip_and_handler(virq, &wm831x_irq_chip, handle_edge_irq);
+       irq_set_nested_thread(virq, 1);
+
+       /* ARM needs us to explicitly flag the IRQ as valid
+        * and will set them noprobe when we do so. */
+#ifdef CONFIG_ARM
+       set_irq_flags(virq, IRQF_VALID);
+#else
+       irq_set_noprobe(virq);
+#endif
+
+       return 0;
+}
+
+static struct irq_domain_ops wm831x_irq_domain_ops = {
+       .map    = wm831x_irq_map,
+       .xlate  = irq_domain_xlate_twocell,
+};
+
 int wm831x_irq_init(struct wm831x *wm831x, int irq)
 {
        struct wm831x_pdata *pdata = wm831x->dev->platform_data;
-       int i, cur_irq, ret;
+       struct irq_domain *domain;
+       int i, ret, irq_base;
 
        mutex_init(&wm831x->irq_lock);
 
@@ -543,18 +586,33 @@ int wm831x_irq_init(struct wm831x *wm831x, int irq)
        }
 
        /* Try to dynamically allocate IRQs if no base is specified */
-       if (!pdata || !pdata->irq_base)
-               wm831x->irq_base = -1;
+       if (pdata && pdata->irq_base) {
+               irq_base = irq_alloc_descs(pdata->irq_base, 0,
+                                          WM831X_NUM_IRQS, 0);
+               if (irq_base < 0) {
+                       dev_warn(wm831x->dev, "Failed to allocate IRQs: %d\n",
+                                irq_base);
+                       irq_base = 0;
+               }
+       } else {
+               irq_base = 0;
+       }
+
+       if (irq_base)
+               domain = irq_domain_add_legacy(wm831x->dev->of_node,
+                                              ARRAY_SIZE(wm831x_irqs),
+                                              irq_base, 0,
+                                              &wm831x_irq_domain_ops,
+                                              wm831x);
        else
-               wm831x->irq_base = pdata->irq_base;
+               domain = irq_domain_add_linear(wm831x->dev->of_node,
+                                              ARRAY_SIZE(wm831x_irqs),
+                                              &wm831x_irq_domain_ops,
+                                              wm831x);
 
-       wm831x->irq_base = irq_alloc_descs(wm831x->irq_base, 0,
-                                          WM831X_NUM_IRQS, 0);
-       if (wm831x->irq_base < 0) {
-               dev_warn(wm831x->dev, "Failed to allocate IRQs: %d\n",
-                        wm831x->irq_base);
-               wm831x->irq_base = 0;
-               return 0;
+       if (!domain) {
+               dev_warn(wm831x->dev, "Failed to allocate IRQ domain\n");
+               return -EINVAL;
        }
 
        if (pdata && pdata->irq_cmos)
@@ -565,38 +623,22 @@ int wm831x_irq_init(struct wm831x *wm831x, int irq)
        wm831x_set_bits(wm831x, WM831X_IRQ_CONFIG,
                        WM831X_IRQ_OD, i);
 
-       /* Try to flag /IRQ as a wake source; there are a number of
-        * unconditional wake sources in the PMIC so this isn't
-        * conditional but we don't actually care *too* much if it
-        * fails.
-        */
-       ret = enable_irq_wake(irq);
-       if (ret != 0) {
-               dev_warn(wm831x->dev, "Can't enable IRQ as wake source: %d\n",
-                        ret);
-       }
-
        wm831x->irq = irq;
-
-       /* Register them with genirq */
-       for (cur_irq = wm831x->irq_base;
-            cur_irq < ARRAY_SIZE(wm831x_irqs) + wm831x->irq_base;
-            cur_irq++) {
-               irq_set_chip_data(cur_irq, wm831x);
-               irq_set_chip_and_handler(cur_irq, &wm831x_irq_chip,
-                                        handle_edge_irq);
-               irq_set_nested_thread(cur_irq, 1);
-
-               /* ARM needs us to explicitly flag the IRQ as valid
-                * and will set them noprobe when we do so. */
-#ifdef CONFIG_ARM
-               set_irq_flags(cur_irq, IRQF_VALID);
-#else
-               irq_set_noprobe(cur_irq);
-#endif
-       }
+       wm831x->irq_domain = domain;
 
        if (irq) {
+               /* Try to flag /IRQ as a wake source; there are a number of
+                * unconditional wake sources in the PMIC so this isn't
+                * conditional but we don't actually care *too* much if it
+                * fails.
+                */
+               ret = enable_irq_wake(irq);
+               if (ret != 0) {
+                       dev_warn(wm831x->dev,
+                                "Can't enable IRQ as wake source: %d\n",
+                                ret);
+               }
+
                ret = request_threaded_irq(irq, NULL, wm831x_irq_thread,
                                           IRQF_TRIGGER_LOW | IRQF_ONESHOT,
                                           "wm831x", wm831x);
index dd1caaac55e4d4138ccc69b19997793418a76e7d..8a9b11ca076ac3c528e914b52dd78d958b714fae 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/device.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
+#include <linux/regmap.h>
 #include <linux/workqueue.h>
 
 #include <linux/mfd/wm8350/core.h>
@@ -74,7 +75,7 @@ static int wm8350_phys_read(struct wm8350 *wm8350, u8 reg, int num_regs,
        int bytes = num_regs * 2;
 
        dev_dbg(wm8350->dev, "volatile read\n");
-       ret = wm8350->read_dev(wm8350, reg, bytes, (char *)dest);
+       ret = regmap_raw_read(wm8350->regmap, reg, dest, bytes);
 
        for (i = reg; i < reg + num_regs; i++) {
                /* Cache is CPU endian */
@@ -96,9 +97,6 @@ static int wm8350_read(struct wm8350 *wm8350, u8 reg, int num_regs, u16 *dest)
        int ret = 0;
        int bytes = num_regs * 2;
 
-       if (wm8350->read_dev == NULL)
-               return -ENODEV;
-
        if ((reg + num_regs - 1) > WM8350_MAX_REGISTER) {
                dev_err(wm8350->dev, "invalid reg %x\n",
                        reg + num_regs - 1);
@@ -149,9 +147,6 @@ static int wm8350_write(struct wm8350 *wm8350, u8 reg, int num_regs, u16 *src)
        int end = reg + num_regs;
        int bytes = num_regs * 2;
 
-       if (wm8350->write_dev == NULL)
-               return -ENODEV;
-
        if ((reg + num_regs - 1) > WM8350_MAX_REGISTER) {
                dev_err(wm8350->dev, "invalid reg %x\n",
                        reg + num_regs - 1);
@@ -182,7 +177,7 @@ static int wm8350_write(struct wm8350 *wm8350, u8 reg, int num_regs, u16 *src)
        }
 
        /* Actually write it out */
-       return wm8350->write_dev(wm8350, reg, bytes, (char *)src);
+       return regmap_raw_write(wm8350->regmap, reg, src, bytes);
 }
 
 /*
@@ -515,9 +510,8 @@ static int wm8350_create_cache(struct wm8350 *wm8350, int type, int mode)
         * a PMIC so the device many not be in a virgin state and we
         * can't rely on the silicon values.
         */
-       ret = wm8350->read_dev(wm8350, 0,
-                              sizeof(u16) * (WM8350_MAX_REGISTER + 1),
-                              wm8350->reg_cache);
+       ret = regmap_raw_read(wm8350->regmap, 0, wm8350->reg_cache,
+                             sizeof(u16) * (WM8350_MAX_REGISTER + 1));
        if (ret < 0) {
                dev_err(wm8350->dev,
                        "failed to read initial cache values\n");
@@ -570,35 +564,30 @@ int wm8350_device_init(struct wm8350 *wm8350, int irq,
                       struct wm8350_platform_data *pdata)
 {
        int ret;
-       u16 id1, id2, mask_rev;
-       u16 cust_id, mode, chip_rev;
+       unsigned int id1, id2, mask_rev;
+       unsigned int cust_id, mode, chip_rev;
 
        dev_set_drvdata(wm8350->dev, wm8350);
 
        /* get WM8350 revision and config mode */
-       ret = wm8350->read_dev(wm8350, WM8350_RESET_ID, sizeof(id1), &id1);
+       ret = regmap_read(wm8350->regmap, WM8350_RESET_ID, &id1);
        if (ret != 0) {
                dev_err(wm8350->dev, "Failed to read ID: %d\n", ret);
                goto err;
        }
 
-       ret = wm8350->read_dev(wm8350, WM8350_ID, sizeof(id2), &id2);
+       ret = regmap_read(wm8350->regmap, WM8350_ID, &id2);
        if (ret != 0) {
                dev_err(wm8350->dev, "Failed to read ID: %d\n", ret);
                goto err;
        }
 
-       ret = wm8350->read_dev(wm8350, WM8350_REVISION, sizeof(mask_rev),
-                              &mask_rev);
+       ret = regmap_read(wm8350->regmap, WM8350_REVISION, &mask_rev);
        if (ret != 0) {
                dev_err(wm8350->dev, "Failed to read revision: %d\n", ret);
                goto err;
        }
 
-       id1 = be16_to_cpu(id1);
-       id2 = be16_to_cpu(id2);
-       mask_rev = be16_to_cpu(mask_rev);
-
        if (id1 != 0x6143) {
                dev_err(wm8350->dev,
                        "Device with ID %x is not a WM8350\n", id1);
index d955faaf27c4a8f824301ae5e3e34998f4333bf0..a68aceb4e48c880fbf7b79e64459762241eeaa29 100644 (file)
 
 #include <linux/module.h>
 #include <linux/moduleparam.h>
+#include <linux/err.h>
 #include <linux/init.h>
 #include <linux/i2c.h>
 #include <linux/platform_device.h>
 #include <linux/mfd/wm8350/core.h>
+#include <linux/regmap.h>
 #include <linux/slab.h>
 
-static int wm8350_i2c_read_device(struct wm8350 *wm8350, char reg,
-                                 int bytes, void *dest)
-{
-       int ret;
-
-       ret = i2c_master_send(wm8350->i2c_client, &reg, 1);
-       if (ret < 0)
-               return ret;
-       ret = i2c_master_recv(wm8350->i2c_client, dest, bytes);
-       if (ret < 0)
-               return ret;
-       if (ret != bytes)
-               return -EIO;
-       return 0;
-}
-
-static int wm8350_i2c_write_device(struct wm8350 *wm8350, char reg,
-                                  int bytes, void *src)
-{
-       /* we add 1 byte for device register */
-       u8 msg[(WM8350_MAX_REGISTER << 1) + 1];
-       int ret;
-
-       if (bytes > ((WM8350_MAX_REGISTER << 1) + 1))
-               return -EINVAL;
-
-       msg[0] = reg;
-       memcpy(&msg[1], src, bytes);
-       ret = i2c_master_send(wm8350->i2c_client, msg, bytes + 1);
-       if (ret < 0)
-               return ret;
-       if (ret != bytes + 1)
-               return -EIO;
-       return 0;
-}
+static const struct regmap_config wm8350_regmap = {
+       .reg_bits = 8,
+       .val_bits = 16,
+};
 
 static int wm8350_i2c_probe(struct i2c_client *i2c,
                            const struct i2c_device_id *id)
@@ -67,20 +38,18 @@ static int wm8350_i2c_probe(struct i2c_client *i2c,
        if (wm8350 == NULL)
                return -ENOMEM;
 
+       wm8350->regmap = devm_regmap_init_i2c(i2c, &wm8350_regmap);
+       if (IS_ERR(wm8350->regmap)) {
+               ret = PTR_ERR(wm8350->regmap);
+               dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+                       ret);
+               return ret;
+       }
+
        i2c_set_clientdata(i2c, wm8350);
        wm8350->dev = &i2c->dev;
-       wm8350->i2c_client = i2c;
-       wm8350->read_dev = wm8350_i2c_read_device;
-       wm8350->write_dev = wm8350_i2c_write_device;
-
-       ret = wm8350_device_init(wm8350, i2c->irq, i2c->dev.platform_data);
-       if (ret < 0)
-               goto err;
-
-       return ret;
 
-err:
-       return ret;
+       return wm8350_device_init(wm8350, i2c->irq, i2c->dev.platform_data);
 }
 
 static int wm8350_i2c_remove(struct i2c_client *i2c)
index 1189a17f0f25f6362b5d97c2ff65e8d7cd685c5f..4b7d378551d58daf515532dbcaea2c6f35c17f1e 100644 (file)
 #include <linux/regmap.h>
 #include <linux/slab.h>
 
-static struct {
-       u16  readable;    /* Mask of readable bits */
-       u16  writable;    /* Mask of writable bits */
-       u16  vol;         /* Mask of volatile bits */
-       int  is_codec;    /* Register controlled by codec reset */
-       u16  default_val; /* Value on reset */
-} reg_data[] = {
-       { 0xFFFF, 0xFFFF, 0x0000, 0, 0x6172 }, /* R0 */
-       { 0x7000, 0x0000, 0x8000, 0, 0x0000 }, /* R1 */
-       { 0xFF17, 0xFF17, 0x0000, 0, 0x0000 }, /* R2 */
-       { 0xEBF3, 0xEBF3, 0x0000, 1, 0x6000 }, /* R3 */
-       { 0x3CF3, 0x3CF3, 0x0000, 1, 0x0000 }, /* R4  */
-       { 0xF1F8, 0xF1F8, 0x0000, 1, 0x4050 }, /* R5  */
-       { 0xFC1F, 0xFC1F, 0x0000, 1, 0x4000 }, /* R6  */
-       { 0xDFDE, 0xDFDE, 0x0000, 1, 0x01C8 }, /* R7  */
-       { 0xFCFC, 0xFCFC, 0x0000, 1, 0x0000 }, /* R8  */
-       { 0xEFFF, 0xEFFF, 0x0000, 1, 0x0040 }, /* R9  */
-       { 0xEFFF, 0xEFFF, 0x0000, 1, 0x0040 }, /* R10 */
-       { 0x27F7, 0x27F7, 0x0000, 1, 0x0004 }, /* R11 */
-       { 0x01FF, 0x01FF, 0x0000, 1, 0x00C0 }, /* R12 */
-       { 0x01FF, 0x01FF, 0x0000, 1, 0x00C0 }, /* R13 */
-       { 0x1FEF, 0x1FEF, 0x0000, 1, 0x0000 }, /* R14 */
-       { 0x0163, 0x0163, 0x0000, 1, 0x0100 }, /* R15 */
-       { 0x01FF, 0x01FF, 0x0000, 1, 0x00C0 }, /* R16 */
-       { 0x01FF, 0x01FF, 0x0000, 1, 0x00C0 }, /* R17 */
-       { 0x1FFF, 0x0FFF, 0x0000, 1, 0x0000 }, /* R18 */
-       { 0xFFFF, 0xFFFF, 0x0000, 1, 0x1000 }, /* R19 */
-       { 0xFFFF, 0xFFFF, 0x0000, 1, 0x1010 }, /* R20 */
-       { 0xFFFF, 0xFFFF, 0x0000, 1, 0x1010 }, /* R21 */
-       { 0x0FDD, 0x0FDD, 0x0000, 1, 0x8000 }, /* R22 */
-       { 0x1FFF, 0x1FFF, 0x0000, 1, 0x0800 }, /* R23 */
-       { 0x0000, 0x01DF, 0x0000, 1, 0x008B }, /* R24 */
-       { 0x0000, 0x01DF, 0x0000, 1, 0x008B }, /* R25 */
-       { 0x0000, 0x01DF, 0x0000, 1, 0x008B }, /* R26 */
-       { 0x0000, 0x01DF, 0x0000, 1, 0x008B }, /* R27 */
-       { 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R28 */
-       { 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R29 */
-       { 0x0000, 0x0077, 0x0000, 1, 0x0066 }, /* R30 */
-       { 0x0000, 0x0033, 0x0000, 1, 0x0022 }, /* R31 */
-       { 0x0000, 0x01FF, 0x0000, 1, 0x0079 }, /* R32 */
-       { 0x0000, 0x01FF, 0x0000, 1, 0x0079 }, /* R33 */
-       { 0x0000, 0x0003, 0x0000, 1, 0x0003 }, /* R34 */
-       { 0x0000, 0x01FF, 0x0000, 1, 0x0003 }, /* R35 */
-       { 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R36 */
-       { 0x0000, 0x003F, 0x0000, 1, 0x0100 }, /* R37 */
-       { 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R38 */
-       { 0x0000, 0x000F, 0x0000, 0, 0x0000 }, /* R39 */
-       { 0x0000, 0x00FF, 0x0000, 1, 0x0000 }, /* R40 */
-       { 0x0000, 0x01B7, 0x0000, 1, 0x0000 }, /* R41 */
-       { 0x0000, 0x01B7, 0x0000, 1, 0x0000 }, /* R42 */
-       { 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R43 */
-       { 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R44 */
-       { 0x0000, 0x00FD, 0x0000, 1, 0x0000 }, /* R45 */
-       { 0x0000, 0x00FD, 0x0000, 1, 0x0000 }, /* R46 */
-       { 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R47 */
-       { 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R48 */
-       { 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R49 */
-       { 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R50 */
-       { 0x0000, 0x01B3, 0x0000, 1, 0x0180 }, /* R51 */
-       { 0x0000, 0x0077, 0x0000, 1, 0x0000 }, /* R52 */
-       { 0x0000, 0x0077, 0x0000, 1, 0x0000 }, /* R53 */
-       { 0x0000, 0x00FF, 0x0000, 1, 0x0000 }, /* R54 */
-       { 0x0000, 0x0001, 0x0000, 1, 0x0000 }, /* R55 */
-       { 0x0000, 0x003F, 0x0000, 1, 0x0000 }, /* R56 */
-       { 0x0000, 0x004F, 0x0000, 1, 0x0000 }, /* R57 */
-       { 0x0000, 0x00FD, 0x0000, 1, 0x0000 }, /* R58 */
-       { 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R59 */
-       { 0x1FFF, 0x1FFF, 0x0000, 1, 0x0000 }, /* R60 */
-       { 0xFFFF, 0xFFFF, 0x0000, 1, 0x0000 }, /* R61 */
-       { 0x03FF, 0x03FF, 0x0000, 1, 0x0000 }, /* R62 */
-       { 0x007F, 0x007F, 0x0000, 1, 0x0000 }, /* R63 */
-       { 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R64 */
-       { 0xDFFF, 0xDFFF, 0x0000, 0, 0x0000 }, /* R65 */
-       { 0xDFFF, 0xDFFF, 0x0000, 0, 0x0000 }, /* R66 */
-       { 0xDFFF, 0xDFFF, 0x0000, 0, 0x0000 }, /* R67 */
-       { 0xDFFF, 0xDFFF, 0x0000, 0, 0x0000 }, /* R68 */
-       { 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R69 */
-       { 0xFFFF, 0xFFFF, 0x0000, 0, 0x4400 }, /* R70 */
-       { 0x23FF, 0x23FF, 0x0000, 0, 0x0000 }, /* R71 */
-       { 0xFFFF, 0xFFFF, 0x0000, 0, 0x4400 }, /* R72 */
-       { 0x23FF, 0x23FF, 0x0000, 0, 0x0000 }, /* R73 */
-       { 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R74 */
-       { 0x000E, 0x000E, 0x0000, 0, 0x0008 }, /* R75 */
-       { 0xE00F, 0xE00F, 0x0000, 0, 0x0000 }, /* R76 */
-       { 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R77 */
-       { 0x03C0, 0x03C0, 0x0000, 0, 0x02C0 }, /* R78 */
-       { 0xFFFF, 0x0000, 0xffff, 0, 0x0000 }, /* R79 */
-       { 0xFFFF, 0xFFFF, 0x0000, 0, 0x0000 }, /* R80 */
-       { 0xFFFF, 0x0000, 0xffff, 0, 0x0000 }, /* R81 */
-       { 0x2BFF, 0x0000, 0xffff, 0, 0x0000 }, /* R82 */
-       { 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R83 */
-       { 0x80FF, 0x80FF, 0x0000, 0, 0x00ff }, /* R84 */
-};
-
-static int wm8400_read(struct wm8400 *wm8400, u8 reg, int num_regs, u16 *dest)
+static bool wm8400_volatile(struct device *dev, unsigned int reg)
 {
-       int i, ret = 0;
-
-       BUG_ON(reg + num_regs > ARRAY_SIZE(wm8400->reg_cache));
-
-       /* If there are any volatile reads then read back the entire block */
-       for (i = reg; i < reg + num_regs; i++)
-               if (reg_data[i].vol) {
-                       ret = regmap_bulk_read(wm8400->regmap, reg, dest,
-                                              num_regs);
-                       return ret;
-               }
-
-       /* Otherwise use the cache */
-       memcpy(dest, &wm8400->reg_cache[reg], num_regs * sizeof(u16));
-
-       return 0;
-}
-
-static int wm8400_write(struct wm8400 *wm8400, u8 reg, int num_regs,
-                       u16 *src)
-{
-       int ret, i;
-
-       BUG_ON(reg + num_regs > ARRAY_SIZE(wm8400->reg_cache));
-
-       for (i = 0; i < num_regs; i++) {
-               BUG_ON(!reg_data[reg + i].writable);
-               wm8400->reg_cache[reg + i] = src[i];
-               ret = regmap_write(wm8400->regmap, reg, src[i]);
-               if (ret != 0)
-                       return ret;
+       switch (reg) {
+       case WM8400_INTERRUPT_STATUS_1:
+       case WM8400_INTERRUPT_LEVELS:
+       case WM8400_SHUTDOWN_REASON:
+               return true;
+       default:
+               return false;
        }
-
-       return 0;
 }
 
 /**
@@ -165,13 +45,12 @@ static int wm8400_write(struct wm8400 *wm8400, u8 reg, int num_regs,
  */
 u16 wm8400_reg_read(struct wm8400 *wm8400, u8 reg)
 {
-       u16 val;
-
-       mutex_lock(&wm8400->io_lock);
-
-       wm8400_read(wm8400, reg, 1, &val);
+       unsigned int val;
+       int ret;
 
-       mutex_unlock(&wm8400->io_lock);
+       ret = regmap_read(wm8400->regmap, reg, &val);
+       if (ret < 0)
+               return ret;
 
        return val;
 }
@@ -179,63 +58,10 @@ EXPORT_SYMBOL_GPL(wm8400_reg_read);
 
 int wm8400_block_read(struct wm8400 *wm8400, u8 reg, int count, u16 *data)
 {
-       int ret;
-
-       mutex_lock(&wm8400->io_lock);
-
-       ret = wm8400_read(wm8400, reg, count, data);
-
-       mutex_unlock(&wm8400->io_lock);
-
-       return ret;
+       return regmap_bulk_read(wm8400->regmap, reg, data, count);
 }
 EXPORT_SYMBOL_GPL(wm8400_block_read);
 
-/**
- * wm8400_set_bits - Bitmask write
- *
- * @wm8400: Pointer to wm8400 control structure
- * @reg:    Register to access
- * @mask:   Mask of bits to change
- * @val:    Value to set for masked bits
- */
-int wm8400_set_bits(struct wm8400 *wm8400, u8 reg, u16 mask, u16 val)
-{
-       u16 tmp;
-       int ret;
-
-       mutex_lock(&wm8400->io_lock);
-
-       ret = wm8400_read(wm8400, reg, 1, &tmp);
-       tmp = (tmp & ~mask) | val;
-       if (ret == 0)
-               ret = wm8400_write(wm8400, reg, 1, &tmp);
-
-       mutex_unlock(&wm8400->io_lock);
-
-       return ret;
-}
-EXPORT_SYMBOL_GPL(wm8400_set_bits);
-
-/**
- * wm8400_reset_codec_reg_cache - Reset cached codec registers to
- * their default values.
- */
-void wm8400_reset_codec_reg_cache(struct wm8400 *wm8400)
-{
-       int i;
-
-       mutex_lock(&wm8400->io_lock);
-
-       /* Reset all codec registers to their initial value */
-       for (i = 0; i < ARRAY_SIZE(wm8400->reg_cache); i++)
-               if (reg_data[i].is_codec)
-                       wm8400->reg_cache[i] = reg_data[i].default_val;
-
-       mutex_unlock(&wm8400->io_lock);
-}
-EXPORT_SYMBOL_GPL(wm8400_reset_codec_reg_cache);
-
 static int wm8400_register_codec(struct wm8400 *wm8400)
 {
        struct mfd_cell cell = {
@@ -257,44 +83,24 @@ static int wm8400_register_codec(struct wm8400 *wm8400)
 static int wm8400_init(struct wm8400 *wm8400,
                       struct wm8400_platform_data *pdata)
 {
-       u16 reg;
-       int ret, i;
-
-       mutex_init(&wm8400->io_lock);
+       unsigned int reg;
+       int ret;
 
        dev_set_drvdata(wm8400->dev, wm8400);
 
        /* Check that this is actually a WM8400 */
-       ret = regmap_read(wm8400->regmap, WM8400_RESET_ID, &i);
+       ret = regmap_read(wm8400->regmap, WM8400_RESET_ID, &reg);
        if (ret != 0) {
                dev_err(wm8400->dev, "Chip ID register read failed\n");
                return -EIO;
        }
-       if (i != reg_data[WM8400_RESET_ID].default_val) {
-               dev_err(wm8400->dev, "Device is not a WM8400, ID is %x\n", i);
+       if (reg != 0x6172) {
+               dev_err(wm8400->dev, "Device is not a WM8400, ID is %x\n",
+                       reg);
                return -ENODEV;
        }
 
-       /* We don't know what state the hardware is in and since this
-        * is a PMIC we can't reset it safely so initialise the register
-        * cache from the hardware.
-        */
-       ret = regmap_raw_read(wm8400->regmap, 0, wm8400->reg_cache,
-                             ARRAY_SIZE(wm8400->reg_cache));
-       if (ret != 0) {
-               dev_err(wm8400->dev, "Register cache read failed\n");
-               return -EIO;
-       }
-       for (i = 0; i < ARRAY_SIZE(wm8400->reg_cache); i++)
-               wm8400->reg_cache[i] = be16_to_cpu(wm8400->reg_cache[i]);
-
-       /* If the codec is in reset use hard coded values */
-       if (!(wm8400->reg_cache[WM8400_POWER_MANAGEMENT_1] & WM8400_CODEC_ENA))
-               for (i = 0; i < ARRAY_SIZE(wm8400->reg_cache); i++)
-                       if (reg_data[i].is_codec)
-                               wm8400->reg_cache[i] = reg_data[i].default_val;
-
-       ret = wm8400_read(wm8400, WM8400_ID, 1, &reg);
+       ret = regmap_read(wm8400->regmap, WM8400_ID, &reg);
        if (ret != 0) {
                dev_err(wm8400->dev, "ID register read failed: %d\n", ret);
                return ret;
@@ -334,8 +140,22 @@ static const struct regmap_config wm8400_regmap_config = {
        .reg_bits = 8,
        .val_bits = 16,
        .max_register = WM8400_REGISTER_COUNT - 1,
+
+       .volatile_reg = wm8400_volatile,
+
+       .cache_type = REGCACHE_RBTREE,
 };
 
+/**
+ * wm8400_reset_codec_reg_cache - Reset cached codec registers to
+ * their default values.
+ */
+void wm8400_reset_codec_reg_cache(struct wm8400 *wm8400)
+{
+       regmap_reinit_cache(wm8400->regmap, &wm8400_regmap_config);
+}
+EXPORT_SYMBOL_GPL(wm8400_reset_codec_reg_cache);
+
 #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
 static int wm8400_i2c_probe(struct i2c_client *i2c,
                            const struct i2c_device_id *id)
index 9d7ca1e978fad30775d1de8b6913333a137118c6..1e321d349777199dad0bdd050288f72af60917a6 100644 (file)
@@ -500,7 +500,8 @@ static __devinit int wm8994_device_init(struct wm8994 *wm8994, int irq)
                        ret);
                goto err_enable;
        }
-       wm8994->revision = ret;
+       wm8994->revision = ret & WM8994_CHIP_REV_MASK;
+       wm8994->cust_id = (ret & WM8994_CUST_ID_MASK) >> WM8994_CUST_ID_SHIFT;
 
        switch (wm8994->type) {
        case WM8994:
@@ -553,8 +554,8 @@ static __devinit int wm8994_device_init(struct wm8994 *wm8994, int irq)
                break;
        }
 
-       dev_info(wm8994->dev, "%s revision %c\n", devname,
-                'A' + wm8994->revision);
+       dev_info(wm8994->dev, "%s revision %c CUST_ID %02x\n", devname,
+                'A' + wm8994->revision, wm8994->cust_id);
 
        switch (wm8994->type) {
        case WM1811:
@@ -732,23 +733,7 @@ static struct i2c_driver wm8994_i2c_driver = {
        .id_table = wm8994_i2c_id,
 };
 
-static int __init wm8994_i2c_init(void)
-{
-       int ret;
-
-       ret = i2c_add_driver(&wm8994_i2c_driver);
-       if (ret != 0)
-               pr_err("Failed to register wm8994 I2C driver: %d\n", ret);
-
-       return ret;
-}
-module_init(wm8994_i2c_init);
-
-static void __exit wm8994_i2c_exit(void)
-{
-       i2c_del_driver(&wm8994_i2c_driver);
-}
-module_exit(wm8994_i2c_exit);
+module_i2c_driver(wm8994_i2c_driver);
 
 MODULE_DESCRIPTION("Core support for the WM8994 audio CODEC");
 MODULE_LICENSE("GPL");
index bfd25af6ecb106e75eddb4cdc0afad561238506f..52e9e29449403b1d5b21dc215973f7e4b8de41e6 100644 (file)
@@ -1122,7 +1122,6 @@ static bool wm8994_volatile_register(struct device *dev, unsigned int reg)
        case WM8994_RATE_STATUS:
        case WM8958_MIC_DETECT_3:
        case WM8994_DC_SERVO_4E:
-       case WM8994_CHIP_REVISION:
        case WM8994_INTERRUPT_STATUS_1:
        case WM8994_INTERRUPT_STATUS_2:
                return true;
index d7a9aa14e5d5aafd8c0efc907b127147a202d197..042a8fe4efaabd2ab57db4299275c19750cad23d 100644 (file)
@@ -142,10 +142,16 @@ static int __devexit ab8500_pwm_remove(struct platform_device *pdev)
        return 0;
 }
 
+static const struct of_device_id ab8500_pwm_match[] = {
+       { .compatible = "stericsson,ab8500-pwm", },
+       {}
+};
+
 static struct platform_driver ab8500_pwm_driver = {
        .driver = {
                .name = "ab8500-pwm",
                .owner = THIS_MODULE,
+               .of_match_table = ab8500_pwm_match,
        },
        .probe = ab8500_pwm_probe,
        .remove = __devexit_p(ab8500_pwm_remove),
index b5401e355745bfd4ce1e92732151e5fd34e0e9e0..c03456f17004017ca0cb1f65513343cd8d470b7c 100644 (file)
@@ -19,9 +19,9 @@
 #include <linux/mtd/cfi.h>
 #include <linux/platform_device.h>
 #include <linux/mtd/physmap.h>
+#include <linux/of.h>
 
 #include <lantiq_soc.h>
-#include <lantiq_platform.h>
 
 /*
  * The NOR flash is connected to the same external bus unit (EBU) as PCI.
@@ -44,8 +44,9 @@ struct ltq_mtd {
        struct map_info *map;
 };
 
-static char ltq_map_name[] = "ltq_nor";
-static const char *ltq_probe_types[] __devinitconst = { "cmdlinepart", NULL };
+static const char ltq_map_name[] = "ltq_nor";
+static const char *ltq_probe_types[] __devinitconst = {
+                                       "cmdlinepart", "ofpart", NULL };
 
 static map_word
 ltq_read16(struct map_info *map, unsigned long adr)
@@ -108,42 +109,38 @@ ltq_copy_to(struct map_info *map, unsigned long to,
        spin_unlock_irqrestore(&ebu_lock, flags);
 }
 
-static int __init
+static int __devinit
 ltq_mtd_probe(struct platform_device *pdev)
 {
-       struct physmap_flash_data *ltq_mtd_data = dev_get_platdata(&pdev->dev);
+       struct mtd_part_parser_data ppdata;
        struct ltq_mtd *ltq_mtd;
-       struct resource *res;
        struct cfi_private *cfi;
        int err;
 
+       if (of_machine_is_compatible("lantiq,falcon") &&
+                       (ltq_boot_select() != BS_FLASH)) {
+               dev_err(&pdev->dev, "invalid bootstrap options\n");
+               return -ENODEV;
+       }
+
        ltq_mtd = kzalloc(sizeof(struct ltq_mtd), GFP_KERNEL);
        platform_set_drvdata(pdev, ltq_mtd);
 
        ltq_mtd->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        if (!ltq_mtd->res) {
-               dev_err(&pdev->dev, "failed to get memory resource");
+               dev_err(&pdev->dev, "failed to get memory resource\n");
                err = -ENOENT;
                goto err_out;
        }
 
-       res = devm_request_mem_region(&pdev->dev, ltq_mtd->res->start,
-               resource_size(ltq_mtd->res), dev_name(&pdev->dev));
-       if (!ltq_mtd->res) {
-               dev_err(&pdev->dev, "failed to request mem resource");
-               err = -EBUSY;
-               goto err_out;
-       }
-
        ltq_mtd->map = kzalloc(sizeof(struct map_info), GFP_KERNEL);
-       ltq_mtd->map->phys = res->start;
-       ltq_mtd->map->size = resource_size(res);
-       ltq_mtd->map->virt = devm_ioremap_nocache(&pdev->dev,
-                               ltq_mtd->map->phys, ltq_mtd->map->size);
+       ltq_mtd->map->phys = ltq_mtd->res->start;
+       ltq_mtd->map->size = resource_size(ltq_mtd->res);
+       ltq_mtd->map->virt = devm_request_and_ioremap(&pdev->dev, ltq_mtd->res);
        if (!ltq_mtd->map->virt) {
-               dev_err(&pdev->dev, "failed to ioremap!\n");
-               err = -ENOMEM;
-               goto err_free;
+               dev_err(&pdev->dev, "failed to remap mem resource\n");
+               err = -EBUSY;
+               goto err_out;
        }
 
        ltq_mtd->map->name = ltq_map_name;
@@ -169,9 +166,9 @@ ltq_mtd_probe(struct platform_device *pdev)
        cfi->addr_unlock1 ^= 1;
        cfi->addr_unlock2 ^= 1;
 
-       err = mtd_device_parse_register(ltq_mtd->mtd, ltq_probe_types, NULL,
-                                       ltq_mtd_data->parts,
-                                       ltq_mtd_data->nr_parts);
+       ppdata.of_node = pdev->dev.of_node;
+       err = mtd_device_parse_register(ltq_mtd->mtd, ltq_probe_types,
+                                       &ppdata, NULL, 0);
        if (err) {
                dev_err(&pdev->dev, "failed to add partitions\n");
                goto err_destroy;
@@ -204,32 +201,23 @@ ltq_mtd_remove(struct platform_device *pdev)
        return 0;
 }
 
+static const struct of_device_id ltq_mtd_match[] = {
+       { .compatible = "lantiq,nor" },
+       {},
+};
+MODULE_DEVICE_TABLE(of, ltq_mtd_match);
+
 static struct platform_driver ltq_mtd_driver = {
+       .probe = ltq_mtd_probe,
        .remove = __devexit_p(ltq_mtd_remove),
        .driver = {
-               .name = "ltq_nor",
+               .name = "ltq-nor",
                .owner = THIS_MODULE,
+               .of_match_table = ltq_mtd_match,
        },
 };
 
-static int __init
-init_ltq_mtd(void)
-{
-       int ret = platform_driver_probe(&ltq_mtd_driver, ltq_mtd_probe);
-
-       if (ret)
-               pr_err("ltq_nor: error registering platform driver");
-       return ret;
-}
-
-static void __exit
-exit_ltq_mtd(void)
-{
-       platform_driver_unregister(&ltq_mtd_driver);
-}
-
-module_init(init_ltq_mtd);
-module_exit(exit_ltq_mtd);
+module_platform_driver(ltq_mtd_driver);
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("John Crispin <blogic@openwrt.org>");
index 4de73643fec676396835c3c7582c287d8648b322..d1827e887f4e9d82ce8d46ee33cfb61052ee2816 100644 (file)
@@ -1096,20 +1096,20 @@ static int __devinit r6040_init_one(struct pci_dev *pdev,
        if (err) {
                dev_err(&pdev->dev, "32-bit PCI DMA addresses"
                                "not supported by the card\n");
-               goto err_out;
+               goto err_out_disable_dev;
        }
        err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
        if (err) {
                dev_err(&pdev->dev, "32-bit PCI DMA addresses"
                                "not supported by the card\n");
-               goto err_out;
+               goto err_out_disable_dev;
        }
 
        /* IO Size check */
        if (pci_resource_len(pdev, bar) < io_size) {
                dev_err(&pdev->dev, "Insufficient PCI resources, aborting\n");
                err = -EIO;
-               goto err_out;
+               goto err_out_disable_dev;
        }
 
        pci_set_master(pdev);
@@ -1117,7 +1117,7 @@ static int __devinit r6040_init_one(struct pci_dev *pdev,
        dev = alloc_etherdev(sizeof(struct r6040_private));
        if (!dev) {
                err = -ENOMEM;
-               goto err_out;
+               goto err_out_disable_dev;
        }
        SET_NETDEV_DEV(dev, &pdev->dev);
        lp = netdev_priv(dev);
@@ -1233,11 +1233,15 @@ err_out_mdio_irq:
 err_out_mdio:
        mdiobus_free(lp->mii_bus);
 err_out_unmap:
+       netif_napi_del(&lp->napi);
+       pci_set_drvdata(pdev, NULL);
        pci_iounmap(pdev, ioaddr);
 err_out_free_res:
        pci_release_regions(pdev);
 err_out_free_dev:
        free_netdev(dev);
+err_out_disable_dev:
+       pci_disable_device(pdev);
 err_out:
        return err;
 }
@@ -1251,6 +1255,9 @@ static void __devexit r6040_remove_one(struct pci_dev *pdev)
        mdiobus_unregister(lp->mii_bus);
        kfree(lp->mii_bus->irq);
        mdiobus_free(lp->mii_bus);
+       netif_napi_del(&lp->napi);
+       pci_set_drvdata(pdev, NULL);
+       pci_iounmap(pdev, lp->base);
        pci_release_regions(pdev);
        free_netdev(dev);
        pci_disable_device(pdev);
index be3c22179161504f39eb2527b85cfa44eb2da685..667169b825263d96402b44ba22784a1696f80c82 100644 (file)
@@ -1101,8 +1101,12 @@ static int sh_eth_rx(struct net_device *ndev)
 
        /* Restart Rx engine if stopped. */
        /* If we don't need to check status, don't. -KDU */
-       if (!(sh_eth_read(ndev, EDRRR) & EDRRR_R))
+       if (!(sh_eth_read(ndev, EDRRR) & EDRRR_R)) {
+               /* fix the values for the next receiving */
+               mdp->cur_rx = mdp->dirty_rx = (sh_eth_read(ndev, RDFAR) -
+                                              sh_eth_read(ndev, RDLAR)) >> 4;
                sh_eth_write(ndev, EDRRR_R, EDRRR);
+       }
 
        return 0;
 }
@@ -1199,8 +1203,6 @@ static void sh_eth_error(struct net_device *ndev, int intr_status)
                /* Receive Descriptor Empty int */
                ndev->stats.rx_over_errors++;
 
-               if (sh_eth_read(ndev, EDRRR) ^ EDRRR_R)
-                       sh_eth_write(ndev, EDRRR_R, EDRRR);
                if (netif_msg_rx_err(mdp))
                        dev_err(&ndev->dev, "Receive Descriptor Empty\n");
        }
index dab9c6f671ec69a4ced1f1ea72c83b171f0ad8b0..1466e5d2af44a438e2cf04205edf908b997c6966 100644 (file)
@@ -2390,11 +2390,11 @@ static int __devinit smsc911x_drv_probe(struct platform_device *pdev)
 
        retval = smsc911x_request_resources(pdev);
        if (retval)
-               goto out_return_resources;
+               goto out_request_resources_fail;
 
        retval = smsc911x_enable_resources(pdev);
        if (retval)
-               goto out_disable_resources;
+               goto out_enable_resources_fail;
 
        if (pdata->ioaddr == NULL) {
                SMSC_WARN(pdata, probe, "Error smsc911x base address invalid");
@@ -2501,8 +2501,9 @@ out_free_irq:
        free_irq(dev->irq, dev);
 out_disable_resources:
        (void)smsc911x_disable_resources(pdev);
-out_return_resources:
+out_enable_resources_fail:
        smsc911x_free_resources(pdev);
+out_request_resources_fail:
        platform_set_drvdata(pdev, NULL);
        iounmap(pdata->ioaddr);
        free_netdev(dev);
index 71e2b0523bc2db243704abab76ce8bd32a47d6d9..3ae80eccd0efd9802e5e997d53cd54946ef6d788 100644 (file)
@@ -35,6 +35,7 @@
 #include <linux/crc32.h>
 #include <linux/usb/usbnet.h>
 #include <linux/slab.h>
+#include <linux/if_vlan.h>
 
 #define DRIVER_VERSION "22-Dec-2011"
 #define DRIVER_NAME "asix"
@@ -321,7 +322,7 @@ static int asix_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
                        return 0;
                }
 
-               if ((size > dev->net->mtu + ETH_HLEN) ||
+               if ((size > dev->net->mtu + ETH_HLEN + VLAN_HLEN) ||
                    (size + offset > skb->len)) {
                        netdev_err(dev->net, "asix_rx_fixup() Bad RX Length %d\n",
                                   size);
index 380dbea6109de022c97865775fb401e7bf199838..3b206786b5e7d8196d2fd1e63060f17cd7101c6e 100644 (file)
@@ -547,6 +547,8 @@ static const struct usb_device_id products[] = {
        {QMI_GOBI_DEVICE(0x16d8, 0x8002)},      /* CMDTech Gobi 2000 Modem device (VU922) */
        {QMI_GOBI_DEVICE(0x05c6, 0x9205)},      /* Gobi 2000 Modem device */
        {QMI_GOBI_DEVICE(0x1199, 0x9013)},      /* Sierra Wireless Gobi 3000 Modem device (MC8355) */
+       {QMI_GOBI_DEVICE(0x1199, 0x9015)},      /* Sierra Wireless Gobi 3000 Modem device */
+       {QMI_GOBI_DEVICE(0x1199, 0x9019)},      /* Sierra Wireless Gobi 3000 Modem device */
        { }                                     /* END */
 };
 MODULE_DEVICE_TABLE(usb, products);
index 9ce6995e8d084d046beb75f80712cd1b16a4aaf3..5214b1eceb9516282cb9ae8b38f79a606da0ecb7 100644 (file)
@@ -1231,11 +1231,6 @@ static int virtnet_freeze(struct virtio_device *vdev)
        vi->config_enable = false;
        mutex_unlock(&vi->config_lock);
 
-       virtqueue_disable_cb(vi->rvq);
-       virtqueue_disable_cb(vi->svq);
-       if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_CTRL_VQ))
-               virtqueue_disable_cb(vi->cvq);
-
        netif_device_detach(vi->dev);
        cancel_delayed_work_sync(&vi->refill);
 
index 0ba81a66061fca201ae7917e21b1ffd6c703a91d..fbaa309300764ef791faff16d06f85a28d7c431d 100644 (file)
@@ -2415,6 +2415,22 @@ ath5k_tx_complete_poll_work(struct work_struct *work)
 * Initialization routines *
 \*************************/
 
+static const struct ieee80211_iface_limit if_limits[] = {
+       { .max = 2048,  .types = BIT(NL80211_IFTYPE_STATION) },
+       { .max = 4,     .types =
+#ifdef CONFIG_MAC80211_MESH
+                                BIT(NL80211_IFTYPE_MESH_POINT) |
+#endif
+                                BIT(NL80211_IFTYPE_AP) },
+};
+
+static const struct ieee80211_iface_combination if_comb = {
+       .limits = if_limits,
+       .n_limits = ARRAY_SIZE(if_limits),
+       .max_interfaces = 2048,
+       .num_different_channels = 1,
+};
+
 int __devinit
 ath5k_init_ah(struct ath5k_hw *ah, const struct ath_bus_ops *bus_ops)
 {
@@ -2436,6 +2452,9 @@ ath5k_init_ah(struct ath5k_hw *ah, const struct ath_bus_ops *bus_ops)
                BIT(NL80211_IFTYPE_ADHOC) |
                BIT(NL80211_IFTYPE_MESH_POINT);
 
+       hw->wiphy->iface_combinations = &if_comb;
+       hw->wiphy->n_iface_combinations = 1;
+
        /* SW support for IBSS_RSN is provided by mac80211 */
        hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN;
 
index ac53d901801deb037db4ca11137812d1df6d458f..dfb0441f406c24e59c10fac636007d7337cd4b65 100644 (file)
@@ -3809,7 +3809,7 @@ static bool is_pmu_set(struct ath_hw *ah, u32 pmu_reg, int pmu_set)
        return true;
 }
 
-static void ar9003_hw_internal_regulator_apply(struct ath_hw *ah)
+void ar9003_hw_internal_regulator_apply(struct ath_hw *ah)
 {
        int internal_regulator =
                ath9k_hw_ar9300_get_eeprom(ah, EEP_INTERNAL_REGULATOR);
index 2505ac44f0c16ff248be27b145b91cd1b90dde49..8396d150ce01d4303174aab4a1e9071d70ca09de 100644 (file)
@@ -334,4 +334,7 @@ u8 *ar9003_get_spur_chan_ptr(struct ath_hw *ah, bool is_2ghz);
 
 unsigned int ar9003_get_paprd_scale_factor(struct ath_hw *ah,
                                           struct ath9k_channel *chan);
+
+void ar9003_hw_internal_regulator_apply(struct ath_hw *ah);
+
 #endif
index f11d9b2677fd05753750e1311a351ac7b7a52cfe..1bd3a3d22101806aca2b06ebc9be5ee77afdddd7 100644 (file)
@@ -1,5 +1,6 @@
 /*
- * Copyright (c) 2011 Atheros Communications Inc.
+ * Copyright (c) 2010-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2012 Qualcomm Atheros Inc.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -18,7 +19,7 @@
 #define INITVALS_9330_1P1_H
 
 static const u32 ar9331_1p1_baseband_postamble[][5] = {
-       /*  Addr      5G_HT20     5G_HT40     2G_HT40     2G_HT20  */
+       /* Addr      5G_HT20     5G_HT40     2G_HT40     2G_HT20   */
        {0x00009810, 0xd00a8005, 0xd00a8005, 0xd00a8005, 0xd00a8005},
        {0x00009820, 0x206a002e, 0x206a002e, 0x206a002e, 0x206a002e},
        {0x00009824, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0},
@@ -27,10 +28,10 @@ static const u32 ar9331_1p1_baseband_postamble[][5] = {
        {0x00009830, 0x0000059c, 0x0000059c, 0x0000059c, 0x0000059c},
        {0x00009c00, 0x00000044, 0x00000044, 0x00000044, 0x00000044},
        {0x00009e00, 0x0372161e, 0x0372161e, 0x037216a4, 0x037216a4},
-       {0x00009e04, 0x00182020, 0x00182020, 0x00182020, 0x00182020},
+       {0x00009e04, 0x00202020, 0x00202020, 0x00202020, 0x00202020},
        {0x00009e0c, 0x6c4000e2, 0x6d4000e2, 0x6d4000e2, 0x6c4000e2},
        {0x00009e10, 0x7ec80d2e, 0x7ec80d2e, 0x7ec80d2e, 0x7ec80d2e},
-       {0x00009e14, 0x31395d5e, 0x3139605e, 0x3139605e, 0x31395d5e},
+       {0x00009e14, 0x31365d5e, 0x3136605e, 0x3136605e, 0x31365d5e},
        {0x00009e18, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
        {0x00009e1c, 0x0001cf9c, 0x0001cf9c, 0x00021f9c, 0x00021f9c},
        {0x00009e20, 0x000003b5, 0x000003b5, 0x000003ce, 0x000003ce},
@@ -55,7 +56,7 @@ static const u32 ar9331_1p1_baseband_postamble[][5] = {
        {0x0000a288, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
        {0x0000a28c, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
        {0x0000a2c4, 0x00158d18, 0x00158d18, 0x00158d18, 0x00158d18},
-       {0x0000a2d0, 0x00071981, 0x00071981, 0x00071981, 0x00071981},
+       {0x0000a2d0, 0x00071982, 0x00071982, 0x00071982, 0x00071982},
        {0x0000a2d8, 0xf999a83a, 0xf999a83a, 0xf999a83a, 0xf999a83a},
        {0x0000a358, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
        {0x0000ae04, 0x00802020, 0x00802020, 0x00802020, 0x00802020},
@@ -63,7 +64,7 @@ static const u32 ar9331_1p1_baseband_postamble[][5] = {
 };
 
 static const u32 ar9331_modes_lowest_ob_db_tx_gain_1p1[][5] = {
-       /*   Addr     5G_HT20     5G_HT40     2G_HT40     2G_HT20  */
+       /* Addr      5G_HT20     5G_HT40     2G_HT40     2G_HT20   */
        {0x0000a2d8, 0x7999a83a, 0x7999a83a, 0x7999a83a, 0x7999a83a},
        {0x0000a2dc, 0xffff2a52, 0xffff2a52, 0xffff2a52, 0xffff2a52},
        {0x0000a2e0, 0xffffcc84, 0xffffcc84, 0xffffcc84, 0xffffcc84},
@@ -155,7 +156,7 @@ static const u32 ar9331_modes_lowest_ob_db_tx_gain_1p1[][5] = {
 };
 
 static const u32 ar9331_modes_high_ob_db_tx_gain_1p1[][5] = {
-       /*   Addr     5G_HT20     5G_HT40     2G_HT40     2G_HT20  */
+       /* Addr      5G_HT20     5G_HT40     2G_HT40     2G_HT20   */
        {0x0000a2d8, 0x7999a83a, 0x7999a83a, 0x7999a83a, 0x7999a83a},
        {0x0000a2dc, 0xffaa9a52, 0xffaa9a52, 0xffaa9a52, 0xffaa9a52},
        {0x0000a2e0, 0xffb31c84, 0xffb31c84, 0xffb31c84, 0xffb31c84},
@@ -245,7 +246,7 @@ static const u32 ar9331_modes_high_ob_db_tx_gain_1p1[][5] = {
 };
 
 static const u32 ar9331_modes_low_ob_db_tx_gain_1p1[][5] = {
-       /*   Addr     5G_HT20     5G_HT40     2G_HT40     2G_HT20  */
+       /* Addr      5G_HT20     5G_HT40     2G_HT40     2G_HT20   */
        {0x0000a2d8, 0x7999a83a, 0x7999a83a, 0x7999a83a, 0x7999a83a},
        {0x0000a2dc, 0xffff2a52, 0xffff2a52, 0xffff2a52, 0xffff2a52},
        {0x0000a2e0, 0xffffcc84, 0xffffcc84, 0xffffcc84, 0xffffcc84},
@@ -377,14 +378,14 @@ static const u32 ar9331_1p1_radio_core[][2] = {
        {0x000160b4, 0x92480040},
        {0x000160c0, 0x006db6db},
        {0x000160c4, 0x0186db60},
-       {0x000160c8, 0x6db6db6c},
+       {0x000160c8, 0x6db4db6c},
        {0x000160cc, 0x6de6c300},
        {0x000160d0, 0x14500820},
        {0x00016100, 0x04cb0001},
        {0x00016104, 0xfff80015},
        {0x00016108, 0x00080010},
        {0x0001610c, 0x00170000},
-       {0x00016140, 0x10804000},
+       {0x00016140, 0x10800000},
        {0x00016144, 0x01884080},
        {0x00016148, 0x000080c0},
        {0x00016280, 0x01000015},
@@ -417,7 +418,7 @@ static const u32 ar9331_1p1_radio_core[][2] = {
 };
 
 static const u32 ar9331_1p1_soc_postamble[][5] = {
-       /*  Addr      5G_HT20     5G_HT40     2G_HT40     2G_HT20  */
+       /* Addr      5G_HT20     5G_HT40     2G_HT40     2G_HT20   */
        {0x00007010, 0x00000022, 0x00000022, 0x00000022, 0x00000022},
 };
 
@@ -691,7 +692,7 @@ static const u32 ar9331_1p1_baseband_core[][2] = {
 };
 
 static const u32 ar9331_modes_high_power_tx_gain_1p1[][5] = {
-       /*  Addr      5G_HT20     5G_HT40     2G_HT40     2G_HT20  */
+       /* Addr      5G_HT20     5G_HT40     2G_HT40     2G_HT20   */
        {0x0000a2d8, 0x7999a83a, 0x7999a83a, 0x7999a83a, 0x7999a83a},
        {0x0000a2dc, 0xffff2a52, 0xffff2a52, 0xffff2a52, 0xffff2a52},
        {0x0000a2e0, 0xffffcc84, 0xffffcc84, 0xffffcc84, 0xffffcc84},
@@ -783,7 +784,7 @@ static const u32 ar9331_modes_high_power_tx_gain_1p1[][5] = {
 };
 
 static const u32 ar9331_1p1_mac_postamble[][5] = {
-       /*  Addr      5G_HT20     5G_HT40     2G_HT40     2G_HT20  */
+       /* Addr      5G_HT20     5G_HT40     2G_HT40     2G_HT20   */
        {0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160},
        {0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c},
        {0x000010b0, 0x00000e60, 0x00001cc0, 0x00007c70, 0x00003e38},
@@ -973,26 +974,27 @@ static const u32 ar9331_1p1_mac_core[][2] = {
 
 static const u32 ar9331_common_rx_gain_1p1[][2] = {
        /* Addr      allmodes  */
-       {0x0000a000, 0x00010000},
-       {0x0000a004, 0x00030002},
-       {0x0000a008, 0x00050004},
-       {0x0000a00c, 0x00810080},
-       {0x0000a010, 0x00830082},
-       {0x0000a014, 0x01810180},
-       {0x0000a018, 0x01830182},
-       {0x0000a01c, 0x01850184},
-       {0x0000a020, 0x01890188},
-       {0x0000a024, 0x018b018a},
-       {0x0000a028, 0x018d018c},
-       {0x0000a02c, 0x01910190},
-       {0x0000a030, 0x01930192},
-       {0x0000a034, 0x01950194},
-       {0x0000a038, 0x038a0196},
-       {0x0000a03c, 0x038c038b},
-       {0x0000a040, 0x0390038d},
-       {0x0000a044, 0x03920391},
-       {0x0000a048, 0x03940393},
-       {0x0000a04c, 0x03960395},
+       {0x00009e18, 0x05000000},
+       {0x0000a000, 0x00060005},
+       {0x0000a004, 0x00810080},
+       {0x0000a008, 0x00830082},
+       {0x0000a00c, 0x00850084},
+       {0x0000a010, 0x01820181},
+       {0x0000a014, 0x01840183},
+       {0x0000a018, 0x01880185},
+       {0x0000a01c, 0x018a0189},
+       {0x0000a020, 0x02850284},
+       {0x0000a024, 0x02890288},
+       {0x0000a028, 0x028b028a},
+       {0x0000a02c, 0x03850384},
+       {0x0000a030, 0x03890388},
+       {0x0000a034, 0x038b038a},
+       {0x0000a038, 0x038d038c},
+       {0x0000a03c, 0x03910390},
+       {0x0000a040, 0x03930392},
+       {0x0000a044, 0x03950394},
+       {0x0000a048, 0x00000396},
+       {0x0000a04c, 0x00000000},
        {0x0000a050, 0x00000000},
        {0x0000a054, 0x00000000},
        {0x0000a058, 0x00000000},
@@ -1005,15 +1007,15 @@ static const u32 ar9331_common_rx_gain_1p1[][2] = {
        {0x0000a074, 0x00000000},
        {0x0000a078, 0x00000000},
        {0x0000a07c, 0x00000000},
-       {0x0000a080, 0x22222229},
-       {0x0000a084, 0x1d1d1d1d},
-       {0x0000a088, 0x1d1d1d1d},
-       {0x0000a08c, 0x1d1d1d1d},
-       {0x0000a090, 0x171d1d1d},
-       {0x0000a094, 0x11111717},
-       {0x0000a098, 0x00030311},
-       {0x0000a09c, 0x00000000},
-       {0x0000a0a0, 0x00000000},
+       {0x0000a080, 0x28282828},
+       {0x0000a084, 0x28282828},
+       {0x0000a088, 0x28282828},
+       {0x0000a08c, 0x28282828},
+       {0x0000a090, 0x28282828},
+       {0x0000a094, 0x24242428},
+       {0x0000a098, 0x171e1e1e},
+       {0x0000a09c, 0x02020b0b},
+       {0x0000a0a0, 0x02020202},
        {0x0000a0a4, 0x00000000},
        {0x0000a0a8, 0x00000000},
        {0x0000a0ac, 0x00000000},
@@ -1021,27 +1023,27 @@ static const u32 ar9331_common_rx_gain_1p1[][2] = {
        {0x0000a0b4, 0x00000000},
        {0x0000a0b8, 0x00000000},
        {0x0000a0bc, 0x00000000},
-       {0x0000a0c0, 0x001f0000},
-       {0x0000a0c4, 0x01000101},
-       {0x0000a0c8, 0x011e011f},
-       {0x0000a0cc, 0x011c011d},
-       {0x0000a0d0, 0x02030204},
-       {0x0000a0d4, 0x02010202},
-       {0x0000a0d8, 0x021f0200},
-       {0x0000a0dc, 0x0302021e},
-       {0x0000a0e0, 0x03000301},
-       {0x0000a0e4, 0x031e031f},
-       {0x0000a0e8, 0x0402031d},
-       {0x0000a0ec, 0x04000401},
-       {0x0000a0f0, 0x041e041f},
-       {0x0000a0f4, 0x0502041d},
-       {0x0000a0f8, 0x05000501},
-       {0x0000a0fc, 0x051e051f},
-       {0x0000a100, 0x06010602},
-       {0x0000a104, 0x061f0600},
-       {0x0000a108, 0x061d061e},
-       {0x0000a10c, 0x07020703},
-       {0x0000a110, 0x07000701},
+       {0x0000a0c0, 0x22072208},
+       {0x0000a0c4, 0x22052206},
+       {0x0000a0c8, 0x22032204},
+       {0x0000a0cc, 0x22012202},
+       {0x0000a0d0, 0x221f2200},
+       {0x0000a0d4, 0x221d221e},
+       {0x0000a0d8, 0x33023303},
+       {0x0000a0dc, 0x33003301},
+       {0x0000a0e0, 0x331e331f},
+       {0x0000a0e4, 0x4402331d},
+       {0x0000a0e8, 0x44004401},
+       {0x0000a0ec, 0x441e441f},
+       {0x0000a0f0, 0x55025503},
+       {0x0000a0f4, 0x55005501},
+       {0x0000a0f8, 0x551e551f},
+       {0x0000a0fc, 0x6602551d},
+       {0x0000a100, 0x66006601},
+       {0x0000a104, 0x661e661f},
+       {0x0000a108, 0x7703661d},
+       {0x0000a10c, 0x77017702},
+       {0x0000a110, 0x00007700},
        {0x0000a114, 0x00000000},
        {0x0000a118, 0x00000000},
        {0x0000a11c, 0x00000000},
@@ -1054,26 +1056,26 @@ static const u32 ar9331_common_rx_gain_1p1[][2] = {
        {0x0000a138, 0x00000000},
        {0x0000a13c, 0x00000000},
        {0x0000a140, 0x001f0000},
-       {0x0000a144, 0x01000101},
-       {0x0000a148, 0x011e011f},
-       {0x0000a14c, 0x011c011d},
-       {0x0000a150, 0x02030204},
-       {0x0000a154, 0x02010202},
-       {0x0000a158, 0x021f0200},
-       {0x0000a15c, 0x0302021e},
-       {0x0000a160, 0x03000301},
-       {0x0000a164, 0x031e031f},
-       {0x0000a168, 0x0402031d},
-       {0x0000a16c, 0x04000401},
-       {0x0000a170, 0x041e041f},
-       {0x0000a174, 0x0502041d},
-       {0x0000a178, 0x05000501},
-       {0x0000a17c, 0x051e051f},
-       {0x0000a180, 0x06010602},
-       {0x0000a184, 0x061f0600},
-       {0x0000a188, 0x061d061e},
-       {0x0000a18c, 0x07020703},
-       {0x0000a190, 0x07000701},
+       {0x0000a144, 0x111f1100},
+       {0x0000a148, 0x111d111e},
+       {0x0000a14c, 0x111b111c},
+       {0x0000a150, 0x22032204},
+       {0x0000a154, 0x22012202},
+       {0x0000a158, 0x221f2200},
+       {0x0000a15c, 0x221d221e},
+       {0x0000a160, 0x33013302},
+       {0x0000a164, 0x331f3300},
+       {0x0000a168, 0x4402331e},
+       {0x0000a16c, 0x44004401},
+       {0x0000a170, 0x441e441f},
+       {0x0000a174, 0x55015502},
+       {0x0000a178, 0x551f5500},
+       {0x0000a17c, 0x6602551e},
+       {0x0000a180, 0x66006601},
+       {0x0000a184, 0x661e661f},
+       {0x0000a188, 0x7703661d},
+       {0x0000a18c, 0x77017702},
+       {0x0000a190, 0x00007700},
        {0x0000a194, 0x00000000},
        {0x0000a198, 0x00000000},
        {0x0000a19c, 0x00000000},
@@ -1100,14 +1102,14 @@ static const u32 ar9331_common_rx_gain_1p1[][2] = {
        {0x0000a1f0, 0x00000396},
        {0x0000a1f4, 0x00000396},
        {0x0000a1f8, 0x00000396},
-       {0x0000a1fc, 0x00000196},
+       {0x0000a1fc, 0x00000296},
 };
 
 static const u32 ar9331_common_tx_gain_offset1_1[][1] = {
-       {0},
-       {3},
-       {0},
-       {0},
+       {0x00000000},
+       {0x00000003},
+       {0x00000000},
+       {0x00000000},
 };
 
 static const u32 ar9331_1p1_chansel_xtal_25M[] = {
index abe05ec85d501dbeaea089a19dd444fd079b0a4f..7db1890448f20fbfe85ccccea1966633de0d84ad 100644 (file)
@@ -1468,6 +1468,9 @@ static bool ath9k_hw_chip_reset(struct ath_hw *ah,
                return false;
 
        ah->chip_fullsleep = false;
+
+       if (AR_SREV_9330(ah))
+               ar9003_hw_internal_regulator_apply(ah);
        ath9k_hw_init_pll(ah, chan);
        ath9k_hw_set_rfmode(ah, chan);
 
index dfa78e8b6470c02074025f5a94ff8be34058ad8e..4de4473776acd808f52eeed3994af2858897796d 100644 (file)
@@ -239,7 +239,7 @@ static bool ath_prepare_reset(struct ath_softc *sc, bool retry_tx, bool flush)
 {
        struct ath_hw *ah = sc->sc_ah;
        struct ath_common *common = ath9k_hw_common(ah);
-       bool ret;
+       bool ret = true;
 
        ieee80211_stop_queues(sc->hw);
 
@@ -250,11 +250,12 @@ static bool ath_prepare_reset(struct ath_softc *sc, bool retry_tx, bool flush)
        ath9k_debug_samp_bb_mac(sc);
        ath9k_hw_disable_interrupts(ah);
 
-       ret = ath_drain_all_txq(sc, retry_tx);
-
        if (!ath_stoprecv(sc))
                ret = false;
 
+       if (!ath_drain_all_txq(sc, retry_tx))
+               ret = false;
+
        if (!flush) {
                if (ah->caps.hw_caps & ATH9K_HW_CAP_EDMA)
                        ath_rx_tasklet(sc, 1, true);
index 23eaa1b26ebe5ca9a1a242ea4de5e9fa6508b02c..d59dd01d6cdeda200280f0f856fe9ecf26e5fbcc 100644 (file)
@@ -64,7 +64,8 @@ static void ath_tx_update_baw(struct ath_softc *sc, struct ath_atx_tid *tid,
 static struct ath_buf *ath_tx_setup_buffer(struct ath_softc *sc,
                                           struct ath_txq *txq,
                                           struct ath_atx_tid *tid,
-                                          struct sk_buff *skb);
+                                          struct sk_buff *skb,
+                                          bool dequeue);
 
 enum {
        MCS_HT20,
@@ -811,7 +812,7 @@ static enum ATH_AGGR_STATUS ath_tx_form_aggr(struct ath_softc *sc,
                fi = get_frame_info(skb);
                bf = fi->bf;
                if (!fi->bf)
-                       bf = ath_tx_setup_buffer(sc, txq, tid, skb);
+                       bf = ath_tx_setup_buffer(sc, txq, tid, skb, true);
 
                if (!bf)
                        continue;
@@ -1726,7 +1727,7 @@ static void ath_tx_send_ampdu(struct ath_softc *sc, struct ath_atx_tid *tid,
                return;
        }
 
-       bf = ath_tx_setup_buffer(sc, txctl->txq, tid, skb);
+       bf = ath_tx_setup_buffer(sc, txctl->txq, tid, skb, false);
        if (!bf)
                return;
 
@@ -1753,7 +1754,7 @@ static void ath_tx_send_normal(struct ath_softc *sc, struct ath_txq *txq,
 
        bf = fi->bf;
        if (!bf)
-               bf = ath_tx_setup_buffer(sc, txq, tid, skb);
+               bf = ath_tx_setup_buffer(sc, txq, tid, skb, false);
 
        if (!bf)
                return;
@@ -1814,7 +1815,8 @@ u8 ath_txchainmask_reduction(struct ath_softc *sc, u8 chainmask, u32 rate)
 static struct ath_buf *ath_tx_setup_buffer(struct ath_softc *sc,
                                           struct ath_txq *txq,
                                           struct ath_atx_tid *tid,
-                                          struct sk_buff *skb)
+                                          struct sk_buff *skb,
+                                          bool dequeue)
 {
        struct ath_common *common = ath9k_hw_common(sc->sc_ah);
        struct ath_frame_info *fi = get_frame_info(skb);
@@ -1863,6 +1865,8 @@ static struct ath_buf *ath_tx_setup_buffer(struct ath_softc *sc,
        return bf;
 
 error:
+       if (dequeue)
+               __skb_unlink(skb, &tid->buf_q);
        dev_kfree_skb_any(skb);
        return NULL;
 }
@@ -1893,7 +1897,7 @@ static void ath_tx_start_dma(struct ath_softc *sc, struct sk_buff *skb,
                 */
                ath_tx_send_ampdu(sc, tid, skb, txctl);
        } else {
-               bf = ath_tx_setup_buffer(sc, txctl->txq, tid, skb);
+               bf = ath_tx_setup_buffer(sc, txctl->txq, tid, skb, false);
                if (!bf)
                        return;
 
index c5a34ffe64599e9d5852aeec83f93ab00a215901..a299d42da8e74a358939b8fa5da8a32a01fd312b 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/uaccess.h>
 #include <linux/firmware.h>
 #include <linux/usb.h>
+#include <linux/vmalloc.h>
 #include <net/cfg80211.h>
 
 #include <defs.h>
@@ -1239,7 +1240,7 @@ static int brcmf_usb_get_fw(struct brcmf_usbdev_info *devinfo)
                return -EINVAL;
        }
 
-       devinfo->image = kmalloc(fw->size, GFP_ATOMIC); /* plus nvram */
+       devinfo->image = vmalloc(fw->size); /* plus nvram */
        if (!devinfo->image)
                return -ENOMEM;
 
@@ -1603,7 +1604,7 @@ static struct usb_driver brcmf_usbdrvr = {
 void brcmf_usb_exit(void)
 {
        usb_deregister(&brcmf_usbdrvr);
-       kfree(g_image.data);
+       vfree(g_image.data);
        g_image.data = NULL;
        g_image.len = 0;
 }
index db6c6e528022635638f8aa8594c2a6872da52f9b..2463c06264387230759f14801239610a4fe4eb58 100644 (file)
@@ -137,11 +137,3 @@ config IWLWIFI_EXPERIMENTAL_MFP
          even if the microcode doesn't advertise it.
 
          Say Y only if you want to experiment with MFP.
-
-config IWLWIFI_UCODE16
-       bool "support uCode 16.0"
-       depends on IWLWIFI
-       help
-         This option enables support for uCode version 16.0.
-
-         Say Y if you want to use 16.0 microcode.
index 406f297a9a56dd27eecde87f8d027a879d337137..d615eacbf050be320d803c74fec0ace223abae73 100644 (file)
@@ -18,7 +18,6 @@ iwlwifi-objs          += iwl-notif-wait.o
 iwlwifi-objs           += iwl-trans-pcie.o iwl-trans-pcie-rx.o iwl-trans-pcie-tx.o
 
 
-iwlwifi-$(CONFIG_IWLWIFI_UCODE16) += iwl-phy-db.o
 iwlwifi-$(CONFIG_IWLWIFI_DEBUGFS) += iwl-debugfs.o
 iwlwifi-$(CONFIG_IWLWIFI_DEVICE_TRACING) += iwl-devtrace.o
 iwlwifi-$(CONFIG_IWLWIFI_DEVICE_TESTMODE) += iwl-testmode.o
index 7f793417c78740b5fd908aa976ea208d5c2c6c3f..8133105ac6450ae19ae2743019aae2a3182b1b46 100644 (file)
@@ -79,7 +79,7 @@ static const struct iwl_base_params iwl2000_base_params = {
        .chain_noise_scale = 1000,
        .wd_timeout = IWL_DEF_WD_TIMEOUT,
        .max_event_log_size = 512,
-       .shadow_reg_enable = true,
+       .shadow_reg_enable = false, /* TODO: fix bugs using this feature */
        .hd_v2 = true,
 };
 
@@ -97,7 +97,7 @@ static const struct iwl_base_params iwl2030_base_params = {
        .chain_noise_scale = 1000,
        .wd_timeout = IWL_LONG_WD_TIMEOUT,
        .max_event_log_size = 512,
-       .shadow_reg_enable = true,
+       .shadow_reg_enable = false, /* TODO: fix bugs using this feature */
        .hd_v2 = true,
 };
 
index 381b02cf339c46e0353b07bf04f201b70f49a4bf..19f7ee84ae89e2b76ba493016cb508745eaae173 100644 (file)
@@ -86,7 +86,7 @@ static const struct iwl_base_params iwl6000_base_params = {
        .chain_noise_scale = 1000,
        .wd_timeout = IWL_DEF_WD_TIMEOUT,
        .max_event_log_size = 512,
-       .shadow_reg_enable = true,
+       .shadow_reg_enable = false, /* TODO: fix bugs using this feature */
 };
 
 static const struct iwl_base_params iwl6050_base_params = {
@@ -102,7 +102,7 @@ static const struct iwl_base_params iwl6050_base_params = {
        .chain_noise_scale = 1500,
        .wd_timeout = IWL_DEF_WD_TIMEOUT,
        .max_event_log_size = 1024,
-       .shadow_reg_enable = true,
+       .shadow_reg_enable = false, /* TODO: fix bugs using this feature */
 };
 
 static const struct iwl_base_params iwl6000_g2_base_params = {
@@ -118,7 +118,7 @@ static const struct iwl_base_params iwl6000_g2_base_params = {
        .chain_noise_scale = 1000,
        .wd_timeout = IWL_LONG_WD_TIMEOUT,
        .max_event_log_size = 512,
-       .shadow_reg_enable = true,
+       .shadow_reg_enable = false, /* TODO: fix bugs using this feature */
 };
 
 static const struct iwl_ht_params iwl6000_ht_params = {
index 51e1a69ffdda629ff84c7008513202d85ce2e94d..8cebd7c363fc301477cd71e5ed8b96a184d58c16 100644 (file)
@@ -884,6 +884,7 @@ static void rs_bt_update_lq(struct iwl_priv *priv, struct iwl_rxon_context *ctx,
        if ((priv->bt_traffic_load != priv->last_bt_traffic_load) ||
            (priv->bt_full_concurrent != full_concurrent)) {
                priv->bt_full_concurrent = full_concurrent;
+               priv->last_bt_traffic_load = priv->bt_traffic_load;
 
                /* Update uCode's rate table. */
                tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
index b31584e87bc7f4d03f2d3d2ba727879979b19156..aea07aab3c9e82c44f6b417d20794f4523e6ae70 100644 (file)
@@ -772,7 +772,7 @@ void iwl_restore_stations(struct iwl_priv *priv, struct iwl_rxon_context *ctx)
                                                ~IWL_STA_DRIVER_ACTIVE;
                                priv->stations[i].used &=
                                                ~IWL_STA_UCODE_INPROGRESS;
-                               spin_unlock_bh(&priv->sta_lock);
+                               continue;
                        }
                        /*
                         * Rate scaling has already been initialized, send
index 3c72bad0ae56fc3d1e443f2b08a70e337910e4f0..d742900969eabc913feb5e3643b3cfdc0ddf3840 100644 (file)
@@ -657,17 +657,17 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
        return -EINVAL;
 }
 
-static int alloc_pci_desc(struct iwl_drv *drv,
-                         struct iwl_firmware_pieces *pieces,
-                         enum iwl_ucode_type type)
+static int iwl_alloc_ucode(struct iwl_drv *drv,
+                          struct iwl_firmware_pieces *pieces,
+                          enum iwl_ucode_type type)
 {
        int i;
        for (i = 0;
             i < IWL_UCODE_SECTION_MAX && get_sec_size(pieces, type, i);
             i++)
                if (iwl_alloc_fw_desc(drv, &(drv->fw.img[type].sec[i]),
-                                               get_sec(pieces, type, i)))
-                       return -1;
+                                     get_sec(pieces, type, i)))
+                       return -ENOMEM;
        return 0;
 }
 
@@ -825,8 +825,8 @@ static void iwl_ucode_callback(const struct firmware *ucode_raw, void *context)
         * 1) unmodified from disk
         * 2) backup cache for save/restore during power-downs */
        for (i = 0; i < IWL_UCODE_TYPE_MAX; i++)
-               if (alloc_pci_desc(drv, &pieces, i))
-                       goto err_pci_alloc;
+               if (iwl_alloc_ucode(drv, &pieces, i))
+                       goto out_free_fw;
 
        /* Now that we can no longer fail, copy information */
 
@@ -866,7 +866,7 @@ static void iwl_ucode_callback(const struct firmware *ucode_raw, void *context)
        drv->op_mode = iwl_dvm_ops.start(drv->trans, drv->cfg, &drv->fw);
 
        if (!drv->op_mode)
-               goto out_unbind;
+               goto out_free_fw;
 
        return;
 
@@ -877,7 +877,7 @@ static void iwl_ucode_callback(const struct firmware *ucode_raw, void *context)
                goto out_unbind;
        return;
 
err_pci_alloc:
out_free_fw:
        IWL_ERR(drv, "failed to allocate pci memory\n");
        iwl_dealloc_ucode(drv);
        release_firmware(ucode_raw);
diff --git a/drivers/net/wireless/iwlwifi/iwl-phy-db.c b/drivers/net/wireless/iwlwifi/iwl-phy-db.c
deleted file mode 100644 (file)
index f166955..0000000
+++ /dev/null
@@ -1,288 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license.  When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * 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,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
- *
- * Contact Information:
- *  Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *  * Neither the name Intel Corporation nor the names of its
- *    contributors may be used to endorse or promote products derived
- *    from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *****************************************************************************/
-
-#include <linux/slab.h>
-#include <linux/string.h>
-
-#include "iwl-debug.h"
-#include "iwl-dev.h"
-
-#include "iwl-phy-db.h"
-
-#define CHANNEL_NUM_SIZE       4       /* num of channels in calib_ch size */
-
-struct iwl_phy_db *iwl_phy_db_init(struct device *dev)
-{
-       struct iwl_phy_db *phy_db = kzalloc(sizeof(struct iwl_phy_db),
-                                           GFP_KERNEL);
-
-       if (!phy_db)
-               return phy_db;
-
-       phy_db->dev = dev;
-
-       /* TODO: add default values of the phy db. */
-       return phy_db;
-}
-
-/*
- * get phy db section: returns a pointer to a phy db section specified by
- * type and channel group id.
- */
-static struct iwl_phy_db_entry *
-iwl_phy_db_get_section(struct iwl_phy_db *phy_db,
-                      enum iwl_phy_db_section_type type,
-                      u16 chg_id)
-{
-       if (!phy_db || type < 0 || type >= IWL_PHY_DB_MAX)
-               return NULL;
-
-       switch (type) {
-       case IWL_PHY_DB_CFG:
-               return &phy_db->cfg;
-       case IWL_PHY_DB_CALIB_NCH:
-               return &phy_db->calib_nch;
-       case IWL_PHY_DB_CALIB_CH:
-               return &phy_db->calib_ch;
-       case IWL_PHY_DB_CALIB_CHG_PAPD:
-               if (chg_id < 0 || chg_id >= IWL_NUM_PAPD_CH_GROUPS)
-                       return NULL;
-               return &phy_db->calib_ch_group_papd[chg_id];
-       case IWL_PHY_DB_CALIB_CHG_TXP:
-               if (chg_id < 0 || chg_id >= IWL_NUM_TXP_CH_GROUPS)
-                       return NULL;
-               return &phy_db->calib_ch_group_txp[chg_id];
-       default:
-               return NULL;
-       }
-       return NULL;
-}
-
-static void iwl_phy_db_free_section(struct iwl_phy_db *phy_db,
-                                   enum iwl_phy_db_section_type type,
-                                   u16 chg_id)
-{
-       struct iwl_phy_db_entry *entry =
-                               iwl_phy_db_get_section(phy_db, type, chg_id);
-       if (!entry)
-               return;
-
-       kfree(entry->data);
-       entry->data = NULL;
-       entry->size = 0;
-}
-
-void iwl_phy_db_free(struct iwl_phy_db *phy_db)
-{
-       int i;
-
-       if (!phy_db)
-               return;
-
-       iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CFG, 0);
-       iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CALIB_NCH, 0);
-       iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CALIB_CH, 0);
-       for (i = 0; i < IWL_NUM_PAPD_CH_GROUPS; i++)
-               iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CALIB_CHG_PAPD, i);
-       for (i = 0; i < IWL_NUM_TXP_CH_GROUPS; i++)
-               iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CALIB_CHG_TXP, i);
-
-       kfree(phy_db);
-}
-
-int iwl_phy_db_set_section(struct iwl_phy_db *phy_db,
-                          enum iwl_phy_db_section_type type, u8 *data,
-                          u16 size, gfp_t alloc_ctx)
-{
-       struct iwl_phy_db_entry *entry;
-       u16 chg_id = 0;
-
-       if (!phy_db)
-               return -EINVAL;
-
-       if (type == IWL_PHY_DB_CALIB_CHG_PAPD ||
-           type == IWL_PHY_DB_CALIB_CHG_TXP)
-               chg_id = le16_to_cpup((__le16 *)data);
-
-       entry = iwl_phy_db_get_section(phy_db, type, chg_id);
-       if (!entry)
-               return -EINVAL;
-
-       kfree(entry->data);
-       entry->data = kmemdup(data, size, alloc_ctx);
-       if (!entry->data) {
-               entry->size = 0;
-               return -ENOMEM;
-       }
-
-       entry->size = size;
-
-       if (type == IWL_PHY_DB_CALIB_CH) {
-               phy_db->channel_num = le32_to_cpup((__le32 *)data);
-               phy_db->channel_size =
-                     (size - CHANNEL_NUM_SIZE) / phy_db->channel_num;
-       }
-
-       return 0;
-}
-
-static int is_valid_channel(u16 ch_id)
-{
-       if (ch_id <= 14 ||
-           (36 <= ch_id && ch_id <= 64 && ch_id % 4 == 0) ||
-           (100 <= ch_id && ch_id <= 140 && ch_id % 4 == 0) ||
-           (145 <= ch_id && ch_id <= 165 && ch_id % 4 == 1))
-               return 1;
-       return 0;
-}
-
-static u8 ch_id_to_ch_index(u16 ch_id)
-{
-       if (WARN_ON(!is_valid_channel(ch_id)))
-               return 0xff;
-
-       if (ch_id <= 14)
-               return ch_id - 1;
-       if (ch_id <= 64)
-               return (ch_id + 20) / 4;
-       if (ch_id <= 140)
-               return (ch_id - 12) / 4;
-       return (ch_id - 13) / 4;
-}
-
-
-static u16 channel_id_to_papd(u16 ch_id)
-{
-       if (WARN_ON(!is_valid_channel(ch_id)))
-               return 0xff;
-
-       if (1 <= ch_id && ch_id <= 14)
-               return 0;
-       if (36 <= ch_id && ch_id <= 64)
-               return 1;
-       if (100 <= ch_id && ch_id <= 140)
-               return 2;
-       return 3;
-}
-
-static u16 channel_id_to_txp(struct iwl_phy_db *phy_db, u16 ch_id)
-{
-       struct iwl_phy_db_chg_txp *txp_chg;
-       int i;
-       u8 ch_index = ch_id_to_ch_index(ch_id);
-       if (ch_index == 0xff)
-               return 0xff;
-
-       for (i = 0; i < IWL_NUM_TXP_CH_GROUPS; i++) {
-               txp_chg = (void *)phy_db->calib_ch_group_txp[i].data;
-               if (!txp_chg)
-                       return 0xff;
-               /*
-                * Looking for the first channel group that its max channel is
-                * higher then wanted channel.
-                */
-               if (le16_to_cpu(txp_chg->max_channel_idx) >= ch_index)
-                       return i;
-       }
-       return 0xff;
-}
-
-int iwl_phy_db_get_section_data(struct iwl_phy_db *phy_db,
-                               enum iwl_phy_db_section_type type, u8 **data,
-                               u16 *size, u16 ch_id)
-{
-       struct iwl_phy_db_entry *entry;
-       u32 channel_num;
-       u32 channel_size;
-       u16 ch_group_id = 0;
-       u16 index;
-
-       if (!phy_db)
-               return -EINVAL;
-
-       /* find wanted channel group */
-       if (type == IWL_PHY_DB_CALIB_CHG_PAPD)
-               ch_group_id = channel_id_to_papd(ch_id);
-       else if (type == IWL_PHY_DB_CALIB_CHG_TXP)
-               ch_group_id = channel_id_to_txp(phy_db, ch_id);
-
-       entry = iwl_phy_db_get_section(phy_db, type, ch_group_id);
-       if (!entry)
-               return -EINVAL;
-
-       if (type == IWL_PHY_DB_CALIB_CH) {
-               index = ch_id_to_ch_index(ch_id);
-               channel_num = phy_db->channel_num;
-               channel_size = phy_db->channel_size;
-               if (index >= channel_num) {
-                       IWL_ERR(phy_db, "Wrong channel number %d", ch_id);
-                       return -EINVAL;
-               }
-               *data = entry->data + CHANNEL_NUM_SIZE + index * channel_size;
-               *size = channel_size;
-       } else {
-               *data = entry->data;
-               *size = entry->size;
-       }
-       return 0;
-}
diff --git a/drivers/net/wireless/iwlwifi/iwl-phy-db.h b/drivers/net/wireless/iwlwifi/iwl-phy-db.h
deleted file mode 100644 (file)
index c34c6a9..0000000
+++ /dev/null
@@ -1,129 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license.  When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * 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,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
- *
- * Contact Information:
- *  Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *  * Neither the name Intel Corporation nor the names of its
- *    contributors may be used to endorse or promote products derived
- *    from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *****************************************************************************/
-
-#ifndef __IWL_PHYDB_H__
-#define __IWL_PHYDB_H__
-
-#include <linux/types.h>
-
-#define IWL_NUM_PAPD_CH_GROUPS 4
-#define IWL_NUM_TXP_CH_GROUPS  8
-
-struct iwl_phy_db_entry {
-       u16     size;
-       u8      *data;
-};
-
-struct iwl_shared;
-
-/**
- * struct iwl_phy_db - stores phy configuration and calibration data.
- *
- * @cfg: phy configuration.
- * @calib_nch: non channel specific calibration data.
- * @calib_ch: channel specific calibration data.
- * @calib_ch_group_papd: calibration data related to papd channel group.
- * @calib_ch_group_txp: calibration data related to tx power chanel group.
- */
-struct iwl_phy_db {
-       struct iwl_phy_db_entry cfg;
-       struct iwl_phy_db_entry calib_nch;
-       struct iwl_phy_db_entry calib_ch;
-       struct iwl_phy_db_entry calib_ch_group_papd[IWL_NUM_PAPD_CH_GROUPS];
-       struct iwl_phy_db_entry calib_ch_group_txp[IWL_NUM_TXP_CH_GROUPS];
-
-       u32 channel_num;
-       u32 channel_size;
-
-       /* for an access to the logger */
-       struct device *dev;
-};
-
-enum iwl_phy_db_section_type {
-       IWL_PHY_DB_CFG = 1,
-       IWL_PHY_DB_CALIB_NCH,
-       IWL_PHY_DB_CALIB_CH,
-       IWL_PHY_DB_CALIB_CHG_PAPD,
-       IWL_PHY_DB_CALIB_CHG_TXP,
-       IWL_PHY_DB_MAX
-};
-
-/* for parsing of tx power channel group data that comes from the firmware*/
-struct iwl_phy_db_chg_txp {
-       __le32 space;
-       __le16 max_channel_idx;
-} __packed;
-
-struct iwl_phy_db *iwl_phy_db_init(struct device *dev);
-
-void iwl_phy_db_free(struct iwl_phy_db *phy_db);
-
-int iwl_phy_db_set_section(struct iwl_phy_db *phy_db,
-                          enum iwl_phy_db_section_type type, u8 *data,
-                          u16 size, gfp_t alloc_ctx);
-
-int iwl_phy_db_get_section_data(struct iwl_phy_db *phy_db,
-                               enum iwl_phy_db_section_type type, u8 **data,
-                               u16 *size, u16 ch_id);
-
-#endif /* __IWL_PHYDB_H__ */
index 6213c05a4b529c6ba0f4263b83d9050afcfdd614..e959207c630a9352f143536c1a51a5de995ee96b 100644 (file)
@@ -347,7 +347,7 @@ void iwl_trans_tx_queue_set_status(struct iwl_trans *trans,
 void iwl_trans_pcie_tx_agg_setup(struct iwl_trans *trans, int queue, int fifo,
                                 int sta_id, int tid, int frame_limit, u16 ssn);
 void iwlagn_txq_free_tfd(struct iwl_trans *trans, struct iwl_tx_queue *txq,
-       int index, enum dma_data_direction dma_dir);
+                        enum dma_data_direction dma_dir);
 int iwl_tx_queue_reclaim(struct iwl_trans *trans, int txq_id, int index,
                         struct sk_buff_head *skbs);
 int iwl_queue_space(const struct iwl_queue *q);
index 21a8a672fbb258caae8735c61fcfbc0f09e9f426..a8750238ee09b78a99623693332e8c8c40c83a85 100644 (file)
@@ -204,33 +204,39 @@ static void iwlagn_unmap_tfd(struct iwl_trans *trans, struct iwl_cmd_meta *meta,
        for (i = 1; i < num_tbs; i++)
                dma_unmap_single(trans->dev, iwl_tfd_tb_get_addr(tfd, i),
                                iwl_tfd_tb_get_len(tfd, i), dma_dir);
+
+       tfd->num_tbs = 0;
 }
 
 /**
  * iwlagn_txq_free_tfd - Free all chunks referenced by TFD [txq->q.read_ptr]
  * @trans - transport private data
  * @txq - tx queue
- * @index - the index of the TFD to be freed
- *@dma_dir - the direction of the DMA mapping
+ * @dma_dir - the direction of the DMA mapping
  *
  * Does NOT advance any TFD circular buffer read/write indexes
  * Does NOT free the TFD itself (which is within circular buffer)
  */
 void iwlagn_txq_free_tfd(struct iwl_trans *trans, struct iwl_tx_queue *txq,
-       int index, enum dma_data_direction dma_dir)
+                        enum dma_data_direction dma_dir)
 {
        struct iwl_tfd *tfd_tmp = txq->tfds;
 
+       /* rd_ptr is bounded by n_bd and idx is bounded by n_window */
+       int rd_ptr = txq->q.read_ptr;
+       int idx = get_cmd_index(&txq->q, rd_ptr);
+
        lockdep_assert_held(&txq->lock);
 
-       iwlagn_unmap_tfd(trans, &txq->entries[index].meta,
-                        &tfd_tmp[index], dma_dir);
+       /* We have only q->n_window txq->entries, but we use q->n_bd tfds */
+       iwlagn_unmap_tfd(trans, &txq->entries[idx].meta,
+                        &tfd_tmp[rd_ptr], dma_dir);
 
        /* free SKB */
        if (txq->entries) {
                struct sk_buff *skb;
 
-               skb = txq->entries[index].skb;
+               skb = txq->entries[idx].skb;
 
                /* Can be called from irqs-disabled context
                 * If skb is not NULL, it means that the whole queue is being
@@ -238,7 +244,7 @@ void iwlagn_txq_free_tfd(struct iwl_trans *trans, struct iwl_tx_queue *txq,
                 */
                if (skb) {
                        iwl_op_mode_free_skb(trans->op_mode, skb);
-                       txq->entries[index].skb = NULL;
+                       txq->entries[idx].skb = NULL;
                }
        }
 }
@@ -973,7 +979,7 @@ int iwl_tx_queue_reclaim(struct iwl_trans *trans, int txq_id, int index,
 
                iwlagn_txq_inval_byte_cnt_tbl(trans, txq);
 
-               iwlagn_txq_free_tfd(trans, txq, txq->q.read_ptr, DMA_TO_DEVICE);
+               iwlagn_txq_free_tfd(trans, txq, DMA_TO_DEVICE);
                freed++;
        }
 
index 2e57161854b901187f40a50628db2fdf6b4c8ac0..ec6fb395b84d0aca4e7d7bfcf2e1729c3959dc5a 100644 (file)
@@ -435,9 +435,7 @@ static void iwl_tx_queue_unmap(struct iwl_trans *trans, int txq_id)
 
        spin_lock_bh(&txq->lock);
        while (q->write_ptr != q->read_ptr) {
-               /* The read_ptr needs to bound by q->n_window */
-               iwlagn_txq_free_tfd(trans, txq, get_cmd_index(q, q->read_ptr),
-                                   dma_dir);
+               iwlagn_txq_free_tfd(trans, txq, dma_dir);
                q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd);
        }
        spin_unlock_bh(&txq->lock);
index 1b851f650e074eb795ffb708dd25b3c94a2cb9e1..e2750a12c6f160a922609f775fcf65c4c81d662e 100644 (file)
@@ -260,6 +260,7 @@ static int wl1251_sdio_probe(struct sdio_func *func,
        }
 
        if (wl->irq) {
+               irq_set_status_flags(wl->irq, IRQ_NOAUTOEN);
                ret = request_irq(wl->irq, wl1251_line_irq, 0, "wl1251", wl);
                if (ret < 0) {
                        wl1251_error("request_irq() failed: %d", ret);
@@ -267,7 +268,6 @@ static int wl1251_sdio_probe(struct sdio_func *func,
                }
 
                irq_set_irq_type(wl->irq, IRQ_TYPE_EDGE_RISING);
-               disable_irq(wl->irq);
 
                wl1251_sdio_ops.enable_irq = wl1251_enable_line_irq;
                wl1251_sdio_ops.disable_irq = wl1251_disable_line_irq;
index 6248c354fc5c659fd840a2e7cff6186536207596..87f6305bda2cc5ced7f6377da9a3a3f9a16b72a4 100644 (file)
@@ -281,6 +281,7 @@ static int __devinit wl1251_spi_probe(struct spi_device *spi)
 
        wl->use_eeprom = pdata->use_eeprom;
 
+       irq_set_status_flags(wl->irq, IRQ_NOAUTOEN);
        ret = request_irq(wl->irq, wl1251_irq, 0, DRIVER_NAME, wl);
        if (ret < 0) {
                wl1251_error("request_irq() failed: %d", ret);
@@ -289,8 +290,6 @@ static int __devinit wl1251_spi_probe(struct spi_device *spi)
 
        irq_set_irq_type(wl->irq, IRQ_TYPE_EDGE_RISING);
 
-       disable_irq(wl->irq);
-
        ret = wl1251_init_ieee80211(wl);
        if (ret)
                goto out_irq;
index 509aa881d790fa4ae7be894c7dc8fa362e161114..f3d6fa5082696c145b30b453fd027ce4cdd88a11 100644 (file)
@@ -1715,6 +1715,7 @@ out:
 
 }
 
+#ifdef CONFIG_PM
 /* Set the global behaviour of RX filters - On/Off + default action */
 int wl1271_acx_default_rx_filter_enable(struct wl1271 *wl, bool enable,
                                        enum rx_filter_action action)
@@ -1794,3 +1795,4 @@ out:
        kfree(acx);
        return ret;
 }
+#endif /* CONFIG_PM */
index 8106b2ebfe607dd921a87565a16f97f4339de355..e6a74869a5ff539df5589e90e73bb12d98f34747 100644 (file)
@@ -1330,9 +1330,11 @@ int wl1271_acx_set_inconnection_sta(struct wl1271 *wl, u8 *addr);
 int wl1271_acx_fm_coex(struct wl1271 *wl);
 int wl12xx_acx_set_rate_mgmt_params(struct wl1271 *wl);
 int wl12xx_acx_config_hangover(struct wl1271 *wl);
+
+#ifdef CONFIG_PM
 int wl1271_acx_default_rx_filter_enable(struct wl1271 *wl, bool enable,
                                        enum rx_filter_action action);
 int wl1271_acx_set_rx_filter(struct wl1271 *wl, u8 index, bool enable,
                             struct wl12xx_rx_filter *filter);
-
+#endif /* CONFIG_PM */
 #endif /* __WL1271_ACX_H__ */
index 1f1d9488dfb6b26a2482d88e66b957e4406b474b..d6a3c6b07827738bbc3e0f3ea0ea977e1a6e9dad 100644 (file)
@@ -279,6 +279,7 @@ void wl12xx_rx(struct wl1271 *wl, struct wl_fw_status *status)
        wl12xx_rearm_rx_streaming(wl, active_hlids);
 }
 
+#ifdef CONFIG_PM
 int wl1271_rx_filter_enable(struct wl1271 *wl,
                            int index, bool enable,
                            struct wl12xx_rx_filter *filter)
@@ -314,3 +315,4 @@ void wl1271_rx_filter_clear_all(struct wl1271 *wl)
                wl1271_rx_filter_enable(wl, i, 0, NULL);
        }
 }
+#endif /* CONFIG_PM */
index 2596401308a86e24210efd65d531d91d6be60746..f4a6fcaeffb1db381ef9fd007a72578d984a3b4c 100644 (file)
@@ -325,8 +325,7 @@ unsigned int xen_netbk_count_skb_slots(struct xenvif *vif, struct sk_buff *skb)
        unsigned int count;
        int i, copy_off;
 
-       count = DIV_ROUND_UP(
-                       offset_in_page(skb->data)+skb_headlen(skb), PAGE_SIZE);
+       count = DIV_ROUND_UP(skb_headlen(skb), PAGE_SIZE);
 
        copy_off = skb_headlen(skb) % PAGE_SIZE;
 
index 46f4a9f9f5e476ce90729e4386c74442abcfa05f..281f18c2fb8282670c4dd6dab4593ab4ef3cc4b8 100644 (file)
@@ -232,7 +232,7 @@ static int pn544_hci_i2c_write(struct i2c_client *client, u8 *buf, int len)
 
 static int check_crc(u8 *buf, int buflen)
 {
-       u8 len;
+       int len;
        u16 crc;
 
        len = buf[0] + 1;
index 93125163dea21fc16bcf2dd0243c7426cd351708..67705381321154546eb8b80a3280ae417a662c01 100644 (file)
@@ -15,7 +15,7 @@
  * PCI tree until an device-node is found, at which point it will finish
  * resolving using the OF tree walking.
  */
-int of_irq_map_pci(struct pci_dev *pdev, struct of_irq *out_irq)
+int of_irq_map_pci(const struct pci_dev *pdev, struct of_irq *out_irq)
 {
        struct device_node *dn, *ppnode;
        struct pci_dev *ppdev;
index 8f169002dc7ec6b4581d4f7f049c35ff9ed7e8a2..447e83472c01558705d0685f7fd6af17a6f8deef 100644 (file)
@@ -2370,7 +2370,7 @@ void pci_enable_acs(struct pci_dev *dev)
  * number is always 0 (see the Implementation Note in section 2.2.8.1 of
  * the PCI Express Base Specification, Revision 2.1)
  */
-u8 pci_swizzle_interrupt_pin(struct pci_dev *dev, u8 pin)
+u8 pci_swizzle_interrupt_pin(const struct pci_dev *dev, u8 pin)
 {
        int slot;
 
index ee79ce64d9dfbdb8a99061f59232325b8c57f6d3..57787d87d9a4780df7e6d3a5d757aa7179545445 100644 (file)
@@ -1104,6 +1104,7 @@ static int __devinit toshiba_acpi_add(struct acpi_device *acpi_dev)
 
        mutex_init(&dev->mutex);
 
+       memset(&props, 0, sizeof(props));
        props.type = BACKLIGHT_PLATFORM;
        props.max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1;
        dev->backlight_dev = backlight_device_register("toshiba",
index 99dc29f2f2f2ba84b16430a51548817b094b9c3a..e3a3b4956f08408741fe5856a562b8defe7294a1 100644 (file)
@@ -1,5 +1,5 @@
 menuconfig POWER_SUPPLY
-       tristate "Power supply class support"
+       bool "Power supply class support"
        help
          Say Y here to enable power supply class support. This allows
          power supply (batteries, AC, USB) monitoring by userspace
@@ -77,7 +77,7 @@ config BATTERY_DS2780
          Say Y here to enable support for batteries with ds2780 chip.
 
 config BATTERY_DS2781
-       tristate "2781 battery driver"
+       tristate "DS2781 battery driver"
        depends on HAS_IOMEM
        select W1
        select W1_SLAVE_DS2781
@@ -181,14 +181,15 @@ config BATTERY_MAX17040
          to operate with a single lithium cell
 
 config BATTERY_MAX17042
-       tristate "Maxim MAX17042/8997/8966 Fuel Gauge"
+       tristate "Maxim MAX17042/17047/17050/8997/8966 Fuel Gauge"
        depends on I2C
        help
          MAX17042 is fuel-gauge systems for lithium-ion (Li+) batteries
          in handheld and portable equipment. The MAX17042 is configured
          to operate with a single lithium cell. MAX8997 and MAX8966 are
          multi-function devices that include fuel gauages that are compatible
-         with MAX17042.
+         with MAX17042. This driver also supports max17047/50 chips which are
+         improved version of max17042.
 
 config BATTERY_Z2
        tristate "Z2 battery driver"
@@ -291,6 +292,7 @@ config CHARGER_MAX8998
 config CHARGER_SMB347
        tristate "Summit Microelectronics SMB347 Battery Charger"
        depends on I2C
+       select REGMAP_I2C
        help
          Say Y to include support for Summit Microelectronics SMB347
          Battery Charger.
index d8bb99394ac01c1e6ebb289698dea0f8d3426cd8..bba3ccac72fe731a6807e211af9171a3204ce8c8 100644 (file)
@@ -964,10 +964,15 @@ static int __devinit ab8500_btemp_probe(struct platform_device *pdev)
 {
        int irq, i, ret = 0;
        u8 val;
-       struct abx500_bm_plat_data *plat_data;
+       struct abx500_bm_plat_data *plat_data = pdev->dev.platform_data;
+       struct ab8500_btemp *di;
+
+       if (!plat_data) {
+               dev_err(&pdev->dev, "No platform data\n");
+               return -EINVAL;
+       }
 
-       struct ab8500_btemp *di =
-               kzalloc(sizeof(struct ab8500_btemp), GFP_KERNEL);
+       di = kzalloc(sizeof(*di), GFP_KERNEL);
        if (!di)
                return -ENOMEM;
 
@@ -977,7 +982,6 @@ static int __devinit ab8500_btemp_probe(struct platform_device *pdev)
        di->gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
 
        /* get btemp specific platform data */
-       plat_data = pdev->dev.platform_data;
        di->pdata = plat_data->btemp;
        if (!di->pdata) {
                dev_err(di->dev, "no btemp platform data supplied\n");
index e2b4accbec8815782ad1bf659b90e40196d31b63..d2303d0b7c755669f7ab48b968e062474241997b 100644 (file)
@@ -2534,10 +2534,15 @@ static int __devexit ab8500_charger_remove(struct platform_device *pdev)
 static int __devinit ab8500_charger_probe(struct platform_device *pdev)
 {
        int irq, i, charger_status, ret = 0;
-       struct abx500_bm_plat_data *plat_data;
+       struct abx500_bm_plat_data *plat_data = pdev->dev.platform_data;
+       struct ab8500_charger *di;
 
-       struct ab8500_charger *di =
-               kzalloc(sizeof(struct ab8500_charger), GFP_KERNEL);
+       if (!plat_data) {
+               dev_err(&pdev->dev, "No platform data\n");
+               return -EINVAL;
+       }
+
+       di = kzalloc(sizeof(*di), GFP_KERNEL);
        if (!di)
                return -ENOMEM;
 
@@ -2550,9 +2555,7 @@ static int __devinit ab8500_charger_probe(struct platform_device *pdev)
        spin_lock_init(&di->usb_state.usb_lock);
 
        /* get charger specific platform data */
-       plat_data = pdev->dev.platform_data;
        di->pdata = plat_data->charger;
-
        if (!di->pdata) {
                dev_err(di->dev, "no charger platform data supplied\n");
                ret = -EINVAL;
index c22f2f05657e28d249d619d9a01aaab8a3095bb6..bf022255994c86b3d3486e27e1edcc905c7246f4 100644 (file)
@@ -2446,10 +2446,15 @@ static int __devinit ab8500_fg_probe(struct platform_device *pdev)
 {
        int i, irq;
        int ret = 0;
-       struct abx500_bm_plat_data *plat_data;
+       struct abx500_bm_plat_data *plat_data = pdev->dev.platform_data;
+       struct ab8500_fg *di;
+
+       if (!plat_data) {
+               dev_err(&pdev->dev, "No platform data\n");
+               return -EINVAL;
+       }
 
-       struct ab8500_fg *di =
-               kzalloc(sizeof(struct ab8500_fg), GFP_KERNEL);
+       di = kzalloc(sizeof(*di), GFP_KERNEL);
        if (!di)
                return -ENOMEM;
 
@@ -2461,7 +2466,6 @@ static int __devinit ab8500_fg_probe(struct platform_device *pdev)
        di->gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
 
        /* get fg specific platform data */
-       plat_data = pdev->dev.platform_data;
        di->pdata = plat_data->fg;
        if (!di->pdata) {
                dev_err(di->dev, "no fg platform data supplied\n");
index 9eca9f1ff0eae2e5b381e503f035ceb446340e66..86935ec1895431aac77c47de1860711ae26e21eb 100644 (file)
 #include <linux/power/charger-manager.h>
 #include <linux/regulator/consumer.h>
 
+static const char * const default_event_names[] = {
+       [CM_EVENT_UNKNOWN] = "Unknown",
+       [CM_EVENT_BATT_FULL] = "Battery Full",
+       [CM_EVENT_BATT_IN] = "Battery Inserted",
+       [CM_EVENT_BATT_OUT] = "Battery Pulled Out",
+       [CM_EVENT_EXT_PWR_IN_OUT] = "External Power Attach/Detach",
+       [CM_EVENT_CHG_START_STOP] = "Charging Start/Stop",
+       [CM_EVENT_OTHERS] = "Other battery events"
+};
+
 /*
  * Regard CM_JIFFIES_SMALL jiffies is small enough to ignore for
  * delayed works so that we can run delayed works with CM_JIFFIES_SMALL
@@ -57,6 +67,12 @@ static bool cm_suspended;
 static bool cm_rtc_set;
 static unsigned long cm_suspend_duration_ms;
 
+/* About normal (not suspended) monitoring */
+static unsigned long polling_jiffy = ULONG_MAX; /* ULONG_MAX: no polling */
+static unsigned long next_polling; /* Next appointed polling time */
+static struct workqueue_struct *cm_wq; /* init at driver add */
+static struct delayed_work cm_monitor_work; /* init at driver add */
+
 /* Global charger-manager description */
 static struct charger_global_desc *g_desc; /* init with setup_charger_manager */
 
@@ -71,6 +87,11 @@ static bool is_batt_present(struct charger_manager *cm)
        int i, ret;
 
        switch (cm->desc->battery_present) {
+       case CM_BATTERY_PRESENT:
+               present = true;
+               break;
+       case CM_NO_BATTERY:
+               break;
        case CM_FUEL_GAUGE:
                ret = cm->fuel_gauge->get_property(cm->fuel_gauge,
                                POWER_SUPPLY_PROP_PRESENT, &val);
@@ -278,6 +299,26 @@ static int try_charger_enable(struct charger_manager *cm, bool enable)
        return err;
 }
 
+/**
+ * try_charger_restart - Restart charging.
+ * @cm: the Charger Manager representing the battery.
+ *
+ * Restart charging by turning off and on the charger.
+ */
+static int try_charger_restart(struct charger_manager *cm)
+{
+       int err;
+
+       if (cm->emergency_stop)
+               return -EAGAIN;
+
+       err = try_charger_enable(cm, false);
+       if (err)
+               return err;
+
+       return try_charger_enable(cm, true);
+}
+
 /**
  * uevent_notify - Let users know something has changed.
  * @cm: the Charger Manager representing the battery.
@@ -333,6 +374,46 @@ static void uevent_notify(struct charger_manager *cm, const char *event)
        dev_info(cm->dev, event);
 }
 
+/**
+ * fullbatt_vchk - Check voltage drop some times after "FULL" event.
+ * @work: the work_struct appointing the function
+ *
+ * If a user has designated "fullbatt_vchkdrop_ms/uV" values with
+ * charger_desc, Charger Manager checks voltage drop after the battery
+ * "FULL" event. It checks whether the voltage has dropped more than
+ * fullbatt_vchkdrop_uV by calling this function after fullbatt_vchkrop_ms.
+ */
+static void fullbatt_vchk(struct work_struct *work)
+{
+       struct delayed_work *dwork = to_delayed_work(work);
+       struct charger_manager *cm = container_of(dwork,
+                       struct charger_manager, fullbatt_vchk_work);
+       struct charger_desc *desc = cm->desc;
+       int batt_uV, err, diff;
+
+       /* remove the appointment for fullbatt_vchk */
+       cm->fullbatt_vchk_jiffies_at = 0;
+
+       if (!desc->fullbatt_vchkdrop_uV || !desc->fullbatt_vchkdrop_ms)
+               return;
+
+       err = get_batt_uV(cm, &batt_uV);
+       if (err) {
+               dev_err(cm->dev, "%s: get_batt_uV error(%d).\n", __func__, err);
+               return;
+       }
+
+       diff = cm->fullbatt_vchk_uV;
+       diff -= batt_uV;
+
+       dev_dbg(cm->dev, "VBATT dropped %duV after full-batt.\n", diff);
+
+       if (diff > desc->fullbatt_vchkdrop_uV) {
+               try_charger_restart(cm);
+               uevent_notify(cm, "Recharge");
+       }
+}
+
 /**
  * _cm_monitor - Monitor the temperature and return true for exceptions.
  * @cm: the Charger Manager representing the battery.
@@ -392,6 +473,131 @@ static bool cm_monitor(void)
        return stop;
 }
 
+/**
+ * _setup_polling - Setup the next instance of polling.
+ * @work: work_struct of the function _setup_polling.
+ */
+static void _setup_polling(struct work_struct *work)
+{
+       unsigned long min = ULONG_MAX;
+       struct charger_manager *cm;
+       bool keep_polling = false;
+       unsigned long _next_polling;
+
+       mutex_lock(&cm_list_mtx);
+
+       list_for_each_entry(cm, &cm_list, entry) {
+               if (is_polling_required(cm) && cm->desc->polling_interval_ms) {
+                       keep_polling = true;
+
+                       if (min > cm->desc->polling_interval_ms)
+                               min = cm->desc->polling_interval_ms;
+               }
+       }
+
+       polling_jiffy = msecs_to_jiffies(min);
+       if (polling_jiffy <= CM_JIFFIES_SMALL)
+               polling_jiffy = CM_JIFFIES_SMALL + 1;
+
+       if (!keep_polling)
+               polling_jiffy = ULONG_MAX;
+       if (polling_jiffy == ULONG_MAX)
+               goto out;
+
+       WARN(cm_wq == NULL, "charger-manager: workqueue not initialized"
+                           ". try it later. %s\n", __func__);
+
+       _next_polling = jiffies + polling_jiffy;
+
+       if (!delayed_work_pending(&cm_monitor_work) ||
+           (delayed_work_pending(&cm_monitor_work) &&
+            time_after(next_polling, _next_polling))) {
+               cancel_delayed_work_sync(&cm_monitor_work);
+               next_polling = jiffies + polling_jiffy;
+               queue_delayed_work(cm_wq, &cm_monitor_work, polling_jiffy);
+       }
+
+out:
+       mutex_unlock(&cm_list_mtx);
+}
+static DECLARE_WORK(setup_polling, _setup_polling);
+
+/**
+ * cm_monitor_poller - The Monitor / Poller.
+ * @work: work_struct of the function cm_monitor_poller
+ *
+ * During non-suspended state, cm_monitor_poller is used to poll and monitor
+ * the batteries.
+ */
+static void cm_monitor_poller(struct work_struct *work)
+{
+       cm_monitor();
+       schedule_work(&setup_polling);
+}
+
+/**
+ * fullbatt_handler - Event handler for CM_EVENT_BATT_FULL
+ * @cm: the Charger Manager representing the battery.
+ */
+static void fullbatt_handler(struct charger_manager *cm)
+{
+       struct charger_desc *desc = cm->desc;
+
+       if (!desc->fullbatt_vchkdrop_uV || !desc->fullbatt_vchkdrop_ms)
+               goto out;
+
+       if (cm_suspended)
+               device_set_wakeup_capable(cm->dev, true);
+
+       if (delayed_work_pending(&cm->fullbatt_vchk_work))
+               cancel_delayed_work(&cm->fullbatt_vchk_work);
+       queue_delayed_work(cm_wq, &cm->fullbatt_vchk_work,
+                          msecs_to_jiffies(desc->fullbatt_vchkdrop_ms));
+       cm->fullbatt_vchk_jiffies_at = jiffies + msecs_to_jiffies(
+                                      desc->fullbatt_vchkdrop_ms);
+
+       if (cm->fullbatt_vchk_jiffies_at == 0)
+               cm->fullbatt_vchk_jiffies_at = 1;
+
+out:
+       dev_info(cm->dev, "EVENT_HANDLE: Battery Fully Charged.\n");
+       uevent_notify(cm, default_event_names[CM_EVENT_BATT_FULL]);
+}
+
+/**
+ * battout_handler - Event handler for CM_EVENT_BATT_OUT
+ * @cm: the Charger Manager representing the battery.
+ */
+static void battout_handler(struct charger_manager *cm)
+{
+       if (cm_suspended)
+               device_set_wakeup_capable(cm->dev, true);
+
+       if (!is_batt_present(cm)) {
+               dev_emerg(cm->dev, "Battery Pulled Out!\n");
+               uevent_notify(cm, default_event_names[CM_EVENT_BATT_OUT]);
+       } else {
+               uevent_notify(cm, "Battery Reinserted?");
+       }
+}
+
+/**
+ * misc_event_handler - Handler for other evnets
+ * @cm: the Charger Manager representing the battery.
+ * @type: the Charger Manager representing the battery.
+ */
+static void misc_event_handler(struct charger_manager *cm,
+                       enum cm_event_types type)
+{
+       if (cm_suspended)
+               device_set_wakeup_capable(cm->dev, true);
+
+       if (!delayed_work_pending(&cm_monitor_work) &&
+           is_polling_required(cm) && cm->desc->polling_interval_ms)
+               schedule_work(&setup_polling);
+       uevent_notify(cm, default_event_names[type]);
+}
+
 static int charger_get_property(struct power_supply *psy,
                enum power_supply_property psp,
                union power_supply_propval *val)
@@ -613,6 +819,21 @@ static bool cm_setup_timer(void)
        mutex_lock(&cm_list_mtx);
 
        list_for_each_entry(cm, &cm_list, entry) {
+               unsigned int fbchk_ms = 0;
+
+               /* fullbatt_vchk is required. setup timer for that */
+               if (cm->fullbatt_vchk_jiffies_at) {
+                       fbchk_ms = jiffies_to_msecs(cm->fullbatt_vchk_jiffies_at
+                                                   - jiffies);
+                       if (time_is_before_eq_jiffies(
+                               cm->fullbatt_vchk_jiffies_at) ||
+                               msecs_to_jiffies(fbchk_ms) < CM_JIFFIES_SMALL) {
+                               fullbatt_vchk(&cm->fullbatt_vchk_work.work);
+                               fbchk_ms = 0;
+                       }
+               }
+               CM_MIN_VALID(wakeup_ms, fbchk_ms);
+
                /* Skip if polling is not required for this CM */
                if (!is_polling_required(cm) && !cm->emergency_stop)
                        continue;
@@ -672,6 +893,23 @@ static bool cm_setup_timer(void)
        return false;
 }
 
+static void _cm_fbchk_in_suspend(struct charger_manager *cm)
+{
+       unsigned long jiffy_now = jiffies;
+
+       if (!cm->fullbatt_vchk_jiffies_at)
+               return;
+
+       if (g_desc && g_desc->assume_timer_stops_in_suspend)
+               jiffy_now += msecs_to_jiffies(cm_suspend_duration_ms);
+
+       /* Execute now if it's going to be executed not too long after */
+       jiffy_now += CM_JIFFIES_SMALL;
+
+       if (time_after_eq(jiffy_now, cm->fullbatt_vchk_jiffies_at))
+               fullbatt_vchk(&cm->fullbatt_vchk_work.work);
+}
+
 /**
  * cm_suspend_again - Determine whether suspend again or not
  *
@@ -693,6 +931,8 @@ bool cm_suspend_again(void)
        ret = true;
        mutex_lock(&cm_list_mtx);
        list_for_each_entry(cm, &cm_list, entry) {
+               _cm_fbchk_in_suspend(cm);
+
                if (cm->status_save_ext_pwr_inserted != is_ext_pwr_online(cm) ||
                    cm->status_save_batt != is_batt_present(cm)) {
                        ret = false;
@@ -796,6 +1036,21 @@ static int charger_manager_probe(struct platform_device *pdev)
        memcpy(cm->desc, desc, sizeof(struct charger_desc));
        cm->last_temp_mC = INT_MIN; /* denotes "unmeasured, yet" */
 
+       /*
+        * The following two do not need to be errors.
+        * Users may intentionally ignore those two features.
+        */
+       if (desc->fullbatt_uV == 0) {
+               dev_info(&pdev->dev, "Ignoring full-battery voltage threshold"
+                                       " as it is not supplied.");
+       }
+       if (!desc->fullbatt_vchkdrop_ms || !desc->fullbatt_vchkdrop_uV) {
+               dev_info(&pdev->dev, "Disabling full-battery voltage drop "
+                               "checking mechanism as it is not supplied.");
+               desc->fullbatt_vchkdrop_ms = 0;
+               desc->fullbatt_vchkdrop_uV = 0;
+       }
+
        if (!desc->charger_regulators || desc->num_charger_regulators < 1) {
                ret = -EINVAL;
                dev_err(&pdev->dev, "charger_regulators undefined.\n");
@@ -903,6 +1158,8 @@ static int charger_manager_probe(struct platform_device *pdev)
                cm->charger_psy.num_properties++;
        }
 
+       INIT_DELAYED_WORK(&cm->fullbatt_vchk_work, fullbatt_vchk);
+
        ret = power_supply_register(NULL, &cm->charger_psy);
        if (ret) {
                dev_err(&pdev->dev, "Cannot register charger-manager with"
@@ -928,6 +1185,15 @@ static int charger_manager_probe(struct platform_device *pdev)
        list_add(&cm->entry, &cm_list);
        mutex_unlock(&cm_list_mtx);
 
+       /*
+        * Charger-manager is capable of waking up the systme from sleep
+        * when event is happend through cm_notify_event()
+        */
+       device_init_wakeup(&pdev->dev, true);
+       device_set_wakeup_capable(&pdev->dev, false);
+
+       schedule_work(&setup_polling);
+
        return 0;
 
 err_chg_enable:
@@ -958,9 +1224,17 @@ static int __devexit charger_manager_remove(struct platform_device *pdev)
        list_del(&cm->entry);
        mutex_unlock(&cm_list_mtx);
 
+       if (work_pending(&setup_polling))
+               cancel_work_sync(&setup_polling);
+       if (delayed_work_pending(&cm_monitor_work))
+               cancel_delayed_work_sync(&cm_monitor_work);
+
        regulator_bulk_free(desc->num_charger_regulators,
                            desc->charger_regulators);
        power_supply_unregister(&cm->charger_psy);
+
+       try_charger_enable(cm, false);
+
        kfree(cm->charger_psy.properties);
        kfree(cm->charger_stat);
        kfree(cm->desc);
@@ -975,6 +1249,18 @@ static const struct platform_device_id charger_manager_id[] = {
 };
 MODULE_DEVICE_TABLE(platform, charger_manager_id);
 
+static int cm_suspend_noirq(struct device *dev)
+{
+       int ret = 0;
+
+       if (device_may_wakeup(dev)) {
+               device_set_wakeup_capable(dev, false);
+               ret = -EAGAIN;
+       }
+
+       return ret;
+}
+
 static int cm_suspend_prepare(struct device *dev)
 {
        struct charger_manager *cm = dev_get_drvdata(dev);
@@ -1000,6 +1286,8 @@ static int cm_suspend_prepare(struct device *dev)
                cm_suspended = true;
        }
 
+       if (delayed_work_pending(&cm->fullbatt_vchk_work))
+               cancel_delayed_work(&cm->fullbatt_vchk_work);
        cm->status_save_ext_pwr_inserted = is_ext_pwr_online(cm);
        cm->status_save_batt = is_batt_present(cm);
 
@@ -1027,11 +1315,40 @@ static void cm_suspend_complete(struct device *dev)
                cm_rtc_set = false;
        }
 
+       /* Re-enqueue delayed work (fullbatt_vchk_work) */
+       if (cm->fullbatt_vchk_jiffies_at) {
+               unsigned long delay = 0;
+               unsigned long now = jiffies + CM_JIFFIES_SMALL;
+
+               if (time_after_eq(now, cm->fullbatt_vchk_jiffies_at)) {
+                       delay = (unsigned long)((long)now
+                               - (long)(cm->fullbatt_vchk_jiffies_at));
+                       delay = jiffies_to_msecs(delay);
+               } else {
+                       delay = 0;
+               }
+
+               /*
+                * Account for cm_suspend_duration_ms if
+                * assume_timer_stops_in_suspend is active
+                */
+               if (g_desc && g_desc->assume_timer_stops_in_suspend) {
+                       if (delay > cm_suspend_duration_ms)
+                               delay -= cm_suspend_duration_ms;
+                       else
+                               delay = 0;
+               }
+
+               queue_delayed_work(cm_wq, &cm->fullbatt_vchk_work,
+                                  msecs_to_jiffies(delay));
+       }
+       device_set_wakeup_capable(cm->dev, false);
        uevent_notify(cm, NULL);
 }
 
 static const struct dev_pm_ops charger_manager_pm = {
        .prepare        = cm_suspend_prepare,
+       .suspend_noirq  = cm_suspend_noirq,
        .complete       = cm_suspend_complete,
 };
 
@@ -1048,16 +1365,91 @@ static struct platform_driver charger_manager_driver = {
 
 static int __init charger_manager_init(void)
 {
+       cm_wq = create_freezable_workqueue("charger_manager");
+       INIT_DELAYED_WORK(&cm_monitor_work, cm_monitor_poller);
+
        return platform_driver_register(&charger_manager_driver);
 }
 late_initcall(charger_manager_init);
 
 static void __exit charger_manager_cleanup(void)
 {
+       destroy_workqueue(cm_wq);
+       cm_wq = NULL;
+
        platform_driver_unregister(&charger_manager_driver);
 }
 module_exit(charger_manager_cleanup);
 
+/**
+ * find_power_supply - find the associated power_supply of charger
+ * @cm: the Charger Manager representing the battery
+ * @psy: pointer to instance of charger's power_supply
+ */
+static bool find_power_supply(struct charger_manager *cm,
+                       struct power_supply *psy)
+{
+       int i;
+       bool found = false;
+
+       for (i = 0; cm->charger_stat[i]; i++) {
+               if (psy == cm->charger_stat[i]) {
+                       found = true;
+                       break;
+               }
+       }
+
+       return found;
+}
+
+/**
+ * cm_notify_event - charger driver notify Charger Manager of charger event
+ * @psy: pointer to instance of charger's power_supply
+ * @type: type of charger event
+ * @msg: optional message passed to uevent_notify fuction
+ */
+void cm_notify_event(struct power_supply *psy, enum cm_event_types type,
+                    char *msg)
+{
+       struct charger_manager *cm;
+       bool found_power_supply = false;
+
+       if (psy == NULL)
+               return;
+
+       mutex_lock(&cm_list_mtx);
+       list_for_each_entry(cm, &cm_list, entry) {
+               found_power_supply = find_power_supply(cm, psy);
+               if (found_power_supply)
+                       break;
+       }
+       mutex_unlock(&cm_list_mtx);
+
+       if (!found_power_supply)
+               return;
+
+       switch (type) {
+       case CM_EVENT_BATT_FULL:
+               fullbatt_handler(cm);
+               break;
+       case CM_EVENT_BATT_OUT:
+               battout_handler(cm);
+               break;
+       case CM_EVENT_BATT_IN:
+       case CM_EVENT_EXT_PWR_IN_OUT ... CM_EVENT_CHG_START_STOP:
+               misc_event_handler(cm, type);
+               break;
+       case CM_EVENT_UNKNOWN:
+       case CM_EVENT_OTHERS:
+               uevent_notify(cm, msg ? msg : default_event_names[type]);
+               break;
+       default:
+               dev_err(cm->dev, "%s type not specified.\n", __func__);
+               break;
+       }
+}
+EXPORT_SYMBOL_GPL(cm_notify_event);
+
 MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
 MODULE_DESCRIPTION("Charger Manager");
 MODULE_LICENSE("GPL");
index ca0d653d0a7a2c3ac0d4c422f66317c3993dc49b..975684a40f1519ad33e5f630ecfa82020d71d960 100644 (file)
@@ -643,9 +643,7 @@ static ssize_t ds2781_read_param_eeprom_bin(struct file *filp,
        struct power_supply *psy = to_power_supply(dev);
        struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
 
-       count = min_t(loff_t, count,
-               DS2781_EEPROM_BLOCK1_END -
-               DS2781_EEPROM_BLOCK1_START + 1 - off);
+       count = min_t(loff_t, count, DS2781_PARAM_EEPROM_SIZE - off);
 
        return ds2781_read_block(dev_info, buf,
                                DS2781_EEPROM_BLOCK1_START + off, count);
@@ -661,9 +659,7 @@ static ssize_t ds2781_write_param_eeprom_bin(struct file *filp,
        struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
        int ret;
 
-       count = min_t(loff_t, count,
-               DS2781_EEPROM_BLOCK1_END -
-               DS2781_EEPROM_BLOCK1_START + 1 - off);
+       count = min_t(loff_t, count, DS2781_PARAM_EEPROM_SIZE - off);
 
        ret = ds2781_write(dev_info, buf,
                                DS2781_EEPROM_BLOCK1_START + off, count);
@@ -682,7 +678,7 @@ static struct bin_attribute ds2781_param_eeprom_bin_attr = {
                .name = "param_eeprom",
                .mode = S_IRUGO | S_IWUSR,
        },
-       .size = DS2781_EEPROM_BLOCK1_END - DS2781_EEPROM_BLOCK1_START + 1,
+       .size = DS2781_PARAM_EEPROM_SIZE,
        .read = ds2781_read_param_eeprom_bin,
        .write = ds2781_write_param_eeprom_bin,
 };
@@ -696,9 +692,7 @@ static ssize_t ds2781_read_user_eeprom_bin(struct file *filp,
        struct power_supply *psy = to_power_supply(dev);
        struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
 
-       count = min_t(loff_t, count,
-               DS2781_EEPROM_BLOCK0_END -
-               DS2781_EEPROM_BLOCK0_START + 1 - off);
+       count = min_t(loff_t, count, DS2781_USER_EEPROM_SIZE - off);
 
        return ds2781_read_block(dev_info, buf,
                                DS2781_EEPROM_BLOCK0_START + off, count);
@@ -715,9 +709,7 @@ static ssize_t ds2781_write_user_eeprom_bin(struct file *filp,
        struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
        int ret;
 
-       count = min_t(loff_t, count,
-               DS2781_EEPROM_BLOCK0_END -
-               DS2781_EEPROM_BLOCK0_START + 1 - off);
+       count = min_t(loff_t, count, DS2781_USER_EEPROM_SIZE - off);
 
        ret = ds2781_write(dev_info, buf,
                                DS2781_EEPROM_BLOCK0_START + off, count);
@@ -736,7 +728,7 @@ static struct bin_attribute ds2781_user_eeprom_bin_attr = {
                .name = "user_eeprom",
                .mode = S_IRUGO | S_IWUSR,
        },
-       .size = DS2781_EEPROM_BLOCK0_END - DS2781_EEPROM_BLOCK0_START + 1,
+       .size = DS2781_USER_EEPROM_SIZE,
        .read = ds2781_read_user_eeprom_bin,
        .write = ds2781_write_user_eeprom_bin,
 };
index 39eb50f35f09fd777445a53202fb30823d9f9438..e5ccd29797732d8dae6992a558eabd896fe6a9f0 100644 (file)
@@ -474,13 +474,13 @@ static int __devinit isp1704_charger_probe(struct platform_device *pdev)
 fail2:
        power_supply_unregister(&isp->psy);
 fail1:
+       isp1704_charger_set_power(isp, 0);
        usb_put_transceiver(isp->phy);
 fail0:
        kfree(isp);
 
        dev_err(&pdev->dev, "failed to register isp1704 with error %d\n", ret);
 
-       isp1704_charger_set_power(isp, 0);
        return ret;
 }
 
index 04620c2cb388f3f5f2411f6a2b8191c5b36b459f..140788b309f84fe26056e77636eff26b954d8cb8 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/i2c.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
+#include <linux/pm.h>
 #include <linux/mod_devicetable.h>
 #include <linux/power_supply.h>
 #include <linux/power/max17042_battery.h>
 #define dP_ACC_100     0x1900
 #define dP_ACC_200     0x3200
 
+#define MAX17042_IC_VERSION    0x0092
+#define MAX17047_IC_VERSION    0x00AC  /* same for max17050 */
+
 struct max17042_chip {
        struct i2c_client *client;
        struct power_supply battery;
+       enum max170xx_chip_type chip_type;
        struct max17042_platform_data *pdata;
        struct work_struct work;
        int    init_complete;
@@ -105,6 +110,7 @@ static enum power_supply_property max17042_battery_props[] = {
        POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
        POWER_SUPPLY_PROP_VOLTAGE_NOW,
        POWER_SUPPLY_PROP_VOLTAGE_AVG,
+       POWER_SUPPLY_PROP_VOLTAGE_OCV,
        POWER_SUPPLY_PROP_CAPACITY,
        POWER_SUPPLY_PROP_CHARGE_FULL,
        POWER_SUPPLY_PROP_TEMP,
@@ -150,7 +156,10 @@ static int max17042_get_property(struct power_supply *psy,
                val->intval *= 20000; /* Units of LSB = 20mV */
                break;
        case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
-               ret = max17042_read_reg(chip->client, MAX17042_V_empty);
+               if (chip->chip_type == MAX17042)
+                       ret = max17042_read_reg(chip->client, MAX17042_V_empty);
+               else
+                       ret = max17042_read_reg(chip->client, MAX17047_V_empty);
                if (ret < 0)
                        return ret;
 
@@ -169,6 +178,13 @@ static int max17042_get_property(struct power_supply *psy,
                if (ret < 0)
                        return ret;
 
+               val->intval = ret * 625 / 8;
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_OCV:
+               ret = max17042_read_reg(chip->client, MAX17042_OCVInternal);
+               if (ret < 0)
+                       return ret;
+
                val->intval = ret * 625 / 8;
                break;
        case POWER_SUPPLY_PROP_CAPACITY:
@@ -325,11 +341,10 @@ static inline int max17042_model_data_compare(struct max17042_chip *chip,
 static int max17042_init_model(struct max17042_chip *chip)
 {
        int ret;
-       int table_size =
-               sizeof(chip->pdata->config_data->cell_char_tbl)/sizeof(u16);
+       int table_size = ARRAY_SIZE(chip->pdata->config_data->cell_char_tbl);
        u16 *temp_data;
 
-       temp_data = kzalloc(table_size, GFP_KERNEL);
+       temp_data = kcalloc(table_size, sizeof(*temp_data), GFP_KERNEL);
        if (!temp_data)
                return -ENOMEM;
 
@@ -354,12 +369,11 @@ static int max17042_init_model(struct max17042_chip *chip)
 static int max17042_verify_model_lock(struct max17042_chip *chip)
 {
        int i;
-       int table_size =
-               sizeof(chip->pdata->config_data->cell_char_tbl);
+       int table_size = ARRAY_SIZE(chip->pdata->config_data->cell_char_tbl);
        u16 *temp_data;
        int ret = 0;
 
-       temp_data = kzalloc(table_size, GFP_KERNEL);
+       temp_data = kcalloc(table_size, sizeof(*temp_data), GFP_KERNEL);
        if (!temp_data)
                return -ENOMEM;
 
@@ -382,6 +396,9 @@ static void max17042_write_config_regs(struct max17042_chip *chip)
        max17042_write_reg(chip->client, MAX17042_FilterCFG,
                        config->filter_cfg);
        max17042_write_reg(chip->client, MAX17042_RelaxCFG, config->relax_cfg);
+       if (chip->chip_type == MAX17047)
+               max17042_write_reg(chip->client, MAX17047_FullSOCThr,
+                                               config->full_soc_thresh);
 }
 
 static void  max17042_write_custom_regs(struct max17042_chip *chip)
@@ -392,12 +409,23 @@ static void  max17042_write_custom_regs(struct max17042_chip *chip)
                                config->rcomp0);
        max17042_write_verify_reg(chip->client, MAX17042_TempCo,
                                config->tcompc0);
-       max17042_write_reg(chip->client, MAX17042_EmptyTempCo,
-                       config->empty_tempco);
-       max17042_write_verify_reg(chip->client, MAX17042_K_empty0,
-                               config->kempty0);
        max17042_write_verify_reg(chip->client, MAX17042_ICHGTerm,
                                config->ichgt_term);
+       if (chip->chip_type == MAX17042) {
+               max17042_write_reg(chip->client, MAX17042_EmptyTempCo,
+                                       config->empty_tempco);
+               max17042_write_verify_reg(chip->client, MAX17042_K_empty0,
+                                       config->kempty0);
+       } else {
+               max17042_write_verify_reg(chip->client, MAX17047_QRTbl00,
+                                               config->qrtbl00);
+               max17042_write_verify_reg(chip->client, MAX17047_QRTbl10,
+                                               config->qrtbl10);
+               max17042_write_verify_reg(chip->client, MAX17047_QRTbl20,
+                                               config->qrtbl20);
+               max17042_write_verify_reg(chip->client, MAX17047_QRTbl30,
+                                               config->qrtbl30);
+       }
 }
 
 static void max17042_update_capacity_regs(struct max17042_chip *chip)
@@ -453,6 +481,8 @@ static void max17042_load_new_capacity_params(struct max17042_chip *chip)
                        config->design_cap);
        max17042_write_verify_reg(chip->client, MAX17042_FullCAPNom,
                        config->fullcapnom);
+       /* Update SOC register with new SOC */
+       max17042_write_reg(chip->client, MAX17042_RepSOC, vfSoc);
 }
 
 /*
@@ -489,20 +519,28 @@ static inline void max17042_override_por_values(struct max17042_chip *chip)
 
        max17042_override_por(client, MAX17042_FullCAP, config->fullcap);
        max17042_override_por(client, MAX17042_FullCAPNom, config->fullcapnom);
-       max17042_override_por(client, MAX17042_SOC_empty, config->socempty);
+       if (chip->chip_type == MAX17042)
+               max17042_override_por(client, MAX17042_SOC_empty,
+                                               config->socempty);
        max17042_override_por(client, MAX17042_LAvg_empty, config->lavg_empty);
        max17042_override_por(client, MAX17042_dQacc, config->dqacc);
        max17042_override_por(client, MAX17042_dPacc, config->dpacc);
 
-       max17042_override_por(client, MAX17042_V_empty, config->vempty);
+       if (chip->chip_type == MAX17042)
+               max17042_override_por(client, MAX17042_V_empty, config->vempty);
+       else
+               max17042_override_por(client, MAX17047_V_empty, config->vempty);
        max17042_override_por(client, MAX17042_TempNom, config->temp_nom);
        max17042_override_por(client, MAX17042_TempLim, config->temp_lim);
        max17042_override_por(client, MAX17042_FCTC, config->fctc);
        max17042_override_por(client, MAX17042_RCOMP0, config->rcomp0);
        max17042_override_por(client, MAX17042_TempCo, config->tcompc0);
-       max17042_override_por(client, MAX17042_EmptyTempCo,
-                       config->empty_tempco);
-       max17042_override_por(client, MAX17042_K_empty0, config->kempty0);
+       if (chip->chip_type) {
+               max17042_override_por(client, MAX17042_EmptyTempCo,
+                                       config->empty_tempco);
+               max17042_override_por(client, MAX17042_K_empty0,
+                                       config->kempty0);
+       }
 }
 
 static int max17042_init_chip(struct max17042_chip *chip)
@@ -659,7 +697,19 @@ static int __devinit max17042_probe(struct i2c_client *client,
 
        i2c_set_clientdata(client, chip);
 
-       chip->battery.name              = "max17042_battery";
+       ret = max17042_read_reg(chip->client, MAX17042_DevName);
+       if (ret == MAX17042_IC_VERSION) {
+               dev_dbg(&client->dev, "chip type max17042 detected\n");
+               chip->chip_type = MAX17042;
+       } else if (ret == MAX17047_IC_VERSION) {
+               dev_dbg(&client->dev, "chip type max17047/50 detected\n");
+               chip->chip_type = MAX17047;
+       } else {
+               dev_err(&client->dev, "device version mismatch: %x\n", ret);
+               return -EIO;
+       }
+
+       chip->battery.name              = "max170xx_battery";
        chip->battery.type              = POWER_SUPPLY_TYPE_BATTERY;
        chip->battery.get_property      = max17042_get_property;
        chip->battery.properties        = max17042_battery_props;
@@ -683,6 +733,12 @@ static int __devinit max17042_probe(struct i2c_client *client,
                max17042_write_reg(client, MAX17042_LearnCFG, 0x0007);
        }
 
+       ret = power_supply_register(&client->dev, &chip->battery);
+       if (ret) {
+               dev_err(&client->dev, "failed: power supply register\n");
+               return ret;
+       }
+
        if (client->irq) {
                ret = request_threaded_irq(client->irq, NULL,
                                                max17042_thread_handler,
@@ -693,13 +749,14 @@ static int __devinit max17042_probe(struct i2c_client *client,
                        reg |= CONFIG_ALRT_BIT_ENBL;
                        max17042_write_reg(client, MAX17042_CONFIG, reg);
                        max17042_set_soc_threshold(chip, 1);
-               } else
+               } else {
+                       client->irq = 0;
                        dev_err(&client->dev, "%s(): cannot get IRQ\n",
                                __func__);
+               }
        }
 
        reg = max17042_read_reg(chip->client, MAX17042_STATUS);
-
        if (reg & STATUS_POR_BIT) {
                INIT_WORK(&chip->work, max17042_init_worker);
                schedule_work(&chip->work);
@@ -707,23 +764,65 @@ static int __devinit max17042_probe(struct i2c_client *client,
                chip->init_complete = 1;
        }
 
-       ret = power_supply_register(&client->dev, &chip->battery);
-       if (ret)
-               dev_err(&client->dev, "failed: power supply register\n");
-       return ret;
+       return 0;
 }
 
 static int __devexit max17042_remove(struct i2c_client *client)
 {
        struct max17042_chip *chip = i2c_get_clientdata(client);
 
+       if (client->irq)
+               free_irq(client->irq, chip);
        power_supply_unregister(&chip->battery);
        return 0;
 }
 
+#ifdef CONFIG_PM
+static int max17042_suspend(struct device *dev)
+{
+       struct max17042_chip *chip = dev_get_drvdata(dev);
+
+       /*
+        * disable the irq and enable irq_wake
+        * capability to the interrupt line.
+        */
+       if (chip->client->irq) {
+               disable_irq(chip->client->irq);
+               enable_irq_wake(chip->client->irq);
+       }
+
+       return 0;
+}
+
+static int max17042_resume(struct device *dev)
+{
+       struct max17042_chip *chip = dev_get_drvdata(dev);
+
+       if (chip->client->irq) {
+               disable_irq_wake(chip->client->irq);
+               enable_irq(chip->client->irq);
+               /* re-program the SOC thresholds to 1% change */
+               max17042_set_soc_threshold(chip, 1);
+       }
+
+       return 0;
+}
+
+static const struct dev_pm_ops max17042_pm_ops = {
+       .suspend        = max17042_suspend,
+       .resume         = max17042_resume,
+};
+
+#define MAX17042_PM_OPS (&max17042_pm_ops)
+#else
+#define MAX17042_PM_OPS NULL
+#endif
+
 #ifdef CONFIG_OF
 static const struct of_device_id max17042_dt_match[] = {
        { .compatible = "maxim,max17042" },
+       { .compatible = "maxim,max17047" },
+       { .compatible = "maxim,max17050" },
        { },
 };
 MODULE_DEVICE_TABLE(of, max17042_dt_match);
@@ -731,6 +830,8 @@ MODULE_DEVICE_TABLE(of, max17042_dt_match);
 
 static const struct i2c_device_id max17042_id[] = {
        { "max17042", 0 },
+       { "max17047", 1 },
+       { "max17050", 2 },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, max17042_id);
@@ -739,6 +840,7 @@ static struct i2c_driver max17042_i2c_driver = {
        .driver = {
                .name   = "max17042",
                .of_match_table = of_match_ptr(max17042_dt_match),
+               .pm     = MAX17042_PM_OPS,
        },
        .probe          = max17042_probe,
        .remove         = __devexit_p(max17042_remove),
index 4368e7d61316bb37c9a3565ca1c0400964aa6781..4150747f9186f8dfaff9faf81bf03a7ade8cb9fa 100644 (file)
@@ -146,6 +146,7 @@ static struct device_attribute power_supply_attrs[] = {
        POWER_SUPPLY_ATTR(voltage_min_design),
        POWER_SUPPLY_ATTR(voltage_now),
        POWER_SUPPLY_ATTR(voltage_avg),
+       POWER_SUPPLY_ATTR(voltage_ocv),
        POWER_SUPPLY_ATTR(current_max),
        POWER_SUPPLY_ATTR(current_now),
        POWER_SUPPLY_ATTR(current_avg),
index 06b659d9179009e032bd7aaf2953aa8bf82c84fb..a5b6849d4123b51b60d2431bbcd72cc9f13c86f4 100644 (file)
@@ -89,7 +89,7 @@ static const struct chip_data {
        [REG_CURRENT] =
                SBS_DATA(POWER_SUPPLY_PROP_CURRENT_NOW, 0x0A, -32768, 32767),
        [REG_CAPACITY] =
-               SBS_DATA(POWER_SUPPLY_PROP_CAPACITY, 0x0E, 0, 100),
+               SBS_DATA(POWER_SUPPLY_PROP_CAPACITY, 0x0D, 0, 100),
        [REG_REMAINING_CAPACITY] =
                SBS_DATA(POWER_SUPPLY_PROP_ENERGY_NOW, 0x0F, 0, 65535),
        [REG_REMAINING_CAPACITY_CHARGE] =
index ce1694d1a36584b3e910adde4dd0ff5800fc4c72..f8eedd8a676fc68ad21f45b8bfa4ddec55add723 100644 (file)
@@ -11,7 +11,7 @@
  * published by the Free Software Foundation.
  */
 
-#include <linux/debugfs.h>
+#include <linux/err.h>
 #include <linux/gpio.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
@@ -21,7 +21,7 @@
 #include <linux/mutex.h>
 #include <linux/power_supply.h>
 #include <linux/power/smb347-charger.h>
-#include <linux/seq_file.h>
+#include <linux/regmap.h>
 
 /*
  * Configuration registers. These are mirrored to volatile RAM and can be
@@ -39,6 +39,7 @@
 #define CFG_CURRENT_LIMIT_DC_SHIFT             4
 #define CFG_CURRENT_LIMIT_USB_MASK             0x0f
 #define CFG_FLOAT_VOLTAGE                      0x03
+#define CFG_FLOAT_VOLTAGE_FLOAT_MASK           0x3f
 #define CFG_FLOAT_VOLTAGE_THRESHOLD_MASK       0xc0
 #define CFG_FLOAT_VOLTAGE_THRESHOLD_SHIFT      6
 #define CFG_STAT                               0x05
 #define STAT_C_CHARGER_ERROR                   BIT(6)
 #define STAT_E                                 0x3f
 
+#define SMB347_MAX_REGISTER                    0x3f
+
 /**
  * struct smb347_charger - smb347 charger instance
  * @lock: protects concurrent access to online variables
- * @client: pointer to i2c client
+ * @dev: pointer to device
+ * @regmap: pointer to driver regmap
  * @mains: power_supply instance for AC/DC power
  * @usb: power_supply instance for USB power
  * @battery: power_supply instance for battery
  * @mains_online: is AC/DC input connected
  * @usb_online: is USB input connected
  * @charging_enabled: is charging enabled
- * @dentry: for debugfs
  * @pdata: pointer to platform data
  */
 struct smb347_charger {
        struct mutex            lock;
-       struct i2c_client       *client;
+       struct device           *dev;
+       struct regmap           *regmap;
        struct power_supply     mains;
        struct power_supply     usb;
        struct power_supply     battery;
        bool                    mains_online;
        bool                    usb_online;
        bool                    charging_enabled;
-       struct dentry           *dentry;
        const struct smb347_charger_platform_data *pdata;
 };
 
@@ -193,14 +196,6 @@ static const unsigned int ccc_tbl[] = {
        1200000,
 };
 
-/* Convert register value to current using lookup table */
-static int hw_to_current(const unsigned int *tbl, size_t size, unsigned int val)
-{
-       if (val >= size)
-               return -EINVAL;
-       return tbl[val];
-}
-
 /* Convert current to register value using lookup table */
 static int current_to_hw(const unsigned int *tbl, size_t size, unsigned int val)
 {
@@ -212,43 +207,22 @@ static int current_to_hw(const unsigned int *tbl, size_t size, unsigned int val)
        return i > 0 ? i - 1 : -EINVAL;
 }
 
-static int smb347_read(struct smb347_charger *smb, u8 reg)
-{
-       int ret;
-
-       ret = i2c_smbus_read_byte_data(smb->client, reg);
-       if (ret < 0)
-               dev_warn(&smb->client->dev, "failed to read reg 0x%x: %d\n",
-                        reg, ret);
-       return ret;
-}
-
-static int smb347_write(struct smb347_charger *smb, u8 reg, u8 val)
-{
-       int ret;
-
-       ret = i2c_smbus_write_byte_data(smb->client, reg, val);
-       if (ret < 0)
-               dev_warn(&smb->client->dev, "failed to write reg 0x%x: %d\n",
-                        reg, ret);
-       return ret;
-}
-
 /**
- * smb347_update_status - updates the charging status
+ * smb347_update_ps_status - refreshes the power source status
  * @smb: pointer to smb347 charger instance
  *
- * Function checks status of the charging and updates internal state
- * accordingly. Returns %0 if there is no change in status, %1 if the
- * status has changed and negative errno in case of failure.
+ * Function checks whether any power source is connected to the charger and
+ * updates internal state accordingly. If there is a change to previous state
+ * function returns %1, otherwise %0 and negative errno in case of errror.
  */
-static int smb347_update_status(struct smb347_charger *smb)
+static int smb347_update_ps_status(struct smb347_charger *smb)
 {
        bool usb = false;
        bool dc = false;
+       unsigned int val;
        int ret;
 
-       ret = smb347_read(smb, IRQSTAT_E);
+       ret = regmap_read(smb->regmap, IRQSTAT_E, &val);
        if (ret < 0)
                return ret;
 
@@ -257,9 +231,9 @@ static int smb347_update_status(struct smb347_charger *smb)
         * platform data _and_ whether corresponding undervoltage is set.
         */
        if (smb->pdata->use_mains)
-               dc = !(ret & IRQSTAT_E_DCIN_UV_STAT);
+               dc = !(val & IRQSTAT_E_DCIN_UV_STAT);
        if (smb->pdata->use_usb)
-               usb = !(ret & IRQSTAT_E_USBIN_UV_STAT);
+               usb = !(val & IRQSTAT_E_USBIN_UV_STAT);
 
        mutex_lock(&smb->lock);
        ret = smb->mains_online != dc || smb->usb_online != usb;
@@ -271,15 +245,15 @@ static int smb347_update_status(struct smb347_charger *smb)
 }
 
 /*
- * smb347_is_online - returns whether input power source is connected
+ * smb347_is_ps_online - returns whether input power source is connected
  * @smb: pointer to smb347 charger instance
  *
  * Returns %true if input power source is connected. Note that this is
  * dependent on what platform has configured for usable power sources. For
- * example if USB is disabled, this will return %false even if the USB
- * cable is connected.
+ * example if USB is disabled, this will return %false even if the USB cable
+ * is connected.
  */
-static bool smb347_is_online(struct smb347_charger *smb)
+static bool smb347_is_ps_online(struct smb347_charger *smb)
 {
        bool ret;
 
@@ -299,16 +273,17 @@ static bool smb347_is_online(struct smb347_charger *smb)
  */
 static int smb347_charging_status(struct smb347_charger *smb)
 {
+       unsigned int val;
        int ret;
 
-       if (!smb347_is_online(smb))
+       if (!smb347_is_ps_online(smb))
                return 0;
 
-       ret = smb347_read(smb, STAT_C);
+       ret = regmap_read(smb->regmap, STAT_C, &val);
        if (ret < 0)
                return 0;
 
-       return (ret & STAT_C_CHG_MASK) >> STAT_C_CHG_SHIFT;
+       return (val & STAT_C_CHG_MASK) >> STAT_C_CHG_SHIFT;
 }
 
 static int smb347_charging_set(struct smb347_charger *smb, bool enable)
@@ -316,27 +291,17 @@ static int smb347_charging_set(struct smb347_charger *smb, bool enable)
        int ret = 0;
 
        if (smb->pdata->enable_control != SMB347_CHG_ENABLE_SW) {
-               dev_dbg(&smb->client->dev,
-                       "charging enable/disable in SW disabled\n");
+               dev_dbg(smb->dev, "charging enable/disable in SW disabled\n");
                return 0;
        }
 
        mutex_lock(&smb->lock);
        if (smb->charging_enabled != enable) {
-               ret = smb347_read(smb, CMD_A);
-               if (ret < 0)
-                       goto out;
-
-               smb->charging_enabled = enable;
-
-               if (enable)
-                       ret |= CMD_A_CHG_ENABLED;
-               else
-                       ret &= ~CMD_A_CHG_ENABLED;
-
-               ret = smb347_write(smb, CMD_A, ret);
+               ret = regmap_update_bits(smb->regmap, CMD_A, CMD_A_CHG_ENABLED,
+                                        enable ? CMD_A_CHG_ENABLED : 0);
+               if (!ret)
+                       smb->charging_enabled = enable;
        }
-out:
        mutex_unlock(&smb->lock);
        return ret;
 }
@@ -351,7 +316,7 @@ static inline int smb347_charging_disable(struct smb347_charger *smb)
        return smb347_charging_set(smb, false);
 }
 
-static int smb347_update_online(struct smb347_charger *smb)
+static int smb347_start_stop_charging(struct smb347_charger *smb)
 {
        int ret;
 
@@ -360,16 +325,14 @@ static int smb347_update_online(struct smb347_charger *smb)
         * disable or enable the charging. We do it manually because it
         * depends on how the platform has configured the valid inputs.
         */
-       if (smb347_is_online(smb)) {
+       if (smb347_is_ps_online(smb)) {
                ret = smb347_charging_enable(smb);
                if (ret < 0)
-                       dev_err(&smb->client->dev,
-                               "failed to enable charging\n");
+                       dev_err(smb->dev, "failed to enable charging\n");
        } else {
                ret = smb347_charging_disable(smb);
                if (ret < 0)
-                       dev_err(&smb->client->dev,
-                               "failed to disable charging\n");
+                       dev_err(smb->dev, "failed to disable charging\n");
        }
 
        return ret;
@@ -377,112 +340,120 @@ static int smb347_update_online(struct smb347_charger *smb)
 
 static int smb347_set_charge_current(struct smb347_charger *smb)
 {
-       int ret, val;
-
-       ret = smb347_read(smb, CFG_CHARGE_CURRENT);
-       if (ret < 0)
-               return ret;
+       int ret;
 
        if (smb->pdata->max_charge_current) {
-               val = current_to_hw(fcc_tbl, ARRAY_SIZE(fcc_tbl),
+               ret = current_to_hw(fcc_tbl, ARRAY_SIZE(fcc_tbl),
                                    smb->pdata->max_charge_current);
-               if (val < 0)
-                       return val;
+               if (ret < 0)
+                       return ret;
 
-               ret &= ~CFG_CHARGE_CURRENT_FCC_MASK;
-               ret |= val << CFG_CHARGE_CURRENT_FCC_SHIFT;
+               ret = regmap_update_bits(smb->regmap, CFG_CHARGE_CURRENT,
+                                        CFG_CHARGE_CURRENT_FCC_MASK,
+                                        ret << CFG_CHARGE_CURRENT_FCC_SHIFT);
+               if (ret < 0)
+                       return ret;
        }
 
        if (smb->pdata->pre_charge_current) {
-               val = current_to_hw(pcc_tbl, ARRAY_SIZE(pcc_tbl),
+               ret = current_to_hw(pcc_tbl, ARRAY_SIZE(pcc_tbl),
                                    smb->pdata->pre_charge_current);
-               if (val < 0)
-                       return val;
+               if (ret < 0)
+                       return ret;
 
-               ret &= ~CFG_CHARGE_CURRENT_PCC_MASK;
-               ret |= val << CFG_CHARGE_CURRENT_PCC_SHIFT;
+               ret = regmap_update_bits(smb->regmap, CFG_CHARGE_CURRENT,
+                                        CFG_CHARGE_CURRENT_PCC_MASK,
+                                        ret << CFG_CHARGE_CURRENT_PCC_SHIFT);
+               if (ret < 0)
+                       return ret;
        }
 
        if (smb->pdata->termination_current) {
-               val = current_to_hw(tc_tbl, ARRAY_SIZE(tc_tbl),
+               ret = current_to_hw(tc_tbl, ARRAY_SIZE(tc_tbl),
                                    smb->pdata->termination_current);
-               if (val < 0)
-                       return val;
+               if (ret < 0)
+                       return ret;
 
-               ret &= ~CFG_CHARGE_CURRENT_TC_MASK;
-               ret |= val;
+               ret = regmap_update_bits(smb->regmap, CFG_CHARGE_CURRENT,
+                                        CFG_CHARGE_CURRENT_TC_MASK, ret);
+               if (ret < 0)
+                       return ret;
        }
 
-       return smb347_write(smb, CFG_CHARGE_CURRENT, ret);
+       return 0;
 }
 
 static int smb347_set_current_limits(struct smb347_charger *smb)
 {
-       int ret, val;
-
-       ret = smb347_read(smb, CFG_CURRENT_LIMIT);
-       if (ret < 0)
-               return ret;
+       int ret;
 
        if (smb->pdata->mains_current_limit) {
-               val = current_to_hw(icl_tbl, ARRAY_SIZE(icl_tbl),
+               ret = current_to_hw(icl_tbl, ARRAY_SIZE(icl_tbl),
                                    smb->pdata->mains_current_limit);
-               if (val < 0)
-                       return val;
+               if (ret < 0)
+                       return ret;
 
-               ret &= ~CFG_CURRENT_LIMIT_DC_MASK;
-               ret |= val << CFG_CURRENT_LIMIT_DC_SHIFT;
+               ret = regmap_update_bits(smb->regmap, CFG_CURRENT_LIMIT,
+                                        CFG_CURRENT_LIMIT_DC_MASK,
+                                        ret << CFG_CURRENT_LIMIT_DC_SHIFT);
+               if (ret < 0)
+                       return ret;
        }
 
        if (smb->pdata->usb_hc_current_limit) {
-               val = current_to_hw(icl_tbl, ARRAY_SIZE(icl_tbl),
+               ret = current_to_hw(icl_tbl, ARRAY_SIZE(icl_tbl),
                                    smb->pdata->usb_hc_current_limit);
-               if (val < 0)
-                       return val;
+               if (ret < 0)
+                       return ret;
 
-               ret &= ~CFG_CURRENT_LIMIT_USB_MASK;
-               ret |= val;
+               ret = regmap_update_bits(smb->regmap, CFG_CURRENT_LIMIT,
+                                        CFG_CURRENT_LIMIT_USB_MASK, ret);
+               if (ret < 0)
+                       return ret;
        }
 
-       return smb347_write(smb, CFG_CURRENT_LIMIT, ret);
+       return 0;
 }
 
 static int smb347_set_voltage_limits(struct smb347_charger *smb)
 {
-       int ret, val;
-
-       ret = smb347_read(smb, CFG_FLOAT_VOLTAGE);
-       if (ret < 0)
-               return ret;
+       int ret;
 
        if (smb->pdata->pre_to_fast_voltage) {
-               val = smb->pdata->pre_to_fast_voltage;
+               ret = smb->pdata->pre_to_fast_voltage;
 
                /* uV */
-               val = clamp_val(val, 2400000, 3000000) - 2400000;
-               val /= 200000;
+               ret = clamp_val(ret, 2400000, 3000000) - 2400000;
+               ret /= 200000;
 
-               ret &= ~CFG_FLOAT_VOLTAGE_THRESHOLD_MASK;
-               ret |= val << CFG_FLOAT_VOLTAGE_THRESHOLD_SHIFT;
+               ret = regmap_update_bits(smb->regmap, CFG_FLOAT_VOLTAGE,
+                               CFG_FLOAT_VOLTAGE_THRESHOLD_MASK,
+                               ret << CFG_FLOAT_VOLTAGE_THRESHOLD_SHIFT);
+               if (ret < 0)
+                       return ret;
        }
 
        if (smb->pdata->max_charge_voltage) {
-               val = smb->pdata->max_charge_voltage;
+               ret = smb->pdata->max_charge_voltage;
 
                /* uV */
-               val = clamp_val(val, 3500000, 4500000) - 3500000;
-               val /= 20000;
+               ret = clamp_val(ret, 3500000, 4500000) - 3500000;
+               ret /= 20000;
 
-               ret |= val;
+               ret = regmap_update_bits(smb->regmap, CFG_FLOAT_VOLTAGE,
+                                        CFG_FLOAT_VOLTAGE_FLOAT_MASK, ret);
+               if (ret < 0)
+                       return ret;
        }
 
-       return smb347_write(smb, CFG_FLOAT_VOLTAGE, ret);
+       return 0;
 }
 
 static int smb347_set_temp_limits(struct smb347_charger *smb)
 {
        bool enable_therm_monitor = false;
-       int ret, val;
+       int ret = 0;
+       int val;
 
        if (smb->pdata->chip_temp_threshold) {
                val = smb->pdata->chip_temp_threshold;
@@ -491,22 +462,13 @@ static int smb347_set_temp_limits(struct smb347_charger *smb)
                val = clamp_val(val, 100, 130) - 100;
                val /= 10;
 
-               ret = smb347_read(smb, CFG_OTG);
-               if (ret < 0)
-                       return ret;
-
-               ret &= ~CFG_OTG_TEMP_THRESHOLD_MASK;
-               ret |= val << CFG_OTG_TEMP_THRESHOLD_SHIFT;
-
-               ret = smb347_write(smb, CFG_OTG, ret);
+               ret = regmap_update_bits(smb->regmap, CFG_OTG,
+                                        CFG_OTG_TEMP_THRESHOLD_MASK,
+                                        val << CFG_OTG_TEMP_THRESHOLD_SHIFT);
                if (ret < 0)
                        return ret;
        }
 
-       ret = smb347_read(smb, CFG_TEMP_LIMIT);
-       if (ret < 0)
-               return ret;
-
        if (smb->pdata->soft_cold_temp_limit != SMB347_TEMP_USE_DEFAULT) {
                val = smb->pdata->soft_cold_temp_limit;
 
@@ -515,8 +477,11 @@ static int smb347_set_temp_limits(struct smb347_charger *smb)
                /* this goes from higher to lower so invert the value */
                val = ~val & 0x3;
 
-               ret &= ~CFG_TEMP_LIMIT_SOFT_COLD_MASK;
-               ret |= val << CFG_TEMP_LIMIT_SOFT_COLD_SHIFT;
+               ret = regmap_update_bits(smb->regmap, CFG_TEMP_LIMIT,
+                                        CFG_TEMP_LIMIT_SOFT_COLD_MASK,
+                                        val << CFG_TEMP_LIMIT_SOFT_COLD_SHIFT);
+               if (ret < 0)
+                       return ret;
 
                enable_therm_monitor = true;
        }
@@ -527,8 +492,11 @@ static int smb347_set_temp_limits(struct smb347_charger *smb)
                val = clamp_val(val, 40, 55) - 40;
                val /= 5;
 
-               ret &= ~CFG_TEMP_LIMIT_SOFT_HOT_MASK;
-               ret |= val << CFG_TEMP_LIMIT_SOFT_HOT_SHIFT;
+               ret = regmap_update_bits(smb->regmap, CFG_TEMP_LIMIT,
+                                        CFG_TEMP_LIMIT_SOFT_HOT_MASK,
+                                        val << CFG_TEMP_LIMIT_SOFT_HOT_SHIFT);
+               if (ret < 0)
+                       return ret;
 
                enable_therm_monitor = true;
        }
@@ -541,8 +509,11 @@ static int smb347_set_temp_limits(struct smb347_charger *smb)
                /* this goes from higher to lower so invert the value */
                val = ~val & 0x3;
 
-               ret &= ~CFG_TEMP_LIMIT_HARD_COLD_MASK;
-               ret |= val << CFG_TEMP_LIMIT_HARD_COLD_SHIFT;
+               ret = regmap_update_bits(smb->regmap, CFG_TEMP_LIMIT,
+                                        CFG_TEMP_LIMIT_HARD_COLD_MASK,
+                                        val << CFG_TEMP_LIMIT_HARD_COLD_SHIFT);
+               if (ret < 0)
+                       return ret;
 
                enable_therm_monitor = true;
        }
@@ -553,16 +524,15 @@ static int smb347_set_temp_limits(struct smb347_charger *smb)
                val = clamp_val(val, 50, 65) - 50;
                val /= 5;
 
-               ret &= ~CFG_TEMP_LIMIT_HARD_HOT_MASK;
-               ret |= val << CFG_TEMP_LIMIT_HARD_HOT_SHIFT;
+               ret = regmap_update_bits(smb->regmap, CFG_TEMP_LIMIT,
+                                        CFG_TEMP_LIMIT_HARD_HOT_MASK,
+                                        val << CFG_TEMP_LIMIT_HARD_HOT_SHIFT);
+               if (ret < 0)
+                       return ret;
 
                enable_therm_monitor = true;
        }
 
-       ret = smb347_write(smb, CFG_TEMP_LIMIT, ret);
-       if (ret < 0)
-               return ret;
-
        /*
         * If any of the temperature limits are set, we also enable the
         * thermistor monitoring.
@@ -574,25 +544,15 @@ static int smb347_set_temp_limits(struct smb347_charger *smb)
         * depending on the configuration.
         */
        if (enable_therm_monitor) {
-               ret = smb347_read(smb, CFG_THERM);
-               if (ret < 0)
-                       return ret;
-
-               ret &= ~CFG_THERM_MONITOR_DISABLED;
-
-               ret = smb347_write(smb, CFG_THERM, ret);
+               ret = regmap_update_bits(smb->regmap, CFG_THERM,
+                                        CFG_THERM_MONITOR_DISABLED, 0);
                if (ret < 0)
                        return ret;
        }
 
        if (smb->pdata->suspend_on_hard_temp_limit) {
-               ret = smb347_read(smb, CFG_SYSOK);
-               if (ret < 0)
-                       return ret;
-
-               ret &= ~CFG_SYSOK_SUSPEND_HARD_LIMIT_DISABLED;
-
-               ret = smb347_write(smb, CFG_SYSOK, ret);
+               ret = regmap_update_bits(smb->regmap, CFG_SYSOK,
+                                CFG_SYSOK_SUSPEND_HARD_LIMIT_DISABLED, 0);
                if (ret < 0)
                        return ret;
        }
@@ -601,17 +561,15 @@ static int smb347_set_temp_limits(struct smb347_charger *smb)
            SMB347_SOFT_TEMP_COMPENSATE_DEFAULT) {
                val = smb->pdata->soft_temp_limit_compensation & 0x3;
 
-               ret = smb347_read(smb, CFG_THERM);
+               ret = regmap_update_bits(smb->regmap, CFG_THERM,
+                                CFG_THERM_SOFT_HOT_COMPENSATION_MASK,
+                                val << CFG_THERM_SOFT_HOT_COMPENSATION_SHIFT);
                if (ret < 0)
                        return ret;
 
-               ret &= ~CFG_THERM_SOFT_HOT_COMPENSATION_MASK;
-               ret |= val << CFG_THERM_SOFT_HOT_COMPENSATION_SHIFT;
-
-               ret &= ~CFG_THERM_SOFT_COLD_COMPENSATION_MASK;
-               ret |= val << CFG_THERM_SOFT_COLD_COMPENSATION_SHIFT;
-
-               ret = smb347_write(smb, CFG_THERM, ret);
+               ret = regmap_update_bits(smb->regmap, CFG_THERM,
+                                CFG_THERM_SOFT_COLD_COMPENSATION_MASK,
+                                val << CFG_THERM_SOFT_COLD_COMPENSATION_SHIFT);
                if (ret < 0)
                        return ret;
        }
@@ -622,14 +580,9 @@ static int smb347_set_temp_limits(struct smb347_charger *smb)
                if (val < 0)
                        return val;
 
-               ret = smb347_read(smb, CFG_OTG);
-               if (ret < 0)
-                       return ret;
-
-               ret &= ~CFG_OTG_CC_COMPENSATION_MASK;
-               ret |= (val & 0x3) << CFG_OTG_CC_COMPENSATION_SHIFT;
-
-               ret = smb347_write(smb, CFG_OTG, ret);
+               ret = regmap_update_bits(smb->regmap, CFG_OTG,
+                               CFG_OTG_CC_COMPENSATION_MASK,
+                               (val & 0x3) << CFG_OTG_CC_COMPENSATION_SHIFT);
                if (ret < 0)
                        return ret;
        }
@@ -648,22 +601,13 @@ static int smb347_set_temp_limits(struct smb347_charger *smb)
  */
 static int smb347_set_writable(struct smb347_charger *smb, bool writable)
 {
-       int ret;
-
-       ret = smb347_read(smb, CMD_A);
-       if (ret < 0)
-               return ret;
-
-       if (writable)
-               ret |= CMD_A_ALLOW_WRITE;
-       else
-               ret &= ~CMD_A_ALLOW_WRITE;
-
-       return smb347_write(smb, CMD_A, ret);
+       return regmap_update_bits(smb->regmap, CMD_A, CMD_A_ALLOW_WRITE,
+                                 writable ? CMD_A_ALLOW_WRITE : 0);
 }
 
 static int smb347_hw_init(struct smb347_charger *smb)
 {
+       unsigned int val;
        int ret;
 
        ret = smb347_set_writable(smb, true);
@@ -692,34 +636,19 @@ static int smb347_hw_init(struct smb347_charger *smb)
 
        /* If USB charging is disabled we put the USB in suspend mode */
        if (!smb->pdata->use_usb) {
-               ret = smb347_read(smb, CMD_A);
-               if (ret < 0)
-                       goto fail;
-
-               ret |= CMD_A_SUSPEND_ENABLED;
-
-               ret = smb347_write(smb, CMD_A, ret);
+               ret = regmap_update_bits(smb->regmap, CMD_A,
+                                        CMD_A_SUSPEND_ENABLED,
+                                        CMD_A_SUSPEND_ENABLED);
                if (ret < 0)
                        goto fail;
        }
 
-       ret = smb347_read(smb, CFG_OTHER);
-       if (ret < 0)
-               goto fail;
-
        /*
         * If configured by platform data, we enable hardware Auto-OTG
         * support for driving VBUS. Otherwise we disable it.
         */
-       ret &= ~CFG_OTHER_RID_MASK;
-       if (smb->pdata->use_usb_otg)
-               ret |= CFG_OTHER_RID_ENABLED_AUTO_OTG;
-
-       ret = smb347_write(smb, CFG_OTHER, ret);
-       if (ret < 0)
-               goto fail;
-
-       ret = smb347_read(smb, CFG_PIN);
+       ret = regmap_update_bits(smb->regmap, CFG_OTHER, CFG_OTHER_RID_MASK,
+               smb->pdata->use_usb_otg ? CFG_OTHER_RID_ENABLED_AUTO_OTG : 0);
        if (ret < 0)
                goto fail;
 
@@ -728,32 +657,33 @@ static int smb347_hw_init(struct smb347_charger *smb)
         * command register unless pin control is specified in the platform
         * data.
         */
-       ret &= ~CFG_PIN_EN_CTRL_MASK;
-
        switch (smb->pdata->enable_control) {
-       case SMB347_CHG_ENABLE_SW:
-               /* Do nothing, 0 means i2c control */
-               break;
        case SMB347_CHG_ENABLE_PIN_ACTIVE_LOW:
-               ret |= CFG_PIN_EN_CTRL_ACTIVE_LOW;
+               val = CFG_PIN_EN_CTRL_ACTIVE_LOW;
                break;
        case SMB347_CHG_ENABLE_PIN_ACTIVE_HIGH:
-               ret |= CFG_PIN_EN_CTRL_ACTIVE_HIGH;
+               val = CFG_PIN_EN_CTRL_ACTIVE_HIGH;
+               break;
+       default:
+               val = 0;
                break;
        }
 
-       /* Disable Automatic Power Source Detection (APSD) interrupt. */
-       ret &= ~CFG_PIN_EN_APSD_IRQ;
+       ret = regmap_update_bits(smb->regmap, CFG_PIN, CFG_PIN_EN_CTRL_MASK,
+                                val);
+       if (ret < 0)
+               goto fail;
 
-       ret = smb347_write(smb, CFG_PIN, ret);
+       /* Disable Automatic Power Source Detection (APSD) interrupt. */
+       ret = regmap_update_bits(smb->regmap, CFG_PIN, CFG_PIN_EN_APSD_IRQ, 0);
        if (ret < 0)
                goto fail;
 
-       ret = smb347_update_status(smb);
+       ret = smb347_update_ps_status(smb);
        if (ret < 0)
                goto fail;
 
-       ret = smb347_update_online(smb);
+       ret = smb347_start_stop_charging(smb);
 
 fail:
        smb347_set_writable(smb, false);
@@ -763,24 +693,25 @@ fail:
 static irqreturn_t smb347_interrupt(int irq, void *data)
 {
        struct smb347_charger *smb = data;
-       int stat_c, irqstat_e, irqstat_c;
-       irqreturn_t ret = IRQ_NONE;
+       unsigned int stat_c, irqstat_e, irqstat_c;
+       bool handled = false;
+       int ret;
 
-       stat_c = smb347_read(smb, STAT_C);
-       if (stat_c < 0) {
-               dev_warn(&smb->client->dev, "reading STAT_C failed\n");
+       ret = regmap_read(smb->regmap, STAT_C, &stat_c);
+       if (ret < 0) {
+               dev_warn(smb->dev, "reading STAT_C failed\n");
                return IRQ_NONE;
        }
 
-       irqstat_c = smb347_read(smb, IRQSTAT_C);
-       if (irqstat_c < 0) {
-               dev_warn(&smb->client->dev, "reading IRQSTAT_C failed\n");
+       ret = regmap_read(smb->regmap, IRQSTAT_C, &irqstat_c);
+       if (ret < 0) {
+               dev_warn(smb->dev, "reading IRQSTAT_C failed\n");
                return IRQ_NONE;
        }
 
-       irqstat_e = smb347_read(smb, IRQSTAT_E);
-       if (irqstat_e < 0) {
-               dev_warn(&smb->client->dev, "reading IRQSTAT_E failed\n");
+       ret = regmap_read(smb->regmap, IRQSTAT_E, &irqstat_e);
+       if (ret < 0) {
+               dev_warn(smb->dev, "reading IRQSTAT_E failed\n");
                return IRQ_NONE;
        }
 
@@ -789,13 +720,11 @@ static irqreturn_t smb347_interrupt(int irq, void *data)
         * disable charging.
         */
        if (stat_c & STAT_C_CHARGER_ERROR) {
-               dev_err(&smb->client->dev,
-                       "error in charger, disabling charging\n");
+               dev_err(smb->dev, "error in charger, disabling charging\n");
 
                smb347_charging_disable(smb);
                power_supply_changed(&smb->battery);
-
-               ret = IRQ_HANDLED;
+               handled = true;
        }
 
        /*
@@ -806,7 +735,7 @@ static irqreturn_t smb347_interrupt(int irq, void *data)
        if (irqstat_c & (IRQSTAT_C_TERMINATION_IRQ | IRQSTAT_C_TAPER_IRQ)) {
                if (irqstat_c & IRQSTAT_C_TERMINATION_STAT)
                        power_supply_changed(&smb->battery);
-               ret = IRQ_HANDLED;
+               handled = true;
        }
 
        /*
@@ -814,15 +743,17 @@ static irqreturn_t smb347_interrupt(int irq, void *data)
         * was connected or disconnected.
         */
        if (irqstat_e & (IRQSTAT_E_USBIN_UV_IRQ | IRQSTAT_E_DCIN_UV_IRQ)) {
-               if (smb347_update_status(smb) > 0) {
-                       smb347_update_online(smb);
-                       power_supply_changed(&smb->mains);
-                       power_supply_changed(&smb->usb);
+               if (smb347_update_ps_status(smb) > 0) {
+                       smb347_start_stop_charging(smb);
+                       if (smb->pdata->use_mains)
+                               power_supply_changed(&smb->mains);
+                       if (smb->pdata->use_usb)
+                               power_supply_changed(&smb->usb);
                }
-               ret = IRQ_HANDLED;
+               handled = true;
        }
 
-       return ret;
+       return handled ? IRQ_HANDLED : IRQ_NONE;
 }
 
 static int smb347_irq_set(struct smb347_charger *smb, bool enable)
@@ -839,41 +770,18 @@ static int smb347_irq_set(struct smb347_charger *smb, bool enable)
         *      - termination current reached
         *      - charger error
         */
-       if (enable) {
-               ret = smb347_write(smb, CFG_FAULT_IRQ, CFG_FAULT_IRQ_DCIN_UV);
-               if (ret < 0)
-                       goto fail;
-
-               ret = smb347_write(smb, CFG_STATUS_IRQ,
-                                  CFG_STATUS_IRQ_TERMINATION_OR_TAPER);
-               if (ret < 0)
-                       goto fail;
-
-               ret = smb347_read(smb, CFG_PIN);
-               if (ret < 0)
-                       goto fail;
-
-               ret |= CFG_PIN_EN_CHARGER_ERROR;
-
-               ret = smb347_write(smb, CFG_PIN, ret);
-       } else {
-               ret = smb347_write(smb, CFG_FAULT_IRQ, 0);
-               if (ret < 0)
-                       goto fail;
-
-               ret = smb347_write(smb, CFG_STATUS_IRQ, 0);
-               if (ret < 0)
-                       goto fail;
-
-               ret = smb347_read(smb, CFG_PIN);
-               if (ret < 0)
-                       goto fail;
-
-               ret &= ~CFG_PIN_EN_CHARGER_ERROR;
+       ret = regmap_update_bits(smb->regmap, CFG_FAULT_IRQ, 0xff,
+                                enable ? CFG_FAULT_IRQ_DCIN_UV : 0);
+       if (ret < 0)
+               goto fail;
 
-               ret = smb347_write(smb, CFG_PIN, ret);
-       }
+       ret = regmap_update_bits(smb->regmap, CFG_STATUS_IRQ, 0xff,
+                       enable ? CFG_STATUS_IRQ_TERMINATION_OR_TAPER : 0);
+       if (ret < 0)
+               goto fail;
 
+       ret = regmap_update_bits(smb->regmap, CFG_PIN, CFG_PIN_EN_CHARGER_ERROR,
+                                enable ? CFG_PIN_EN_CHARGER_ERROR : 0);
 fail:
        smb347_set_writable(smb, false);
        return ret;
@@ -889,18 +797,18 @@ static inline int smb347_irq_disable(struct smb347_charger *smb)
        return smb347_irq_set(smb, false);
 }
 
-static int smb347_irq_init(struct smb347_charger *smb)
+static int smb347_irq_init(struct smb347_charger *smb,
+                          struct i2c_client *client)
 {
        const struct smb347_charger_platform_data *pdata = smb->pdata;
        int ret, irq = gpio_to_irq(pdata->irq_gpio);
 
-       ret = gpio_request_one(pdata->irq_gpio, GPIOF_IN, smb->client->name);
+       ret = gpio_request_one(pdata->irq_gpio, GPIOF_IN, client->name);
        if (ret < 0)
                goto fail;
 
        ret = request_threaded_irq(irq, NULL, smb347_interrupt,
-                                  IRQF_TRIGGER_FALLING, smb->client->name,
-                                  smb);
+                                  IRQF_TRIGGER_FALLING, client->name, smb);
        if (ret < 0)
                goto fail_gpio;
 
@@ -912,23 +820,14 @@ static int smb347_irq_init(struct smb347_charger *smb)
         * Configure the STAT output to be suitable for interrupts: disable
         * all other output (except interrupts) and make it active low.
         */
-       ret = smb347_read(smb, CFG_STAT);
-       if (ret < 0)
-               goto fail_readonly;
-
-       ret &= ~CFG_STAT_ACTIVE_HIGH;
-       ret |= CFG_STAT_DISABLED;
-
-       ret = smb347_write(smb, CFG_STAT, ret);
-       if (ret < 0)
-               goto fail_readonly;
-
-       ret = smb347_irq_enable(smb);
+       ret = regmap_update_bits(smb->regmap, CFG_STAT,
+                                CFG_STAT_ACTIVE_HIGH | CFG_STAT_DISABLED,
+                                CFG_STAT_DISABLED);
        if (ret < 0)
                goto fail_readonly;
 
        smb347_set_writable(smb, false);
-       smb->client->irq = irq;
+       client->irq = irq;
        return 0;
 
 fail_readonly:
@@ -938,7 +837,7 @@ fail_irq:
 fail_gpio:
        gpio_free(pdata->irq_gpio);
 fail:
-       smb->client->irq = 0;
+       client->irq = 0;
        return ret;
 }
 
@@ -987,13 +886,13 @@ static int smb347_battery_get_property(struct power_supply *psy,
        const struct smb347_charger_platform_data *pdata = smb->pdata;
        int ret;
 
-       ret = smb347_update_status(smb);
+       ret = smb347_update_ps_status(smb);
        if (ret < 0)
                return ret;
 
        switch (prop) {
        case POWER_SUPPLY_PROP_STATUS:
-               if (!smb347_is_online(smb)) {
+               if (!smb347_is_ps_online(smb)) {
                        val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
                        break;
                }
@@ -1004,7 +903,7 @@ static int smb347_battery_get_property(struct power_supply *psy,
                break;
 
        case POWER_SUPPLY_PROP_CHARGE_TYPE:
-               if (!smb347_is_online(smb))
+               if (!smb347_is_ps_online(smb))
                        return -ENODATA;
 
                /*
@@ -1036,44 +935,6 @@ static int smb347_battery_get_property(struct power_supply *psy,
                val->intval = pdata->battery_info.voltage_max_design;
                break;
 
-       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-               if (!smb347_is_online(smb))
-                       return -ENODATA;
-               ret = smb347_read(smb, STAT_A);
-               if (ret < 0)
-                       return ret;
-
-               ret &= STAT_A_FLOAT_VOLTAGE_MASK;
-               if (ret > 0x3d)
-                       ret = 0x3d;
-
-               val->intval = 3500000 + ret * 20000;
-               break;
-
-       case POWER_SUPPLY_PROP_CURRENT_NOW:
-               if (!smb347_is_online(smb))
-                       return -ENODATA;
-
-               ret = smb347_read(smb, STAT_B);
-               if (ret < 0)
-                       return ret;
-
-               /*
-                * The current value is composition of FCC and PCC values
-                * and we can detect which table to use from bit 5.
-                */
-               if (ret & 0x20) {
-                       val->intval = hw_to_current(fcc_tbl,
-                                                   ARRAY_SIZE(fcc_tbl),
-                                                   ret & 7);
-               } else {
-                       ret >>= 3;
-                       val->intval = hw_to_current(pcc_tbl,
-                                                   ARRAY_SIZE(pcc_tbl),
-                                                   ret & 7);
-               }
-               break;
-
        case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
                val->intval = pdata->battery_info.charge_full_design;
                break;
@@ -1095,64 +956,58 @@ static enum power_supply_property smb347_battery_properties[] = {
        POWER_SUPPLY_PROP_TECHNOLOGY,
        POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
        POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
-       POWER_SUPPLY_PROP_VOLTAGE_NOW,
-       POWER_SUPPLY_PROP_CURRENT_NOW,
        POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
        POWER_SUPPLY_PROP_MODEL_NAME,
 };
 
-static int smb347_debugfs_show(struct seq_file *s, void *data)
+static bool smb347_volatile_reg(struct device *dev, unsigned int reg)
 {
-       struct smb347_charger *smb = s->private;
-       int ret;
-       u8 reg;
-
-       seq_printf(s, "Control registers:\n");
-       seq_printf(s, "==================\n");
-       for (reg = CFG_CHARGE_CURRENT; reg <= CFG_ADDRESS; reg++) {
-               ret = smb347_read(smb, reg);
-               seq_printf(s, "0x%02x:\t0x%02x\n", reg, ret);
-       }
-       seq_printf(s, "\n");
-
-       seq_printf(s, "Command registers:\n");
-       seq_printf(s, "==================\n");
-       ret = smb347_read(smb, CMD_A);
-       seq_printf(s, "0x%02x:\t0x%02x\n", CMD_A, ret);
-       ret = smb347_read(smb, CMD_B);
-       seq_printf(s, "0x%02x:\t0x%02x\n", CMD_B, ret);
-       ret = smb347_read(smb, CMD_C);
-       seq_printf(s, "0x%02x:\t0x%02x\n", CMD_C, ret);
-       seq_printf(s, "\n");
-
-       seq_printf(s, "Interrupt status registers:\n");
-       seq_printf(s, "===========================\n");
-       for (reg = IRQSTAT_A; reg <= IRQSTAT_F; reg++) {
-               ret = smb347_read(smb, reg);
-               seq_printf(s, "0x%02x:\t0x%02x\n", reg, ret);
-       }
-       seq_printf(s, "\n");
-
-       seq_printf(s, "Status registers:\n");
-       seq_printf(s, "=================\n");
-       for (reg = STAT_A; reg <= STAT_E; reg++) {
-               ret = smb347_read(smb, reg);
-               seq_printf(s, "0x%02x:\t0x%02x\n", reg, ret);
+       switch (reg) {
+       case IRQSTAT_A:
+       case IRQSTAT_C:
+       case IRQSTAT_E:
+       case IRQSTAT_F:
+       case STAT_A:
+       case STAT_B:
+       case STAT_C:
+       case STAT_E:
+               return true;
        }
 
-       return 0;
+       return false;
 }
 
-static int smb347_debugfs_open(struct inode *inode, struct file *file)
+static bool smb347_readable_reg(struct device *dev, unsigned int reg)
 {
-       return single_open(file, smb347_debugfs_show, inode->i_private);
+       switch (reg) {
+       case CFG_CHARGE_CURRENT:
+       case CFG_CURRENT_LIMIT:
+       case CFG_FLOAT_VOLTAGE:
+       case CFG_STAT:
+       case CFG_PIN:
+       case CFG_THERM:
+       case CFG_SYSOK:
+       case CFG_OTHER:
+       case CFG_OTG:
+       case CFG_TEMP_LIMIT:
+       case CFG_FAULT_IRQ:
+       case CFG_STATUS_IRQ:
+       case CFG_ADDRESS:
+       case CMD_A:
+       case CMD_B:
+       case CMD_C:
+               return true;
+       }
+
+       return smb347_volatile_reg(dev, reg);
 }
 
-static const struct file_operations smb347_debugfs_fops = {
-       .open           = smb347_debugfs_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
+static const struct regmap_config smb347_regmap = {
+       .reg_bits       = 8,
+       .val_bits       = 8,
+       .max_register   = SMB347_MAX_REGISTER,
+       .volatile_reg   = smb347_volatile_reg,
+       .readable_reg   = smb347_readable_reg,
 };
 
 static int smb347_probe(struct i2c_client *client,
@@ -1178,28 +1033,45 @@ static int smb347_probe(struct i2c_client *client,
        i2c_set_clientdata(client, smb);
 
        mutex_init(&smb->lock);
-       smb->client = client;
+       smb->dev = &client->dev;
        smb->pdata = pdata;
 
+       smb->regmap = devm_regmap_init_i2c(client, &smb347_regmap);
+       if (IS_ERR(smb->regmap))
+               return PTR_ERR(smb->regmap);
+
        ret = smb347_hw_init(smb);
        if (ret < 0)
                return ret;
 
-       smb->mains.name = "smb347-mains";
-       smb->mains.type = POWER_SUPPLY_TYPE_MAINS;
-       smb->mains.get_property = smb347_mains_get_property;
-       smb->mains.properties = smb347_mains_properties;
-       smb->mains.num_properties = ARRAY_SIZE(smb347_mains_properties);
-       smb->mains.supplied_to = battery;
-       smb->mains.num_supplicants = ARRAY_SIZE(battery);
-
-       smb->usb.name = "smb347-usb";
-       smb->usb.type = POWER_SUPPLY_TYPE_USB;
-       smb->usb.get_property = smb347_usb_get_property;
-       smb->usb.properties = smb347_usb_properties;
-       smb->usb.num_properties = ARRAY_SIZE(smb347_usb_properties);
-       smb->usb.supplied_to = battery;
-       smb->usb.num_supplicants = ARRAY_SIZE(battery);
+       if (smb->pdata->use_mains) {
+               smb->mains.name = "smb347-mains";
+               smb->mains.type = POWER_SUPPLY_TYPE_MAINS;
+               smb->mains.get_property = smb347_mains_get_property;
+               smb->mains.properties = smb347_mains_properties;
+               smb->mains.num_properties = ARRAY_SIZE(smb347_mains_properties);
+               smb->mains.supplied_to = battery;
+               smb->mains.num_supplicants = ARRAY_SIZE(battery);
+               ret = power_supply_register(dev, &smb->mains);
+               if (ret < 0)
+                       return ret;
+       }
+
+       if (smb->pdata->use_usb) {
+               smb->usb.name = "smb347-usb";
+               smb->usb.type = POWER_SUPPLY_TYPE_USB;
+               smb->usb.get_property = smb347_usb_get_property;
+               smb->usb.properties = smb347_usb_properties;
+               smb->usb.num_properties = ARRAY_SIZE(smb347_usb_properties);
+               smb->usb.supplied_to = battery;
+               smb->usb.num_supplicants = ARRAY_SIZE(battery);
+               ret = power_supply_register(dev, &smb->usb);
+               if (ret < 0) {
+                       if (smb->pdata->use_mains)
+                               power_supply_unregister(&smb->mains);
+                       return ret;
+               }
+       }
 
        smb->battery.name = "smb347-battery";
        smb->battery.type = POWER_SUPPLY_TYPE_BATTERY;
@@ -1207,20 +1079,13 @@ static int smb347_probe(struct i2c_client *client,
        smb->battery.properties = smb347_battery_properties;
        smb->battery.num_properties = ARRAY_SIZE(smb347_battery_properties);
 
-       ret = power_supply_register(dev, &smb->mains);
-       if (ret < 0)
-               return ret;
-
-       ret = power_supply_register(dev, &smb->usb);
-       if (ret < 0) {
-               power_supply_unregister(&smb->mains);
-               return ret;
-       }
 
        ret = power_supply_register(dev, &smb->battery);
        if (ret < 0) {
-               power_supply_unregister(&smb->usb);
-               power_supply_unregister(&smb->mains);
+               if (smb->pdata->use_usb)
+                       power_supply_unregister(&smb->usb);
+               if (smb->pdata->use_mains)
+                       power_supply_unregister(&smb->mains);
                return ret;
        }
 
@@ -1229,15 +1094,15 @@ static int smb347_probe(struct i2c_client *client,
         * interrupt support here.
         */
        if (pdata->irq_gpio >= 0) {
-               ret = smb347_irq_init(smb);
+               ret = smb347_irq_init(smb, client);
                if (ret < 0) {
                        dev_warn(dev, "failed to initialize IRQ: %d\n", ret);
                        dev_warn(dev, "disabling IRQ support\n");
+               } else {
+                       smb347_irq_enable(smb);
                }
        }
 
-       smb->dentry = debugfs_create_file("smb347-regs", S_IRUSR, NULL, smb,
-                                         &smb347_debugfs_fops);
        return 0;
 }
 
@@ -1245,9 +1110,6 @@ static int smb347_remove(struct i2c_client *client)
 {
        struct smb347_charger *smb = i2c_get_clientdata(client);
 
-       if (!IS_ERR_OR_NULL(smb->dentry))
-               debugfs_remove(smb->dentry);
-
        if (client->irq) {
                smb347_irq_disable(smb);
                free_irq(client->irq, smb);
@@ -1255,8 +1117,10 @@ static int smb347_remove(struct i2c_client *client)
        }
 
        power_supply_unregister(&smb->battery);
-       power_supply_unregister(&smb->usb);
-       power_supply_unregister(&smb->mains);
+       if (smb->pdata->use_usb)
+               power_supply_unregister(&smb->usb);
+       if (smb->pdata->use_mains)
+               power_supply_unregister(&smb->mains);
        return 0;
 }
 
@@ -1275,17 +1139,7 @@ static struct i2c_driver smb347_driver = {
        .id_table     = smb347_id,
 };
 
-static int __init smb347_init(void)
-{
-       return i2c_add_driver(&smb347_driver);
-}
-module_init(smb347_init);
-
-static void __exit smb347_exit(void)
-{
-       i2c_del_driver(&smb347_driver);
-}
-module_exit(smb347_exit);
+module_i2c_driver(smb347_driver);
 
 MODULE_AUTHOR("Bruce E. Robertson <bruce.e.robertson@intel.com>");
 MODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com>");
index 987332b71d8de3ddfbfab813800cac880e48e53d..fc1ad9551182602f8d485d5e18a2f56e0b057908 100644 (file)
@@ -565,7 +565,7 @@ static __devinit int wm831x_power_probe(struct platform_device *pdev)
                            goto err_usb;
        }
 
-       irq = platform_get_irq_byname(pdev, "SYSLO");
+       irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "SYSLO"));
        ret = request_threaded_irq(irq, NULL, wm831x_syslo_irq,
                                   IRQF_TRIGGER_RISING, "System power low",
                                   power);
@@ -575,7 +575,7 @@ static __devinit int wm831x_power_probe(struct platform_device *pdev)
                goto err_battery;
        }
 
-       irq = platform_get_irq_byname(pdev, "PWR SRC");
+       irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "PWR SRC"));
        ret = request_threaded_irq(irq, NULL, wm831x_pwr_src_irq,
                                   IRQF_TRIGGER_RISING, "Power source",
                                   power);
@@ -586,7 +586,9 @@ static __devinit int wm831x_power_probe(struct platform_device *pdev)
        }
 
        for (i = 0; i < ARRAY_SIZE(wm831x_bat_irqs); i++) {
-               irq = platform_get_irq_byname(pdev, wm831x_bat_irqs[i]);
+               irq = wm831x_irq(wm831x,
+                                platform_get_irq_byname(pdev,
+                                                        wm831x_bat_irqs[i]));
                ret = request_threaded_irq(irq, NULL, wm831x_bat_irq,
                                           IRQF_TRIGGER_RISING,
                                           wm831x_bat_irqs[i],
@@ -606,10 +608,10 @@ err_bat_irq:
                irq = platform_get_irq_byname(pdev, wm831x_bat_irqs[i]);
                free_irq(irq, power);
        }
-       irq = platform_get_irq_byname(pdev, "PWR SRC");
+       irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "PWR SRC"));
        free_irq(irq, power);
 err_syslo:
-       irq = platform_get_irq_byname(pdev, "SYSLO");
+       irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "SYSLO"));
        free_irq(irq, power);
 err_battery:
        if (power->have_battery)
@@ -626,17 +628,20 @@ err_kmalloc:
 static __devexit int wm831x_power_remove(struct platform_device *pdev)
 {
        struct wm831x_power *wm831x_power = platform_get_drvdata(pdev);
+       struct wm831x *wm831x = wm831x_power->wm831x;
        int irq, i;
 
        for (i = 0; i < ARRAY_SIZE(wm831x_bat_irqs); i++) {
-               irq = platform_get_irq_byname(pdev, wm831x_bat_irqs[i]);
+               irq = wm831x_irq(wm831x, 
+                                platform_get_irq_byname(pdev,
+                                                        wm831x_bat_irqs[i]));
                free_irq(irq, wm831x_power);
        }
 
-       irq = platform_get_irq_byname(pdev, "PWR SRC");
+       irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "PWR SRC"));
        free_irq(irq, wm831x_power);
 
-       irq = platform_get_irq_byname(pdev, "SYSLO");
+       irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "SYSLO"));
        free_irq(irq, wm831x_power);
 
        if (wm831x_power->have_battery)
index bc871923879303f5771a27c83f7c148850682ddd..6194d35ebb9740c0af7f999dcb7b73aec75d91ca 100644 (file)
@@ -22,6 +22,20 @@ config RAPIDIO_ENABLE_RX_TX_PORTS
          ports for Input/Output direction to allow other traffic
          than Maintenance transfers.
 
+config RAPIDIO_DMA_ENGINE
+       bool "DMA Engine support for RapidIO"
+       depends on RAPIDIO
+       select DMADEVICES
+       select DMA_ENGINE
+       help
+         Say Y here if you want to use DMA Engine frameork for RapidIO data
+         transfers to/from target RIO devices. RapidIO uses NREAD and
+         NWRITE (NWRITE_R, SWRITE) requests to transfer data between local
+         memory and memory on remote target device. You need a DMA controller
+         capable to perform data transfers to/from RapidIO.
+
+         If you are unsure about this, say Y here.
+
 config RAPIDIO_DEBUG
        bool "RapidIO subsystem debug messages"
        depends on RAPIDIO
index 3b7b4e2dff7c8a07dd159c85037302c16678b8bd..7b62860f34f805842ab9fbe28ea35a6909f029b3 100644 (file)
@@ -3,3 +3,6 @@
 #
 
 obj-$(CONFIG_RAPIDIO_TSI721)   += tsi721.o
+ifeq ($(CONFIG_RAPIDIO_DMA_ENGINE),y)
+obj-$(CONFIG_RAPIDIO_TSI721)   += tsi721_dma.o
+endif
index 30d2072f480b72947c74401d5522fbb9d697d313..722246cf20ab2ed592a6a5ed5435b0f1320a5a44 100644 (file)
@@ -108,6 +108,7 @@ static int tsi721_maint_dma(struct tsi721_device *priv, u32 sys_size,
                        u16 destid, u8 hopcount, u32 offset, int len,
                        u32 *data, int do_wr)
 {
+       void __iomem *regs = priv->regs + TSI721_DMAC_BASE(priv->mdma.ch_id);
        struct tsi721_dma_desc *bd_ptr;
        u32 rd_count, swr_ptr, ch_stat;
        int i, err = 0;
@@ -116,10 +117,9 @@ static int tsi721_maint_dma(struct tsi721_device *priv, u32 sys_size,
        if (offset > (RIO_MAINT_SPACE_SZ - len) || (len != sizeof(u32)))
                return -EINVAL;
 
-       bd_ptr = priv->bdma[TSI721_DMACH_MAINT].bd_base;
+       bd_ptr = priv->mdma.bd_base;
 
-       rd_count = ioread32(
-                       priv->regs + TSI721_DMAC_DRDCNT(TSI721_DMACH_MAINT));
+       rd_count = ioread32(regs + TSI721_DMAC_DRDCNT);
 
        /* Initialize DMA descriptor */
        bd_ptr[0].type_id = cpu_to_le32((DTYPE2 << 29) | (op << 19) | destid);
@@ -134,19 +134,18 @@ static int tsi721_maint_dma(struct tsi721_device *priv, u32 sys_size,
        mb();
 
        /* Start DMA operation */
-       iowrite32(rd_count + 2,
-               priv->regs + TSI721_DMAC_DWRCNT(TSI721_DMACH_MAINT));
-       ioread32(priv->regs + TSI721_DMAC_DWRCNT(TSI721_DMACH_MAINT));
+       iowrite32(rd_count + 2, regs + TSI721_DMAC_DWRCNT);
+       ioread32(regs + TSI721_DMAC_DWRCNT);
        i = 0;
 
        /* Wait until DMA transfer is finished */
-       while ((ch_stat = ioread32(priv->regs +
-               TSI721_DMAC_STS(TSI721_DMACH_MAINT))) & TSI721_DMAC_STS_RUN) {
+       while ((ch_stat = ioread32(regs + TSI721_DMAC_STS))
+                                                       & TSI721_DMAC_STS_RUN) {
                udelay(1);
                if (++i >= 5000000) {
                        dev_dbg(&priv->pdev->dev,
                                "%s : DMA[%d] read timeout ch_status=%x\n",
-                               __func__, TSI721_DMACH_MAINT, ch_stat);
+                               __func__, priv->mdma.ch_id, ch_stat);
                        if (!do_wr)
                                *data = 0xffffffff;
                        err = -EIO;
@@ -162,13 +161,10 @@ static int tsi721_maint_dma(struct tsi721_device *priv, u32 sys_size,
                        __func__, ch_stat);
                dev_dbg(&priv->pdev->dev, "OP=%d : destid=%x hc=%x off=%x\n",
                        do_wr ? MAINT_WR : MAINT_RD, destid, hopcount, offset);
-               iowrite32(TSI721_DMAC_INT_ALL,
-                       priv->regs + TSI721_DMAC_INT(TSI721_DMACH_MAINT));
-               iowrite32(TSI721_DMAC_CTL_INIT,
-                       priv->regs + TSI721_DMAC_CTL(TSI721_DMACH_MAINT));
+               iowrite32(TSI721_DMAC_INT_ALL, regs + TSI721_DMAC_INT);
+               iowrite32(TSI721_DMAC_CTL_INIT, regs + TSI721_DMAC_CTL);
                udelay(10);
-               iowrite32(0, priv->regs +
-                               TSI721_DMAC_DWRCNT(TSI721_DMACH_MAINT));
+               iowrite32(0, regs + TSI721_DMAC_DWRCNT);
                udelay(1);
                if (!do_wr)
                        *data = 0xffffffff;
@@ -184,8 +180,8 @@ static int tsi721_maint_dma(struct tsi721_device *priv, u32 sys_size,
         * NOTE: Skipping check and clear FIFO entries because we are waiting
         * for transfer to be completed.
         */
-       swr_ptr = ioread32(priv->regs + TSI721_DMAC_DSWP(TSI721_DMACH_MAINT));
-       iowrite32(swr_ptr, priv->regs + TSI721_DMAC_DSRP(TSI721_DMACH_MAINT));
+       swr_ptr = ioread32(regs + TSI721_DMAC_DSWP);
+       iowrite32(swr_ptr, regs + TSI721_DMAC_DSRP);
 err_out:
 
        return err;
@@ -541,6 +537,22 @@ static irqreturn_t tsi721_irqhandler(int irq, void *ptr)
                        tsi721_pw_handler(mport);
        }
 
+#ifdef CONFIG_RAPIDIO_DMA_ENGINE
+       if (dev_int & TSI721_DEV_INT_BDMA_CH) {
+               int ch;
+
+               if (dev_ch_int & TSI721_INT_BDMA_CHAN_M) {
+                       dev_dbg(&priv->pdev->dev,
+                               "IRQ from DMA channel 0x%08x\n", dev_ch_int);
+
+                       for (ch = 0; ch < TSI721_DMA_MAXCH; ch++) {
+                               if (!(dev_ch_int & TSI721_INT_BDMA_CHAN(ch)))
+                                       continue;
+                               tsi721_bdma_handler(&priv->bdma[ch]);
+                       }
+               }
+       }
+#endif
        return IRQ_HANDLED;
 }
 
@@ -553,18 +565,26 @@ static void tsi721_interrupts_init(struct tsi721_device *priv)
                priv->regs + TSI721_SR_CHINT(IDB_QUEUE));
        iowrite32(TSI721_SR_CHINT_IDBQRCV,
                priv->regs + TSI721_SR_CHINTE(IDB_QUEUE));
-       iowrite32(TSI721_INT_SR2PC_CHAN(IDB_QUEUE),
-               priv->regs + TSI721_DEV_CHAN_INTE);
 
        /* Enable SRIO MAC interrupts */
        iowrite32(TSI721_RIO_EM_DEV_INT_EN_INT,
                priv->regs + TSI721_RIO_EM_DEV_INT_EN);
 
+       /* Enable interrupts from channels in use */
+#ifdef CONFIG_RAPIDIO_DMA_ENGINE
+       intr = TSI721_INT_SR2PC_CHAN(IDB_QUEUE) |
+               (TSI721_INT_BDMA_CHAN_M &
+                ~TSI721_INT_BDMA_CHAN(TSI721_DMACH_MAINT));
+#else
+       intr = TSI721_INT_SR2PC_CHAN(IDB_QUEUE);
+#endif
+       iowrite32(intr, priv->regs + TSI721_DEV_CHAN_INTE);
+
        if (priv->flags & TSI721_USING_MSIX)
                intr = TSI721_DEV_INT_SRIO;
        else
                intr = TSI721_DEV_INT_SR2PC_CH | TSI721_DEV_INT_SRIO |
-                       TSI721_DEV_INT_SMSG_CH;
+                       TSI721_DEV_INT_SMSG_CH | TSI721_DEV_INT_BDMA_CH;
 
        iowrite32(intr, priv->regs + TSI721_DEV_INTE);
        ioread32(priv->regs + TSI721_DEV_INTE);
@@ -715,12 +735,29 @@ static int tsi721_enable_msix(struct tsi721_device *priv)
                                        TSI721_MSIX_OMSG_INT(i);
        }
 
+#ifdef CONFIG_RAPIDIO_DMA_ENGINE
+       /*
+        * Initialize MSI-X entries for Block DMA Engine:
+        * this driver supports XXX DMA channels
+        * (one is reserved for SRIO maintenance transactions)
+        */
+       for (i = 0; i < TSI721_DMA_CHNUM; i++) {
+               entries[TSI721_VECT_DMA0_DONE + i].entry =
+                                       TSI721_MSIX_DMACH_DONE(i);
+               entries[TSI721_VECT_DMA0_INT + i].entry =
+                                       TSI721_MSIX_DMACH_INT(i);
+       }
+#endif /* CONFIG_RAPIDIO_DMA_ENGINE */
+
        err = pci_enable_msix(priv->pdev, entries, ARRAY_SIZE(entries));
        if (err) {
                if (err > 0)
                        dev_info(&priv->pdev->dev,
                                 "Only %d MSI-X vectors available, "
                                 "not using MSI-X\n", err);
+               else
+                       dev_err(&priv->pdev->dev,
+                               "Failed to enable MSI-X (err=%d)\n", err);
                return err;
        }
 
@@ -760,6 +797,22 @@ static int tsi721_enable_msix(struct tsi721_device *priv)
                         i, pci_name(priv->pdev));
        }
 
+#ifdef CONFIG_RAPIDIO_DMA_ENGINE
+       for (i = 0; i < TSI721_DMA_CHNUM; i++) {
+               priv->msix[TSI721_VECT_DMA0_DONE + i].vector =
+                               entries[TSI721_VECT_DMA0_DONE + i].vector;
+               snprintf(priv->msix[TSI721_VECT_DMA0_DONE + i].irq_name,
+                        IRQ_DEVICE_NAME_MAX, DRV_NAME "-dmad%d@pci:%s",
+                        i, pci_name(priv->pdev));
+
+               priv->msix[TSI721_VECT_DMA0_INT + i].vector =
+                               entries[TSI721_VECT_DMA0_INT + i].vector;
+               snprintf(priv->msix[TSI721_VECT_DMA0_INT + i].irq_name,
+                        IRQ_DEVICE_NAME_MAX, DRV_NAME "-dmai%d@pci:%s",
+                        i, pci_name(priv->pdev));
+       }
+#endif /* CONFIG_RAPIDIO_DMA_ENGINE */
+
        return 0;
 }
 #endif /* CONFIG_PCI_MSI */
@@ -888,20 +941,34 @@ static void tsi721_doorbell_free(struct tsi721_device *priv)
        priv->idb_base = NULL;
 }
 
-static int tsi721_bdma_ch_init(struct tsi721_device *priv, int chnum)
+/**
+ * tsi721_bdma_maint_init - Initialize maintenance request BDMA channel.
+ * @priv: pointer to tsi721 private data
+ *
+ * Initialize BDMA channel allocated for RapidIO maintenance read/write
+ * request generation
+ * Returns %0 on success or %-ENOMEM on failure.
+ */
+static int tsi721_bdma_maint_init(struct tsi721_device *priv)
 {
        struct tsi721_dma_desc *bd_ptr;
        u64             *sts_ptr;
        dma_addr_t      bd_phys, sts_phys;
        int             sts_size;
-       int             bd_num = priv->bdma[chnum].bd_num;
+       int             bd_num = 2;
+       void __iomem    *regs;
 
-       dev_dbg(&priv->pdev->dev, "Init Block DMA Engine, CH%d\n", chnum);
+       dev_dbg(&priv->pdev->dev,
+               "Init Block DMA Engine for Maintenance requests, CH%d\n",
+               TSI721_DMACH_MAINT);
 
        /*
         * Initialize DMA channel for maintenance requests
         */
 
+       priv->mdma.ch_id = TSI721_DMACH_MAINT;
+       regs = priv->regs + TSI721_DMAC_BASE(TSI721_DMACH_MAINT);
+
        /* Allocate space for DMA descriptors */
        bd_ptr = dma_zalloc_coherent(&priv->pdev->dev,
                                        bd_num * sizeof(struct tsi721_dma_desc),
@@ -909,8 +976,9 @@ static int tsi721_bdma_ch_init(struct tsi721_device *priv, int chnum)
        if (!bd_ptr)
                return -ENOMEM;
 
-       priv->bdma[chnum].bd_phys = bd_phys;
-       priv->bdma[chnum].bd_base = bd_ptr;
+       priv->mdma.bd_num = bd_num;
+       priv->mdma.bd_phys = bd_phys;
+       priv->mdma.bd_base = bd_ptr;
 
        dev_dbg(&priv->pdev->dev, "DMA descriptors @ %p (phys = %llx)\n",
                bd_ptr, (unsigned long long)bd_phys);
@@ -927,13 +995,13 @@ static int tsi721_bdma_ch_init(struct tsi721_device *priv, int chnum)
                dma_free_coherent(&priv->pdev->dev,
                                  bd_num * sizeof(struct tsi721_dma_desc),
                                  bd_ptr, bd_phys);
-               priv->bdma[chnum].bd_base = NULL;
+               priv->mdma.bd_base = NULL;
                return -ENOMEM;
        }
 
-       priv->bdma[chnum].sts_phys = sts_phys;
-       priv->bdma[chnum].sts_base = sts_ptr;
-       priv->bdma[chnum].sts_size = sts_size;
+       priv->mdma.sts_phys = sts_phys;
+       priv->mdma.sts_base = sts_ptr;
+       priv->mdma.sts_size = sts_size;
 
        dev_dbg(&priv->pdev->dev,
                "desc status FIFO @ %p (phys = %llx) size=0x%x\n",
@@ -946,83 +1014,61 @@ static int tsi721_bdma_ch_init(struct tsi721_device *priv, int chnum)
        bd_ptr[bd_num - 1].next_hi = cpu_to_le32((u64)bd_phys >> 32);
 
        /* Setup DMA descriptor pointers */
-       iowrite32(((u64)bd_phys >> 32),
-               priv->regs + TSI721_DMAC_DPTRH(chnum));
+       iowrite32(((u64)bd_phys >> 32), regs + TSI721_DMAC_DPTRH);
        iowrite32(((u64)bd_phys & TSI721_DMAC_DPTRL_MASK),
-               priv->regs + TSI721_DMAC_DPTRL(chnum));
+               regs + TSI721_DMAC_DPTRL);
 
        /* Setup descriptor status FIFO */
-       iowrite32(((u64)sts_phys >> 32),
-               priv->regs + TSI721_DMAC_DSBH(chnum));
+       iowrite32(((u64)sts_phys >> 32), regs + TSI721_DMAC_DSBH);
        iowrite32(((u64)sts_phys & TSI721_DMAC_DSBL_MASK),
-               priv->regs + TSI721_DMAC_DSBL(chnum));
+               regs + TSI721_DMAC_DSBL);
        iowrite32(TSI721_DMAC_DSSZ_SIZE(sts_size),
-               priv->regs + TSI721_DMAC_DSSZ(chnum));
+               regs + TSI721_DMAC_DSSZ);
 
        /* Clear interrupt bits */
-       iowrite32(TSI721_DMAC_INT_ALL,
-               priv->regs + TSI721_DMAC_INT(chnum));
+       iowrite32(TSI721_DMAC_INT_ALL, regs + TSI721_DMAC_INT);
 
-       ioread32(priv->regs + TSI721_DMAC_INT(chnum));
+       ioread32(regs + TSI721_DMAC_INT);
 
        /* Toggle DMA channel initialization */
-       iowrite32(TSI721_DMAC_CTL_INIT, priv->regs + TSI721_DMAC_CTL(chnum));
-       ioread32(priv->regs + TSI721_DMAC_CTL(chnum));
+       iowrite32(TSI721_DMAC_CTL_INIT, regs + TSI721_DMAC_CTL);
+       ioread32(regs + TSI721_DMAC_CTL);
        udelay(10);
 
        return 0;
 }
 
-static int tsi721_bdma_ch_free(struct tsi721_device *priv, int chnum)
+static int tsi721_bdma_maint_free(struct tsi721_device *priv)
 {
        u32 ch_stat;
+       struct tsi721_bdma_maint *mdma = &priv->mdma;
+       void __iomem *regs = priv->regs + TSI721_DMAC_BASE(mdma->ch_id);
 
-       if (priv->bdma[chnum].bd_base == NULL)
+       if (mdma->bd_base == NULL)
                return 0;
 
        /* Check if DMA channel still running */
-       ch_stat = ioread32(priv->regs + TSI721_DMAC_STS(chnum));
+       ch_stat = ioread32(regs + TSI721_DMAC_STS);
        if (ch_stat & TSI721_DMAC_STS_RUN)
                return -EFAULT;
 
        /* Put DMA channel into init state */
-       iowrite32(TSI721_DMAC_CTL_INIT,
-               priv->regs + TSI721_DMAC_CTL(chnum));
+       iowrite32(TSI721_DMAC_CTL_INIT, regs + TSI721_DMAC_CTL);
 
        /* Free space allocated for DMA descriptors */
        dma_free_coherent(&priv->pdev->dev,
-               priv->bdma[chnum].bd_num * sizeof(struct tsi721_dma_desc),
-               priv->bdma[chnum].bd_base, priv->bdma[chnum].bd_phys);
-       priv->bdma[chnum].bd_base = NULL;
+               mdma->bd_num * sizeof(struct tsi721_dma_desc),
+               mdma->bd_base, mdma->bd_phys);
+       mdma->bd_base = NULL;
 
        /* Free space allocated for status FIFO */
        dma_free_coherent(&priv->pdev->dev,
-               priv->bdma[chnum].sts_size * sizeof(struct tsi721_dma_sts),
-               priv->bdma[chnum].sts_base, priv->bdma[chnum].sts_phys);
-       priv->bdma[chnum].sts_base = NULL;
-       return 0;
-}
-
-static int tsi721_bdma_init(struct tsi721_device *priv)
-{
-       /* Initialize BDMA channel allocated for RapidIO maintenance read/write
-        * request generation
-        */
-       priv->bdma[TSI721_DMACH_MAINT].bd_num = 2;
-       if (tsi721_bdma_ch_init(priv, TSI721_DMACH_MAINT)) {
-               dev_err(&priv->pdev->dev, "Unable to initialize maintenance DMA"
-                       " channel %d, aborting\n", TSI721_DMACH_MAINT);
-               return -ENOMEM;
-       }
-
+               mdma->sts_size * sizeof(struct tsi721_dma_sts),
+               mdma->sts_base, mdma->sts_phys);
+       mdma->sts_base = NULL;
        return 0;
 }
 
-static void tsi721_bdma_free(struct tsi721_device *priv)
-{
-       tsi721_bdma_ch_free(priv, TSI721_DMACH_MAINT);
-}
-
 /* Enable Inbound Messaging Interrupts */
 static void
 tsi721_imsg_interrupt_enable(struct tsi721_device *priv, int ch,
@@ -2035,7 +2081,8 @@ static void tsi721_disable_ints(struct tsi721_device *priv)
 
        /* Disable all BDMA Channel interrupts */
        for (ch = 0; ch < TSI721_DMA_MAXCH; ch++)
-               iowrite32(0, priv->regs + TSI721_DMAC_INTE(ch));
+               iowrite32(0,
+                       priv->regs + TSI721_DMAC_BASE(ch) + TSI721_DMAC_INTE);
 
        /* Disable all general BDMA interrupts */
        iowrite32(0, priv->regs + TSI721_BDMA_INTE);
@@ -2104,6 +2151,7 @@ static int __devinit tsi721_setup_mport(struct tsi721_device *priv)
        mport->phy_type = RIO_PHY_SERIAL;
        mport->priv = (void *)priv;
        mport->phys_efptr = 0x100;
+       priv->mport = mport;
 
        INIT_LIST_HEAD(&mport->dbells);
 
@@ -2129,17 +2177,21 @@ static int __devinit tsi721_setup_mport(struct tsi721_device *priv)
        if (!err) {
                tsi721_interrupts_init(priv);
                ops->pwenable = tsi721_pw_enable;
-       } else
+       } else {
                dev_err(&pdev->dev, "Unable to get assigned PCI IRQ "
                        "vector %02X err=0x%x\n", pdev->irq, err);
+               goto err_exit;
+       }
 
+#ifdef CONFIG_RAPIDIO_DMA_ENGINE
+       tsi721_register_dma(priv);
+#endif
        /* Enable SRIO link */
        iowrite32(ioread32(priv->regs + TSI721_DEVCTL) |
                  TSI721_DEVCTL_SRBOOT_CMPL,
                  priv->regs + TSI721_DEVCTL);
 
        rio_register_mport(mport);
-       priv->mport = mport;
 
        if (mport->host_deviceid >= 0)
                iowrite32(RIO_PORT_GEN_HOST | RIO_PORT_GEN_MASTER |
@@ -2149,6 +2201,11 @@ static int __devinit tsi721_setup_mport(struct tsi721_device *priv)
                iowrite32(0, priv->regs + (0x100 + RIO_PORT_GEN_CTL_CSR));
 
        return 0;
+
+err_exit:
+       kfree(mport);
+       kfree(ops);
+       return err;
 }
 
 static int __devinit tsi721_probe(struct pci_dev *pdev,
@@ -2294,7 +2351,7 @@ static int __devinit tsi721_probe(struct pci_dev *pdev,
        tsi721_init_pc2sr_mapping(priv);
        tsi721_init_sr2pc_mapping(priv);
 
-       if (tsi721_bdma_init(priv)) {
+       if (tsi721_bdma_maint_init(priv)) {
                dev_err(&pdev->dev, "BDMA initialization failed, aborting\n");
                err = -ENOMEM;
                goto err_unmap_bars;
@@ -2319,7 +2376,7 @@ static int __devinit tsi721_probe(struct pci_dev *pdev,
 err_free_consistent:
        tsi721_doorbell_free(priv);
 err_free_bdma:
-       tsi721_bdma_free(priv);
+       tsi721_bdma_maint_free(priv);
 err_unmap_bars:
        if (priv->regs)
                iounmap(priv->regs);
index 1c226b31af13fc9e196d221a4e3557f91ae9ae6c..59de9d7be3460a08250bcd197dc858bed0af6dee 100644 (file)
 #define TSI721_DEV_INTE                0x29840
 #define TSI721_DEV_INT         0x29844
 #define TSI721_DEV_INTSET      0x29848
+#define TSI721_DEV_INT_BDMA_CH 0x00002000
+#define TSI721_DEV_INT_BDMA_NCH        0x00001000
 #define TSI721_DEV_INT_SMSG_CH 0x00000800
 #define TSI721_DEV_INT_SMSG_NCH        0x00000400
 #define TSI721_DEV_INT_SR2PC_CH        0x00000200
 #define TSI721_INT_IMSG_CHAN(x)        (1 << (16 + (x)))
 #define TSI721_INT_OMSG_CHAN_M 0x0000ff00
 #define TSI721_INT_OMSG_CHAN(x)        (1 << (8 + (x)))
+#define TSI721_INT_BDMA_CHAN_M 0x000000ff
+#define TSI721_INT_BDMA_CHAN(x)        (1 << (x))
 
 /*
  * PC2SR block registers
  *   x = 0..7
  */
 
-#define TSI721_DMAC_DWRCNT(x)  (0x51000 + (x) * 0x1000)
-#define TSI721_DMAC_DRDCNT(x)  (0x51004 + (x) * 0x1000)
+#define TSI721_DMAC_BASE(x)    (0x51000 + (x) * 0x1000)
 
-#define TSI721_DMAC_CTL(x)     (0x51008 + (x) * 0x1000)
+#define TSI721_DMAC_DWRCNT     0x000
+#define TSI721_DMAC_DRDCNT     0x004
+
+#define TSI721_DMAC_CTL                0x008
 #define TSI721_DMAC_CTL_SUSP   0x00000002
 #define TSI721_DMAC_CTL_INIT   0x00000001
 
-#define TSI721_DMAC_INT(x)     (0x5100c + (x) * 0x1000)
+#define TSI721_DMAC_INT                0x00c
 #define TSI721_DMAC_INT_STFULL 0x00000010
 #define TSI721_DMAC_INT_DONE   0x00000008
 #define TSI721_DMAC_INT_SUSP   0x00000004
 #define TSI721_DMAC_INT_IOFDONE        0x00000001
 #define TSI721_DMAC_INT_ALL    0x0000001f
 
-#define TSI721_DMAC_INTSET(x)  (0x51010 + (x) * 0x1000)
+#define TSI721_DMAC_INTSET     0x010
 
-#define TSI721_DMAC_STS(x)     (0x51014 + (x) * 0x1000)
+#define TSI721_DMAC_STS                0x014
 #define TSI721_DMAC_STS_ABORT  0x00400000
 #define TSI721_DMAC_STS_RUN    0x00200000
 #define TSI721_DMAC_STS_CS     0x001f0000
 
-#define TSI721_DMAC_INTE(x)    (0x51018 + (x) * 0x1000)
+#define TSI721_DMAC_INTE       0x018
 
-#define TSI721_DMAC_DPTRL(x)   (0x51024 + (x) * 0x1000)
+#define TSI721_DMAC_DPTRL      0x024
 #define TSI721_DMAC_DPTRL_MASK 0xffffffe0
 
-#define TSI721_DMAC_DPTRH(x)   (0x51028 + (x) * 0x1000)
+#define TSI721_DMAC_DPTRH      0x028
 
-#define TSI721_DMAC_DSBL(x)    (0x5102c + (x) * 0x1000)
+#define TSI721_DMAC_DSBL       0x02c
 #define TSI721_DMAC_DSBL_MASK  0xffffffc0
 
-#define TSI721_DMAC_DSBH(x)    (0x51030 + (x) * 0x1000)
+#define TSI721_DMAC_DSBH       0x030
 
-#define TSI721_DMAC_DSSZ(x)    (0x51034 + (x) * 0x1000)
+#define TSI721_DMAC_DSSZ       0x034
 #define TSI721_DMAC_DSSZ_SIZE_M        0x0000000f
 #define TSI721_DMAC_DSSZ_SIZE(size)    (__fls(size) - 4)
 
-
-#define TSI721_DMAC_DSRP(x)    (0x51038 + (x) * 0x1000)
+#define TSI721_DMAC_DSRP       0x038
 #define TSI721_DMAC_DSRP_MASK  0x0007ffff
 
-#define TSI721_DMAC_DSWP(x)    (0x5103c + (x) * 0x1000)
+#define TSI721_DMAC_DSWP       0x03c
 #define TSI721_DMAC_DSWP_MASK  0x0007ffff
 
 #define TSI721_BDMA_INTE       0x5f000
@@ -612,6 +617,8 @@ enum dma_rtype {
 #define TSI721_DMACH_MAINT     0       /* DMA channel for maint requests */
 #define TSI721_DMACH_MAINT_NBD 32      /* Number of BDs for maint requests */
 
+#define TSI721_DMACH_DMA       1       /* DMA channel for data transfers */
+
 #define MSG_DMA_ENTRY_INX_TO_SIZE(x)   ((0x10 << (x)) & 0xFFFF0)
 
 enum tsi721_smsg_int_flag {
@@ -626,7 +633,48 @@ enum tsi721_smsg_int_flag {
 
 /* Structures */
 
+#ifdef CONFIG_RAPIDIO_DMA_ENGINE
+
+struct tsi721_tx_desc {
+       struct dma_async_tx_descriptor  txd;
+       struct tsi721_dma_desc          *hw_desc;
+       u16                             destid;
+       /* low 64-bits of 66-bit RIO address */
+       u64                             rio_addr;
+       /* upper 2-bits of 66-bit RIO address */
+       u8                              rio_addr_u;
+       bool                            interrupt;
+       struct list_head                desc_node;
+       struct list_head                tx_list;
+};
+
 struct tsi721_bdma_chan {
+       int             id;
+       void __iomem    *regs;
+       int             bd_num;         /* number of buffer descriptors */
+       void            *bd_base;       /* start of DMA descriptors */
+       dma_addr_t      bd_phys;
+       void            *sts_base;      /* start of DMA BD status FIFO */
+       dma_addr_t      sts_phys;
+       int             sts_size;
+       u32             sts_rdptr;
+       u32             wr_count;
+       u32             wr_count_next;
+
+       struct dma_chan         dchan;
+       struct tsi721_tx_desc   *tx_desc;
+       spinlock_t              lock;
+       struct list_head        active_list;
+       struct list_head        queue;
+       struct list_head        free_list;
+       dma_cookie_t            completed_cookie;
+       struct tasklet_struct   tasklet;
+};
+
+#endif /* CONFIG_RAPIDIO_DMA_ENGINE */
+
+struct tsi721_bdma_maint {
+       int             ch_id;          /* BDMA channel number */
        int             bd_num;         /* number of buffer descriptors */
        void            *bd_base;       /* start of DMA descriptors */
        dma_addr_t      bd_phys;
@@ -721,6 +769,24 @@ enum tsi721_msix_vect {
        TSI721_VECT_IMB1_INT,
        TSI721_VECT_IMB2_INT,
        TSI721_VECT_IMB3_INT,
+#ifdef CONFIG_RAPIDIO_DMA_ENGINE
+       TSI721_VECT_DMA0_DONE,
+       TSI721_VECT_DMA1_DONE,
+       TSI721_VECT_DMA2_DONE,
+       TSI721_VECT_DMA3_DONE,
+       TSI721_VECT_DMA4_DONE,
+       TSI721_VECT_DMA5_DONE,
+       TSI721_VECT_DMA6_DONE,
+       TSI721_VECT_DMA7_DONE,
+       TSI721_VECT_DMA0_INT,
+       TSI721_VECT_DMA1_INT,
+       TSI721_VECT_DMA2_INT,
+       TSI721_VECT_DMA3_INT,
+       TSI721_VECT_DMA4_INT,
+       TSI721_VECT_DMA5_INT,
+       TSI721_VECT_DMA6_INT,
+       TSI721_VECT_DMA7_INT,
+#endif /* CONFIG_RAPIDIO_DMA_ENGINE */
        TSI721_VECT_MAX
 };
 
@@ -754,7 +820,11 @@ struct tsi721_device {
        u32             pw_discard_count;
 
        /* BDMA Engine */
+       struct tsi721_bdma_maint mdma; /* Maintenance rd/wr request channel */
+
+#ifdef CONFIG_RAPIDIO_DMA_ENGINE
        struct tsi721_bdma_chan bdma[TSI721_DMA_CHNUM];
+#endif
 
        /* Inbound Messaging */
        int             imsg_init[TSI721_IMSG_CHNUM];
@@ -765,4 +835,9 @@ struct tsi721_device {
        struct tsi721_omsg_ring omsg_ring[TSI721_OMSG_CHNUM];
 };
 
+#ifdef CONFIG_RAPIDIO_DMA_ENGINE
+extern void tsi721_bdma_handler(struct tsi721_bdma_chan *bdma_chan);
+extern int __devinit tsi721_register_dma(struct tsi721_device *priv);
+#endif
+
 #endif
diff --git a/drivers/rapidio/devices/tsi721_dma.c b/drivers/rapidio/devices/tsi721_dma.c
new file mode 100644 (file)
index 0000000..92e06a5
--- /dev/null
@@ -0,0 +1,823 @@
+/*
+ * DMA Engine support for Tsi721 PCIExpress-to-SRIO bridge
+ *
+ * Copyright 2011 Integrated Device Technology, Inc.
+ * Alexandre Bounine <alexandre.bounine@idt.com>
+ *
+ * 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., 59
+ * Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <linux/io.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/rio.h>
+#include <linux/rio_drv.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/delay.h>
+
+#include "tsi721.h"
+
+static inline struct tsi721_bdma_chan *to_tsi721_chan(struct dma_chan *chan)
+{
+       return container_of(chan, struct tsi721_bdma_chan, dchan);
+}
+
+static inline struct tsi721_device *to_tsi721(struct dma_device *ddev)
+{
+       return container_of(ddev, struct rio_mport, dma)->priv;
+}
+
+static inline
+struct tsi721_tx_desc *to_tsi721_desc(struct dma_async_tx_descriptor *txd)
+{
+       return container_of(txd, struct tsi721_tx_desc, txd);
+}
+
+static inline
+struct tsi721_tx_desc *tsi721_dma_first_active(
+                               struct tsi721_bdma_chan *bdma_chan)
+{
+       return list_first_entry(&bdma_chan->active_list,
+                               struct tsi721_tx_desc, desc_node);
+}
+
+static int tsi721_bdma_ch_init(struct tsi721_bdma_chan *bdma_chan)
+{
+       struct tsi721_dma_desc *bd_ptr;
+       struct device *dev = bdma_chan->dchan.device->dev;
+       u64             *sts_ptr;
+       dma_addr_t      bd_phys;
+       dma_addr_t      sts_phys;
+       int             sts_size;
+       int             bd_num = bdma_chan->bd_num;
+
+       dev_dbg(dev, "Init Block DMA Engine, CH%d\n", bdma_chan->id);
+
+       /* Allocate space for DMA descriptors */
+       bd_ptr = dma_zalloc_coherent(dev,
+                               bd_num * sizeof(struct tsi721_dma_desc),
+                               &bd_phys, GFP_KERNEL);
+       if (!bd_ptr)
+               return -ENOMEM;
+
+       bdma_chan->bd_phys = bd_phys;
+       bdma_chan->bd_base = bd_ptr;
+
+       dev_dbg(dev, "DMA descriptors @ %p (phys = %llx)\n",
+               bd_ptr, (unsigned long long)bd_phys);
+
+       /* Allocate space for descriptor status FIFO */
+       sts_size = (bd_num >= TSI721_DMA_MINSTSSZ) ?
+                                       bd_num : TSI721_DMA_MINSTSSZ;
+       sts_size = roundup_pow_of_two(sts_size);
+       sts_ptr = dma_zalloc_coherent(dev,
+                                    sts_size * sizeof(struct tsi721_dma_sts),
+                                    &sts_phys, GFP_KERNEL);
+       if (!sts_ptr) {
+               /* Free space allocated for DMA descriptors */
+               dma_free_coherent(dev,
+                                 bd_num * sizeof(struct tsi721_dma_desc),
+                                 bd_ptr, bd_phys);
+               bdma_chan->bd_base = NULL;
+               return -ENOMEM;
+       }
+
+       bdma_chan->sts_phys = sts_phys;
+       bdma_chan->sts_base = sts_ptr;
+       bdma_chan->sts_size = sts_size;
+
+       dev_dbg(dev,
+               "desc status FIFO @ %p (phys = %llx) size=0x%x\n",
+               sts_ptr, (unsigned long long)sts_phys, sts_size);
+
+       /* Initialize DMA descriptors ring */
+       bd_ptr[bd_num - 1].type_id = cpu_to_le32(DTYPE3 << 29);
+       bd_ptr[bd_num - 1].next_lo = cpu_to_le32((u64)bd_phys &
+                                                TSI721_DMAC_DPTRL_MASK);
+       bd_ptr[bd_num - 1].next_hi = cpu_to_le32((u64)bd_phys >> 32);
+
+       /* Setup DMA descriptor pointers */
+       iowrite32(((u64)bd_phys >> 32),
+               bdma_chan->regs + TSI721_DMAC_DPTRH);
+       iowrite32(((u64)bd_phys & TSI721_DMAC_DPTRL_MASK),
+               bdma_chan->regs + TSI721_DMAC_DPTRL);
+
+       /* Setup descriptor status FIFO */
+       iowrite32(((u64)sts_phys >> 32),
+               bdma_chan->regs + TSI721_DMAC_DSBH);
+       iowrite32(((u64)sts_phys & TSI721_DMAC_DSBL_MASK),
+               bdma_chan->regs + TSI721_DMAC_DSBL);
+       iowrite32(TSI721_DMAC_DSSZ_SIZE(sts_size),
+               bdma_chan->regs + TSI721_DMAC_DSSZ);
+
+       /* Clear interrupt bits */
+       iowrite32(TSI721_DMAC_INT_ALL,
+               bdma_chan->regs + TSI721_DMAC_INT);
+
+       ioread32(bdma_chan->regs + TSI721_DMAC_INT);
+
+       /* Toggle DMA channel initialization */
+       iowrite32(TSI721_DMAC_CTL_INIT, bdma_chan->regs + TSI721_DMAC_CTL);
+       ioread32(bdma_chan->regs + TSI721_DMAC_CTL);
+       bdma_chan->wr_count = bdma_chan->wr_count_next = 0;
+       bdma_chan->sts_rdptr = 0;
+       udelay(10);
+
+       return 0;
+}
+
+static int tsi721_bdma_ch_free(struct tsi721_bdma_chan *bdma_chan)
+{
+       u32 ch_stat;
+
+       if (bdma_chan->bd_base == NULL)
+               return 0;
+
+       /* Check if DMA channel still running */
+       ch_stat = ioread32(bdma_chan->regs + TSI721_DMAC_STS);
+       if (ch_stat & TSI721_DMAC_STS_RUN)
+               return -EFAULT;
+
+       /* Put DMA channel into init state */
+       iowrite32(TSI721_DMAC_CTL_INIT, bdma_chan->regs + TSI721_DMAC_CTL);
+
+       /* Free space allocated for DMA descriptors */
+       dma_free_coherent(bdma_chan->dchan.device->dev,
+               bdma_chan->bd_num * sizeof(struct tsi721_dma_desc),
+               bdma_chan->bd_base, bdma_chan->bd_phys);
+       bdma_chan->bd_base = NULL;
+
+       /* Free space allocated for status FIFO */
+       dma_free_coherent(bdma_chan->dchan.device->dev,
+               bdma_chan->sts_size * sizeof(struct tsi721_dma_sts),
+               bdma_chan->sts_base, bdma_chan->sts_phys);
+       bdma_chan->sts_base = NULL;
+       return 0;
+}
+
+static void
+tsi721_bdma_interrupt_enable(struct tsi721_bdma_chan *bdma_chan, int enable)
+{
+       if (enable) {
+               /* Clear pending BDMA channel interrupts */
+               iowrite32(TSI721_DMAC_INT_ALL,
+                       bdma_chan->regs + TSI721_DMAC_INT);
+               ioread32(bdma_chan->regs + TSI721_DMAC_INT);
+               /* Enable BDMA channel interrupts */
+               iowrite32(TSI721_DMAC_INT_ALL,
+                       bdma_chan->regs + TSI721_DMAC_INTE);
+       } else {
+               /* Disable BDMA channel interrupts */
+               iowrite32(0, bdma_chan->regs + TSI721_DMAC_INTE);
+               /* Clear pending BDMA channel interrupts */
+               iowrite32(TSI721_DMAC_INT_ALL,
+                       bdma_chan->regs + TSI721_DMAC_INT);
+       }
+
+}
+
+static bool tsi721_dma_is_idle(struct tsi721_bdma_chan *bdma_chan)
+{
+       u32 sts;
+
+       sts = ioread32(bdma_chan->regs + TSI721_DMAC_STS);
+       return ((sts & TSI721_DMAC_STS_RUN) == 0);
+}
+
+void tsi721_bdma_handler(struct tsi721_bdma_chan *bdma_chan)
+{
+       /* Disable BDMA channel interrupts */
+       iowrite32(0, bdma_chan->regs + TSI721_DMAC_INTE);
+
+       tasklet_schedule(&bdma_chan->tasklet);
+}
+
+#ifdef CONFIG_PCI_MSI
+/**
+ * tsi721_omsg_msix - MSI-X interrupt handler for BDMA channels
+ * @irq: Linux interrupt number
+ * @ptr: Pointer to interrupt-specific data (BDMA channel structure)
+ *
+ * Handles BDMA channel interrupts signaled using MSI-X.
+ */
+static irqreturn_t tsi721_bdma_msix(int irq, void *ptr)
+{
+       struct tsi721_bdma_chan *bdma_chan = ptr;
+
+       tsi721_bdma_handler(bdma_chan);
+       return IRQ_HANDLED;
+}
+#endif /* CONFIG_PCI_MSI */
+
+/* Must be called with the spinlock held */
+static void tsi721_start_dma(struct tsi721_bdma_chan *bdma_chan)
+{
+       if (!tsi721_dma_is_idle(bdma_chan)) {
+               dev_err(bdma_chan->dchan.device->dev,
+                       "BUG: Attempt to start non-idle channel\n");
+               return;
+       }
+
+       if (bdma_chan->wr_count == bdma_chan->wr_count_next) {
+               dev_err(bdma_chan->dchan.device->dev,
+                       "BUG: Attempt to start DMA with no BDs ready\n");
+               return;
+       }
+
+       dev_dbg(bdma_chan->dchan.device->dev,
+               "tx_chan: %p, chan: %d, regs: %p\n",
+               bdma_chan, bdma_chan->dchan.chan_id, bdma_chan->regs);
+
+       iowrite32(bdma_chan->wr_count_next,
+               bdma_chan->regs + TSI721_DMAC_DWRCNT);
+       ioread32(bdma_chan->regs + TSI721_DMAC_DWRCNT);
+
+       bdma_chan->wr_count = bdma_chan->wr_count_next;
+}
+
+static void tsi721_desc_put(struct tsi721_bdma_chan *bdma_chan,
+                           struct tsi721_tx_desc *desc)
+{
+       dev_dbg(bdma_chan->dchan.device->dev,
+               "Put desc: %p into free list\n", desc);
+
+       if (desc) {
+               spin_lock_bh(&bdma_chan->lock);
+               list_splice_init(&desc->tx_list, &bdma_chan->free_list);
+               list_add(&desc->desc_node, &bdma_chan->free_list);
+               bdma_chan->wr_count_next = bdma_chan->wr_count;
+               spin_unlock_bh(&bdma_chan->lock);
+       }
+}
+
+static
+struct tsi721_tx_desc *tsi721_desc_get(struct tsi721_bdma_chan *bdma_chan)
+{
+       struct tsi721_tx_desc *tx_desc, *_tx_desc;
+       struct tsi721_tx_desc *ret = NULL;
+       int i;
+
+       spin_lock_bh(&bdma_chan->lock);
+       list_for_each_entry_safe(tx_desc, _tx_desc,
+                                &bdma_chan->free_list, desc_node) {
+               if (async_tx_test_ack(&tx_desc->txd)) {
+                       list_del(&tx_desc->desc_node);
+                       ret = tx_desc;
+                       break;
+               }
+               dev_dbg(bdma_chan->dchan.device->dev,
+                       "desc %p not ACKed\n", tx_desc);
+       }
+
+       i = bdma_chan->wr_count_next % bdma_chan->bd_num;
+       if (i == bdma_chan->bd_num - 1) {
+               i = 0;
+               bdma_chan->wr_count_next++; /* skip link descriptor */
+       }
+
+       bdma_chan->wr_count_next++;
+       tx_desc->txd.phys = bdma_chan->bd_phys +
+                               i * sizeof(struct tsi721_dma_desc);
+       tx_desc->hw_desc = &((struct tsi721_dma_desc *)bdma_chan->bd_base)[i];
+
+       spin_unlock_bh(&bdma_chan->lock);
+
+       return ret;
+}
+
+static int
+tsi721_fill_desc(struct tsi721_bdma_chan *bdma_chan,
+       struct tsi721_tx_desc *desc, struct scatterlist *sg,
+       enum dma_rtype rtype, u32 sys_size)
+{
+       struct tsi721_dma_desc *bd_ptr = desc->hw_desc;
+       u64 rio_addr;
+
+       if (sg_dma_len(sg) > TSI721_DMAD_BCOUNT1 + 1) {
+               dev_err(bdma_chan->dchan.device->dev,
+                       "SG element is too large\n");
+               return -EINVAL;
+       }
+
+       dev_dbg(bdma_chan->dchan.device->dev,
+               "desc: 0x%llx, addr: 0x%llx len: 0x%x\n",
+               (u64)desc->txd.phys, (unsigned long long)sg_dma_address(sg),
+               sg_dma_len(sg));
+
+       dev_dbg(bdma_chan->dchan.device->dev,
+               "bd_ptr = %p did=%d raddr=0x%llx\n",
+               bd_ptr, desc->destid, desc->rio_addr);
+
+       /* Initialize DMA descriptor */
+       bd_ptr->type_id = cpu_to_le32((DTYPE1 << 29) |
+                                       (rtype << 19) | desc->destid);
+       if (desc->interrupt)
+               bd_ptr->type_id |= cpu_to_le32(TSI721_DMAD_IOF);
+       bd_ptr->bcount = cpu_to_le32(((desc->rio_addr & 0x3) << 30) |
+                                       (sys_size << 26) | sg_dma_len(sg));
+       rio_addr = (desc->rio_addr >> 2) |
+                               ((u64)(desc->rio_addr_u & 0x3) << 62);
+       bd_ptr->raddr_lo = cpu_to_le32(rio_addr & 0xffffffff);
+       bd_ptr->raddr_hi = cpu_to_le32(rio_addr >> 32);
+       bd_ptr->t1.bufptr_lo = cpu_to_le32(
+                                       (u64)sg_dma_address(sg) & 0xffffffff);
+       bd_ptr->t1.bufptr_hi = cpu_to_le32((u64)sg_dma_address(sg) >> 32);
+       bd_ptr->t1.s_dist = 0;
+       bd_ptr->t1.s_size = 0;
+
+       return 0;
+}
+
+static void tsi721_dma_chain_complete(struct tsi721_bdma_chan *bdma_chan,
+                                     struct tsi721_tx_desc *desc)
+{
+       struct dma_async_tx_descriptor *txd = &desc->txd;
+       dma_async_tx_callback callback = txd->callback;
+       void *param = txd->callback_param;
+
+       list_splice_init(&desc->tx_list, &bdma_chan->free_list);
+       list_move(&desc->desc_node, &bdma_chan->free_list);
+       bdma_chan->completed_cookie = txd->cookie;
+
+       if (callback)
+               callback(param);
+}
+
+static void tsi721_dma_complete_all(struct tsi721_bdma_chan *bdma_chan)
+{
+       struct tsi721_tx_desc *desc, *_d;
+       LIST_HEAD(list);
+
+       BUG_ON(!tsi721_dma_is_idle(bdma_chan));
+
+       if (!list_empty(&bdma_chan->queue))
+               tsi721_start_dma(bdma_chan);
+
+       list_splice_init(&bdma_chan->active_list, &list);
+       list_splice_init(&bdma_chan->queue, &bdma_chan->active_list);
+
+       list_for_each_entry_safe(desc, _d, &list, desc_node)
+               tsi721_dma_chain_complete(bdma_chan, desc);
+}
+
+static void tsi721_clr_stat(struct tsi721_bdma_chan *bdma_chan)
+{
+       u32 srd_ptr;
+       u64 *sts_ptr;
+       int i, j;
+
+       /* Check and clear descriptor status FIFO entries */
+       srd_ptr = bdma_chan->sts_rdptr;
+       sts_ptr = bdma_chan->sts_base;
+       j = srd_ptr * 8;
+       while (sts_ptr[j]) {
+               for (i = 0; i < 8 && sts_ptr[j]; i++, j++)
+                       sts_ptr[j] = 0;
+
+               ++srd_ptr;
+               srd_ptr %= bdma_chan->sts_size;
+               j = srd_ptr * 8;
+       }
+
+       iowrite32(srd_ptr, bdma_chan->regs + TSI721_DMAC_DSRP);
+       bdma_chan->sts_rdptr = srd_ptr;
+}
+
+static void tsi721_advance_work(struct tsi721_bdma_chan *bdma_chan)
+{
+       if (list_empty(&bdma_chan->active_list) ||
+               list_is_singular(&bdma_chan->active_list)) {
+               dev_dbg(bdma_chan->dchan.device->dev,
+                       "%s: Active_list empty\n", __func__);
+               tsi721_dma_complete_all(bdma_chan);
+       } else {
+               dev_dbg(bdma_chan->dchan.device->dev,
+                       "%s: Active_list NOT empty\n", __func__);
+               tsi721_dma_chain_complete(bdma_chan,
+                                       tsi721_dma_first_active(bdma_chan));
+               tsi721_start_dma(bdma_chan);
+       }
+}
+
+static void tsi721_dma_tasklet(unsigned long data)
+{
+       struct tsi721_bdma_chan *bdma_chan = (struct tsi721_bdma_chan *)data;
+       u32 dmac_int, dmac_sts;
+
+       dmac_int = ioread32(bdma_chan->regs + TSI721_DMAC_INT);
+       dev_dbg(bdma_chan->dchan.device->dev, "%s: DMAC%d_INT = 0x%x\n",
+               __func__, bdma_chan->id, dmac_int);
+       /* Clear channel interrupts */
+       iowrite32(dmac_int, bdma_chan->regs + TSI721_DMAC_INT);
+
+       if (dmac_int & TSI721_DMAC_INT_ERR) {
+               dmac_sts = ioread32(bdma_chan->regs + TSI721_DMAC_STS);
+               dev_err(bdma_chan->dchan.device->dev,
+                       "%s: DMA ERROR - DMAC%d_STS = 0x%x\n",
+                       __func__, bdma_chan->id, dmac_sts);
+       }
+
+       if (dmac_int & TSI721_DMAC_INT_STFULL) {
+               dev_err(bdma_chan->dchan.device->dev,
+                       "%s: DMAC%d descriptor status FIFO is full\n",
+                       __func__, bdma_chan->id);
+       }
+
+       if (dmac_int & (TSI721_DMAC_INT_DONE | TSI721_DMAC_INT_IOFDONE)) {
+               tsi721_clr_stat(bdma_chan);
+               spin_lock(&bdma_chan->lock);
+               tsi721_advance_work(bdma_chan);
+               spin_unlock(&bdma_chan->lock);
+       }
+
+       /* Re-Enable BDMA channel interrupts */
+       iowrite32(TSI721_DMAC_INT_ALL, bdma_chan->regs + TSI721_DMAC_INTE);
+}
+
+static dma_cookie_t tsi721_tx_submit(struct dma_async_tx_descriptor *txd)
+{
+       struct tsi721_tx_desc *desc = to_tsi721_desc(txd);
+       struct tsi721_bdma_chan *bdma_chan = to_tsi721_chan(txd->chan);
+       dma_cookie_t cookie;
+
+       spin_lock_bh(&bdma_chan->lock);
+
+       cookie = txd->chan->cookie;
+       if (++cookie < 0)
+               cookie = 1;
+       txd->chan->cookie = cookie;
+       txd->cookie = cookie;
+
+       if (list_empty(&bdma_chan->active_list)) {
+               list_add_tail(&desc->desc_node, &bdma_chan->active_list);
+               tsi721_start_dma(bdma_chan);
+       } else {
+               list_add_tail(&desc->desc_node, &bdma_chan->queue);
+       }
+
+       spin_unlock_bh(&bdma_chan->lock);
+       return cookie;
+}
+
+static int tsi721_alloc_chan_resources(struct dma_chan *dchan)
+{
+       struct tsi721_bdma_chan *bdma_chan = to_tsi721_chan(dchan);
+#ifdef CONFIG_PCI_MSI
+       struct tsi721_device *priv = to_tsi721(dchan->device);
+#endif
+       struct tsi721_tx_desc *desc = NULL;
+       LIST_HEAD(tmp_list);
+       int i;
+       int rc;
+
+       if (bdma_chan->bd_base)
+               return bdma_chan->bd_num - 1;
+
+       /* Initialize BDMA channel */
+       if (tsi721_bdma_ch_init(bdma_chan)) {
+               dev_err(dchan->device->dev, "Unable to initialize data DMA"
+                       " channel %d, aborting\n", bdma_chan->id);
+               return -ENOMEM;
+       }
+
+       /* Alocate matching number of logical descriptors */
+       desc = kcalloc((bdma_chan->bd_num - 1), sizeof(struct tsi721_tx_desc),
+                       GFP_KERNEL);
+       if (!desc) {
+               dev_err(dchan->device->dev,
+                       "Failed to allocate logical descriptors\n");
+               rc = -ENOMEM;
+               goto err_out;
+       }
+
+       bdma_chan->tx_desc = desc;
+
+       for (i = 0; i < bdma_chan->bd_num - 1; i++) {
+               dma_async_tx_descriptor_init(&desc[i].txd, dchan);
+               desc[i].txd.tx_submit = tsi721_tx_submit;
+               desc[i].txd.flags = DMA_CTRL_ACK;
+               INIT_LIST_HEAD(&desc[i].tx_list);
+               list_add_tail(&desc[i].desc_node, &tmp_list);
+       }
+
+       spin_lock_bh(&bdma_chan->lock);
+       list_splice(&tmp_list, &bdma_chan->free_list);
+       bdma_chan->completed_cookie = dchan->cookie = 1;
+       spin_unlock_bh(&bdma_chan->lock);
+
+#ifdef CONFIG_PCI_MSI
+       if (priv->flags & TSI721_USING_MSIX) {
+               /* Request interrupt service if we are in MSI-X mode */
+               rc = request_irq(
+                       priv->msix[TSI721_VECT_DMA0_DONE +
+                                  bdma_chan->id].vector,
+                       tsi721_bdma_msix, 0,
+                       priv->msix[TSI721_VECT_DMA0_DONE +
+                                  bdma_chan->id].irq_name,
+                       (void *)bdma_chan);
+
+               if (rc) {
+                       dev_dbg(dchan->device->dev,
+                               "Unable to allocate MSI-X interrupt for "
+                               "BDMA%d-DONE\n", bdma_chan->id);
+                       goto err_out;
+               }
+
+               rc = request_irq(priv->msix[TSI721_VECT_DMA0_INT +
+                                           bdma_chan->id].vector,
+                               tsi721_bdma_msix, 0,
+                               priv->msix[TSI721_VECT_DMA0_INT +
+                                          bdma_chan->id].irq_name,
+                               (void *)bdma_chan);
+
+               if (rc) {
+                       dev_dbg(dchan->device->dev,
+                               "Unable to allocate MSI-X interrupt for "
+                               "BDMA%d-INT\n", bdma_chan->id);
+                       free_irq(
+                               priv->msix[TSI721_VECT_DMA0_DONE +
+                                          bdma_chan->id].vector,
+                               (void *)bdma_chan);
+                       rc = -EIO;
+                       goto err_out;
+               }
+       }
+#endif /* CONFIG_PCI_MSI */
+
+       tasklet_enable(&bdma_chan->tasklet);
+       tsi721_bdma_interrupt_enable(bdma_chan, 1);
+
+       return bdma_chan->bd_num - 1;
+
+err_out:
+       kfree(desc);
+       tsi721_bdma_ch_free(bdma_chan);
+       return rc;
+}
+
+static void tsi721_free_chan_resources(struct dma_chan *dchan)
+{
+       struct tsi721_bdma_chan *bdma_chan = to_tsi721_chan(dchan);
+#ifdef CONFIG_PCI_MSI
+       struct tsi721_device *priv = to_tsi721(dchan->device);
+#endif
+       LIST_HEAD(list);
+
+       dev_dbg(dchan->device->dev, "%s: Entry\n", __func__);
+
+       if (bdma_chan->bd_base == NULL)
+               return;
+
+       BUG_ON(!list_empty(&bdma_chan->active_list));
+       BUG_ON(!list_empty(&bdma_chan->queue));
+
+       tasklet_disable(&bdma_chan->tasklet);
+
+       spin_lock_bh(&bdma_chan->lock);
+       list_splice_init(&bdma_chan->free_list, &list);
+       spin_unlock_bh(&bdma_chan->lock);
+
+       tsi721_bdma_interrupt_enable(bdma_chan, 0);
+
+#ifdef CONFIG_PCI_MSI
+       if (priv->flags & TSI721_USING_MSIX) {
+               free_irq(priv->msix[TSI721_VECT_DMA0_DONE +
+                                   bdma_chan->id].vector, (void *)bdma_chan);
+               free_irq(priv->msix[TSI721_VECT_DMA0_INT +
+                                   bdma_chan->id].vector, (void *)bdma_chan);
+       }
+#endif /* CONFIG_PCI_MSI */
+
+       tsi721_bdma_ch_free(bdma_chan);
+       kfree(bdma_chan->tx_desc);
+}
+
+static
+enum dma_status tsi721_tx_status(struct dma_chan *dchan, dma_cookie_t cookie,
+                                struct dma_tx_state *txstate)
+{
+       struct tsi721_bdma_chan *bdma_chan = to_tsi721_chan(dchan);
+       dma_cookie_t            last_used;
+       dma_cookie_t            last_completed;
+       int                     ret;
+
+       spin_lock_bh(&bdma_chan->lock);
+       last_completed = bdma_chan->completed_cookie;
+       last_used = dchan->cookie;
+       spin_unlock_bh(&bdma_chan->lock);
+
+       ret = dma_async_is_complete(cookie, last_completed, last_used);
+
+       dma_set_tx_state(txstate, last_completed, last_used, 0);
+
+       dev_dbg(dchan->device->dev,
+               "%s: exit, ret: %d, last_completed: %d, last_used: %d\n",
+               __func__, ret, last_completed, last_used);
+
+       return ret;
+}
+
+static void tsi721_issue_pending(struct dma_chan *dchan)
+{
+       struct tsi721_bdma_chan *bdma_chan = to_tsi721_chan(dchan);
+
+       dev_dbg(dchan->device->dev, "%s: Entry\n", __func__);
+
+       if (tsi721_dma_is_idle(bdma_chan)) {
+               spin_lock_bh(&bdma_chan->lock);
+               tsi721_advance_work(bdma_chan);
+               spin_unlock_bh(&bdma_chan->lock);
+       } else
+               dev_dbg(dchan->device->dev,
+                       "%s: DMA channel still busy\n", __func__);
+}
+
+static
+struct dma_async_tx_descriptor *tsi721_prep_rio_sg(struct dma_chan *dchan,
+                       struct scatterlist *sgl, unsigned int sg_len,
+                       enum dma_transfer_direction dir, unsigned long flags,
+                       void *tinfo)
+{
+       struct tsi721_bdma_chan *bdma_chan = to_tsi721_chan(dchan);
+       struct tsi721_tx_desc *desc = NULL;
+       struct tsi721_tx_desc *first = NULL;
+       struct scatterlist *sg;
+       struct rio_dma_ext *rext = tinfo;
+       u64 rio_addr = rext->rio_addr; /* limited to 64-bit rio_addr for now */
+       unsigned int i;
+       u32 sys_size = dma_to_mport(dchan->device)->sys_size;
+       enum dma_rtype rtype;
+
+       if (!sgl || !sg_len) {
+               dev_err(dchan->device->dev, "%s: No SG list\n", __func__);
+               return NULL;
+       }
+
+       if (dir == DMA_DEV_TO_MEM)
+               rtype = NREAD;
+       else if (dir == DMA_MEM_TO_DEV) {
+               switch (rext->wr_type) {
+               case RDW_ALL_NWRITE:
+                       rtype = ALL_NWRITE;
+                       break;
+               case RDW_ALL_NWRITE_R:
+                       rtype = ALL_NWRITE_R;
+                       break;
+               case RDW_LAST_NWRITE_R:
+               default:
+                       rtype = LAST_NWRITE_R;
+                       break;
+               }
+       } else {
+               dev_err(dchan->device->dev,
+                       "%s: Unsupported DMA direction option\n", __func__);
+               return NULL;
+       }
+
+       for_each_sg(sgl, sg, sg_len, i) {
+               int err;
+
+               dev_dbg(dchan->device->dev, "%s: sg #%d\n", __func__, i);
+               desc = tsi721_desc_get(bdma_chan);
+               if (!desc) {
+                       dev_err(dchan->device->dev,
+                               "Not enough descriptors available\n");
+                       goto err_desc_get;
+               }
+
+               if (sg_is_last(sg))
+                       desc->interrupt = (flags & DMA_PREP_INTERRUPT) != 0;
+               else
+                       desc->interrupt = false;
+
+               desc->destid = rext->destid;
+               desc->rio_addr = rio_addr;
+               desc->rio_addr_u = 0;
+
+               err = tsi721_fill_desc(bdma_chan, desc, sg, rtype, sys_size);
+               if (err) {
+                       dev_err(dchan->device->dev,
+                               "Failed to build desc: %d\n", err);
+                       goto err_desc_get;
+               }
+
+               rio_addr += sg_dma_len(sg);
+
+               if (!first)
+                       first = desc;
+               else
+                       list_add_tail(&desc->desc_node, &first->tx_list);
+       }
+
+       first->txd.cookie = -EBUSY;
+       desc->txd.flags = flags;
+
+       return &first->txd;
+
+err_desc_get:
+       tsi721_desc_put(bdma_chan, first);
+       return NULL;
+}
+
+static int tsi721_device_control(struct dma_chan *dchan, enum dma_ctrl_cmd cmd,
+                            unsigned long arg)
+{
+       struct tsi721_bdma_chan *bdma_chan = to_tsi721_chan(dchan);
+       struct tsi721_tx_desc *desc, *_d;
+       LIST_HEAD(list);
+
+       dev_dbg(dchan->device->dev, "%s: Entry\n", __func__);
+
+       if (cmd != DMA_TERMINATE_ALL)
+               return -ENXIO;
+
+       spin_lock_bh(&bdma_chan->lock);
+
+       /* make sure to stop the transfer */
+       iowrite32(TSI721_DMAC_CTL_SUSP, bdma_chan->regs + TSI721_DMAC_CTL);
+
+       list_splice_init(&bdma_chan->active_list, &list);
+       list_splice_init(&bdma_chan->queue, &list);
+
+       list_for_each_entry_safe(desc, _d, &list, desc_node)
+               tsi721_dma_chain_complete(bdma_chan, desc);
+
+       spin_unlock_bh(&bdma_chan->lock);
+
+       return 0;
+}
+
+int __devinit tsi721_register_dma(struct tsi721_device *priv)
+{
+       int i;
+       int nr_channels = TSI721_DMA_MAXCH;
+       int err;
+       struct rio_mport *mport = priv->mport;
+
+       mport->dma.dev = &priv->pdev->dev;
+       mport->dma.chancnt = nr_channels;
+
+       INIT_LIST_HEAD(&mport->dma.channels);
+
+       for (i = 0; i < nr_channels; i++) {
+               struct tsi721_bdma_chan *bdma_chan = &priv->bdma[i];
+
+               if (i == TSI721_DMACH_MAINT)
+                       continue;
+
+               bdma_chan->bd_num = 64;
+               bdma_chan->regs = priv->regs + TSI721_DMAC_BASE(i);
+
+               bdma_chan->dchan.device = &mport->dma;
+               bdma_chan->dchan.cookie = 1;
+               bdma_chan->dchan.chan_id = i;
+               bdma_chan->id = i;
+
+               spin_lock_init(&bdma_chan->lock);
+
+               INIT_LIST_HEAD(&bdma_chan->active_list);
+               INIT_LIST_HEAD(&bdma_chan->queue);
+               INIT_LIST_HEAD(&bdma_chan->free_list);
+
+               tasklet_init(&bdma_chan->tasklet, tsi721_dma_tasklet,
+                            (unsigned long)bdma_chan);
+               tasklet_disable(&bdma_chan->tasklet);
+               list_add_tail(&bdma_chan->dchan.device_node,
+                             &mport->dma.channels);
+       }
+
+       dma_cap_zero(mport->dma.cap_mask);
+       dma_cap_set(DMA_PRIVATE, mport->dma.cap_mask);
+       dma_cap_set(DMA_SLAVE, mport->dma.cap_mask);
+
+       mport->dma.device_alloc_chan_resources = tsi721_alloc_chan_resources;
+       mport->dma.device_free_chan_resources = tsi721_free_chan_resources;
+       mport->dma.device_tx_status = tsi721_tx_status;
+       mport->dma.device_issue_pending = tsi721_issue_pending;
+       mport->dma.device_prep_slave_sg = tsi721_prep_rio_sg;
+       mport->dma.device_control = tsi721_device_control;
+
+       err = dma_async_device_register(&mport->dma);
+       if (err)
+               dev_err(&priv->pdev->dev, "Failed to register DMA device\n");
+
+       return err;
+}
index 86c9a091a2ffdbfb3cac0868f17824ee9f4e5b6d..c40665a4fa3347a8b9bdb1edd12c4b48eacb5d43 100644 (file)
@@ -1121,6 +1121,87 @@ int rio_std_route_clr_table(struct rio_mport *mport, u16 destid, u8 hopcount,
        return 0;
 }
 
+#ifdef CONFIG_RAPIDIO_DMA_ENGINE
+
+static bool rio_chan_filter(struct dma_chan *chan, void *arg)
+{
+       struct rio_dev *rdev = arg;
+
+       /* Check that DMA device belongs to the right MPORT */
+       return (rdev->net->hport ==
+               container_of(chan->device, struct rio_mport, dma));
+}
+
+/**
+ * rio_request_dma - request RapidIO capable DMA channel that supports
+ *   specified target RapidIO device.
+ * @rdev: RIO device control structure
+ *
+ * Returns pointer to allocated DMA channel or NULL if failed.
+ */
+struct dma_chan *rio_request_dma(struct rio_dev *rdev)
+{
+       dma_cap_mask_t mask;
+       struct dma_chan *dchan;
+
+       dma_cap_zero(mask);
+       dma_cap_set(DMA_SLAVE, mask);
+       dchan = dma_request_channel(mask, rio_chan_filter, rdev);
+
+       return dchan;
+}
+EXPORT_SYMBOL_GPL(rio_request_dma);
+
+/**
+ * rio_release_dma - release specified DMA channel
+ * @dchan: DMA channel to release
+ */
+void rio_release_dma(struct dma_chan *dchan)
+{
+       dma_release_channel(dchan);
+}
+EXPORT_SYMBOL_GPL(rio_release_dma);
+
+/**
+ * rio_dma_prep_slave_sg - RapidIO specific wrapper
+ *   for device_prep_slave_sg callback defined by DMAENGINE.
+ * @rdev: RIO device control structure
+ * @dchan: DMA channel to configure
+ * @data: RIO specific data descriptor
+ * @direction: DMA data transfer direction (TO or FROM the device)
+ * @flags: dmaengine defined flags
+ *
+ * Initializes RapidIO capable DMA channel for the specified data transfer.
+ * Uses DMA channel private extension to pass information related to remote
+ * target RIO device.
+ * Returns pointer to DMA transaction descriptor or NULL if failed.
+ */
+struct dma_async_tx_descriptor *rio_dma_prep_slave_sg(struct rio_dev *rdev,
+       struct dma_chan *dchan, struct rio_dma_data *data,
+       enum dma_transfer_direction direction, unsigned long flags)
+{
+       struct dma_async_tx_descriptor *txd = NULL;
+       struct rio_dma_ext rio_ext;
+
+       if (dchan->device->device_prep_slave_sg == NULL) {
+               pr_err("%s: prep_rio_sg == NULL\n", __func__);
+               return NULL;
+       }
+
+       rio_ext.destid = rdev->destid;
+       rio_ext.rio_addr_u = data->rio_addr_u;
+       rio_ext.rio_addr = data->rio_addr;
+       rio_ext.wr_type = data->wr_type;
+
+       txd = dmaengine_prep_rio_sg(dchan, data->sg, data->sg_len,
+                                       direction, flags, &rio_ext);
+
+       return txd;
+}
+EXPORT_SYMBOL_GPL(rio_dma_prep_slave_sg);
+
+#endif /* CONFIG_RAPIDIO_DMA_ENGINE */
+
 static void rio_fixup_device(struct rio_dev *dev)
 {
 }
index 49b2112b0486120c5434f7e2295955202538387f..3660bace123c97adc3f3ce4e1165216655578a3d 100644 (file)
@@ -47,7 +47,7 @@ static int anatop_set_voltage(struct regulator_dev *reg, int min_uV,
                                  int max_uV, unsigned *selector)
 {
        struct anatop_regulator *anatop_reg = rdev_get_drvdata(reg);
-       u32 val, sel;
+       u32 val, sel, mask;
        int uv;
 
        uv = min_uV;
@@ -71,11 +71,10 @@ static int anatop_set_voltage(struct regulator_dev *reg, int min_uV,
        val = anatop_reg->min_bit_val + sel;
        *selector = sel;
        dev_dbg(&reg->dev, "%s: calculated val %d\n", __func__, val);
-       anatop_set_bits(anatop_reg->mfd,
-                       anatop_reg->control_reg,
-                       anatop_reg->vol_bit_shift,
-                       anatop_reg->vol_bit_width,
-                       val);
+       mask = ((1 << anatop_reg->vol_bit_width) - 1) <<
+               anatop_reg->vol_bit_shift;
+       val <<= anatop_reg->vol_bit_shift;
+       anatop_write_reg(anatop_reg->mfd, anatop_reg->control_reg, val, mask);
 
        return 0;
 }
@@ -88,10 +87,9 @@ static int anatop_get_voltage_sel(struct regulator_dev *reg)
        if (!anatop_reg->control_reg)
                return -ENOTSUPP;
 
-       val = anatop_get_bits(anatop_reg->mfd,
-                             anatop_reg->control_reg,
-                             anatop_reg->vol_bit_shift,
-                             anatop_reg->vol_bit_width);
+       val = anatop_read_reg(anatop_reg->mfd, anatop_reg->control_reg);
+       val = (val & ((1 << anatop_reg->vol_bit_width) - 1)) >>
+               anatop_reg->vol_bit_shift;
 
        return val - anatop_reg->min_bit_val;
 }
index 4e01a423471b7d521921a5cf65a99afa1cd7fdc5..6bf864b4bdf67e8ddd860ed16fe0e7248e65318b 100644 (file)
@@ -331,21 +331,16 @@ struct tps65910_reg {
 
 static inline int tps65910_read(struct tps65910_reg *pmic, u8 reg)
 {
-       u8 val;
+       unsigned int val;
        int err;
 
-       err = pmic->mfd->read(pmic->mfd, reg, 1, &val);
+       err = tps65910_reg_read(pmic->mfd, reg, &val);
        if (err)
                return err;
 
        return val;
 }
 
-static inline int tps65910_write(struct tps65910_reg *pmic, u8 reg, u8 val)
-{
-       return pmic->mfd->write(pmic->mfd, reg, 1, &val);
-}
-
 static int tps65910_modify_bits(struct tps65910_reg *pmic, u8 reg,
                                        u8 set_mask, u8 clear_mask)
 {
@@ -362,7 +357,7 @@ static int tps65910_modify_bits(struct tps65910_reg *pmic, u8 reg,
 
        data &= ~clear_mask;
        data |= set_mask;
-       err = tps65910_write(pmic, reg, data);
+       err = tps65910_reg_write(pmic->mfd, reg, data);
        if (err)
                dev_err(pmic->mfd->dev, "Write for reg 0x%x failed\n", reg);
 
@@ -371,7 +366,7 @@ out:
        return err;
 }
 
-static int tps65910_reg_read(struct tps65910_reg *pmic, u8 reg)
+static int tps65910_reg_read_locked(struct tps65910_reg *pmic, u8 reg)
 {
        int data;
 
@@ -385,13 +380,13 @@ static int tps65910_reg_read(struct tps65910_reg *pmic, u8 reg)
        return data;
 }
 
-static int tps65910_reg_write(struct tps65910_reg *pmic, u8 reg, u8 val)
+static int tps65910_reg_write_locked(struct tps65910_reg *pmic, u8 reg, u8 val)
 {
        int err;
 
        mutex_lock(&pmic->mutex);
 
-       err = tps65910_write(pmic, reg, val);
+       err = tps65910_reg_write(pmic->mfd, reg, val);
        if (err < 0)
                dev_err(pmic->mfd->dev, "Write for reg 0x%x failed\n", reg);
 
@@ -490,9 +485,9 @@ static int tps65910_set_mode(struct regulator_dev *dev, unsigned int mode)
                                                        LDO_ST_MODE_BIT);
        case REGULATOR_MODE_IDLE:
                value = LDO_ST_ON_BIT | LDO_ST_MODE_BIT;
-               return tps65910_set_bits(mfd, reg, value);
+               return tps65910_reg_set_bits(mfd, reg, value);
        case REGULATOR_MODE_STANDBY:
-               return tps65910_clear_bits(mfd, reg, LDO_ST_ON_BIT);
+               return tps65910_reg_clear_bits(mfd, reg, LDO_ST_ON_BIT);
        }
 
        return -EINVAL;
@@ -507,7 +502,7 @@ static unsigned int tps65910_get_mode(struct regulator_dev *dev)
        if (reg < 0)
                return reg;
 
-       value = tps65910_reg_read(pmic, reg);
+       value = tps65910_reg_read_locked(pmic, reg);
        if (value < 0)
                return value;
 
@@ -527,28 +522,28 @@ static int tps65910_get_voltage_dcdc_sel(struct regulator_dev *dev)
 
        switch (id) {
        case TPS65910_REG_VDD1:
-               opvsel = tps65910_reg_read(pmic, TPS65910_VDD1_OP);
-               mult = tps65910_reg_read(pmic, TPS65910_VDD1);
+               opvsel = tps65910_reg_read_locked(pmic, TPS65910_VDD1_OP);
+               mult = tps65910_reg_read_locked(pmic, TPS65910_VDD1);
                mult = (mult & VDD1_VGAIN_SEL_MASK) >> VDD1_VGAIN_SEL_SHIFT;
-               srvsel = tps65910_reg_read(pmic, TPS65910_VDD1_SR);
+               srvsel = tps65910_reg_read_locked(pmic, TPS65910_VDD1_SR);
                sr = opvsel & VDD1_OP_CMD_MASK;
                opvsel &= VDD1_OP_SEL_MASK;
                srvsel &= VDD1_SR_SEL_MASK;
                vselmax = 75;
                break;
        case TPS65910_REG_VDD2:
-               opvsel = tps65910_reg_read(pmic, TPS65910_VDD2_OP);
-               mult = tps65910_reg_read(pmic, TPS65910_VDD2);
+               opvsel = tps65910_reg_read_locked(pmic, TPS65910_VDD2_OP);
+               mult = tps65910_reg_read_locked(pmic, TPS65910_VDD2);
                mult = (mult & VDD2_VGAIN_SEL_MASK) >> VDD2_VGAIN_SEL_SHIFT;
-               srvsel = tps65910_reg_read(pmic, TPS65910_VDD2_SR);
+               srvsel = tps65910_reg_read_locked(pmic, TPS65910_VDD2_SR);
                sr = opvsel & VDD2_OP_CMD_MASK;
                opvsel &= VDD2_OP_SEL_MASK;
                srvsel &= VDD2_SR_SEL_MASK;
                vselmax = 75;
                break;
        case TPS65911_REG_VDDCTRL:
-               opvsel = tps65910_reg_read(pmic, TPS65911_VDDCTRL_OP);
-               srvsel = tps65910_reg_read(pmic, TPS65911_VDDCTRL_SR);
+               opvsel = tps65910_reg_read_locked(pmic, TPS65911_VDDCTRL_OP);
+               srvsel = tps65910_reg_read_locked(pmic, TPS65911_VDDCTRL_SR);
                sr = opvsel & VDDCTRL_OP_CMD_MASK;
                opvsel &= VDDCTRL_OP_SEL_MASK;
                srvsel &= VDDCTRL_SR_SEL_MASK;
@@ -588,7 +583,7 @@ static int tps65910_get_voltage_sel(struct regulator_dev *dev)
        if (reg < 0)
                return reg;
 
-       value = tps65910_reg_read(pmic, reg);
+       value = tps65910_reg_read_locked(pmic, reg);
        if (value < 0)
                return value;
 
@@ -625,7 +620,7 @@ static int tps65911_get_voltage_sel(struct regulator_dev *dev)
 
        reg = pmic->get_ctrl_reg(id);
 
-       value = tps65910_reg_read(pmic, reg);
+       value = tps65910_reg_read_locked(pmic, reg);
 
        switch (id) {
        case TPS65911_REG_LDO1:
@@ -670,7 +665,7 @@ static int tps65910_set_voltage_dcdc_sel(struct regulator_dev *dev,
                tps65910_modify_bits(pmic, TPS65910_VDD1,
                                (dcdc_mult << VDD1_VGAIN_SEL_SHIFT),
                                                VDD1_VGAIN_SEL_MASK);
-               tps65910_reg_write(pmic, TPS65910_VDD1_OP, vsel);
+               tps65910_reg_write_locked(pmic, TPS65910_VDD1_OP, vsel);
                break;
        case TPS65910_REG_VDD2:
                dcdc_mult = (selector / VDD1_2_NUM_VOLT_FINE) + 1;
@@ -681,11 +676,11 @@ static int tps65910_set_voltage_dcdc_sel(struct regulator_dev *dev,
                tps65910_modify_bits(pmic, TPS65910_VDD2,
                                (dcdc_mult << VDD2_VGAIN_SEL_SHIFT),
                                                VDD1_VGAIN_SEL_MASK);
-               tps65910_reg_write(pmic, TPS65910_VDD2_OP, vsel);
+               tps65910_reg_write_locked(pmic, TPS65910_VDD2_OP, vsel);
                break;
        case TPS65911_REG_VDDCTRL:
                vsel = selector + 3;
-               tps65910_reg_write(pmic, TPS65911_VDDCTRL_OP, vsel);
+               tps65910_reg_write_locked(pmic, TPS65911_VDDCTRL_OP, vsel);
        }
 
        return 0;
@@ -936,10 +931,10 @@ static int tps65910_set_ext_sleep_config(struct tps65910_reg *pmic,
 
        /* External EN1 control */
        if (ext_sleep_config & TPS65910_SLEEP_CONTROL_EXT_INPUT_EN1)
-               ret = tps65910_set_bits(mfd,
+               ret = tps65910_reg_set_bits(mfd,
                                TPS65910_EN1_LDO_ASS + regoffs, bit_pos);
        else
-               ret = tps65910_clear_bits(mfd,
+               ret = tps65910_reg_clear_bits(mfd,
                                TPS65910_EN1_LDO_ASS + regoffs, bit_pos);
        if (ret < 0) {
                dev_err(mfd->dev,
@@ -949,10 +944,10 @@ static int tps65910_set_ext_sleep_config(struct tps65910_reg *pmic,
 
        /* External EN2 control */
        if (ext_sleep_config & TPS65910_SLEEP_CONTROL_EXT_INPUT_EN2)
-               ret = tps65910_set_bits(mfd,
+               ret = tps65910_reg_set_bits(mfd,
                                TPS65910_EN2_LDO_ASS + regoffs, bit_pos);
        else
-               ret = tps65910_clear_bits(mfd,
+               ret = tps65910_reg_clear_bits(mfd,
                                TPS65910_EN2_LDO_ASS + regoffs, bit_pos);
        if (ret < 0) {
                dev_err(mfd->dev,
@@ -964,10 +959,10 @@ static int tps65910_set_ext_sleep_config(struct tps65910_reg *pmic,
        if ((tps65910_chip_id(mfd) == TPS65910) &&
                        (id >= TPS65910_REG_VDIG1)) {
                if (ext_sleep_config & TPS65910_SLEEP_CONTROL_EXT_INPUT_EN3)
-                       ret = tps65910_set_bits(mfd,
+                       ret = tps65910_reg_set_bits(mfd,
                                TPS65910_EN3_LDO_ASS + regoffs, bit_pos);
                else
-                       ret = tps65910_clear_bits(mfd,
+                       ret = tps65910_reg_clear_bits(mfd,
                                TPS65910_EN3_LDO_ASS + regoffs, bit_pos);
                if (ret < 0) {
                        dev_err(mfd->dev,
@@ -979,10 +974,10 @@ static int tps65910_set_ext_sleep_config(struct tps65910_reg *pmic,
        /* Return if no external control is selected */
        if (!(ext_sleep_config & EXT_SLEEP_CONTROL)) {
                /* Clear all sleep controls */
-               ret = tps65910_clear_bits(mfd,
+               ret = tps65910_reg_clear_bits(mfd,
                        TPS65910_SLEEP_KEEP_LDO_ON + regoffs, bit_pos);
                if (!ret)
-                       ret = tps65910_clear_bits(mfd,
+                       ret = tps65910_reg_clear_bits(mfd,
                                TPS65910_SLEEP_SET_LDO_OFF + regoffs, bit_pos);
                if (ret < 0)
                        dev_err(mfd->dev,
@@ -1001,32 +996,33 @@ static int tps65910_set_ext_sleep_config(struct tps65910_reg *pmic,
                                (tps65910_chip_id(mfd) == TPS65911))) {
                int op_reg_add = pmic->get_ctrl_reg(id) + 1;
                int sr_reg_add = pmic->get_ctrl_reg(id) + 2;
-               int opvsel = tps65910_reg_read(pmic, op_reg_add);
-               int srvsel = tps65910_reg_read(pmic, sr_reg_add);
+               int opvsel = tps65910_reg_read_locked(pmic, op_reg_add);
+               int srvsel = tps65910_reg_read_locked(pmic, sr_reg_add);
                if (opvsel & VDD1_OP_CMD_MASK) {
                        u8 reg_val = srvsel & VDD1_OP_SEL_MASK;
-                       ret = tps65910_reg_write(pmic, op_reg_add, reg_val);
+                       ret = tps65910_reg_write_locked(pmic, op_reg_add,
+                                                       reg_val);
                        if (ret < 0) {
                                dev_err(mfd->dev,
                                        "Error in configuring op register\n");
                                return ret;
                        }
                }
-               ret = tps65910_reg_write(pmic, sr_reg_add, 0);
+               ret = tps65910_reg_write_locked(pmic, sr_reg_add, 0);
                if (ret < 0) {
                        dev_err(mfd->dev, "Error in settting sr register\n");
                        return ret;
                }
        }
 
-       ret = tps65910_clear_bits(mfd,
+       ret = tps65910_reg_clear_bits(mfd,
                        TPS65910_SLEEP_KEEP_LDO_ON + regoffs, bit_pos);
        if (!ret) {
                if (ext_sleep_config & TPS65911_SLEEP_CONTROL_EXT_INPUT_SLEEP)
-                       ret = tps65910_set_bits(mfd,
+                       ret = tps65910_reg_set_bits(mfd,
                                TPS65910_SLEEP_SET_LDO_OFF + regoffs, bit_pos);
                else
-                       ret = tps65910_clear_bits(mfd,
+                       ret = tps65910_reg_clear_bits(mfd,
                                TPS65910_SLEEP_SET_LDO_OFF + regoffs, bit_pos);
        }
        if (ret < 0)
@@ -1177,7 +1173,7 @@ static __devinit int tps65910_probe(struct platform_device *pdev)
        platform_set_drvdata(pdev, pmic);
 
        /* Give control of all register to control port */
-       tps65910_set_bits(pmic->mfd, TPS65910_DEVCTRL,
+       tps65910_reg_set_bits(pmic->mfd, TPS65910_DEVCTRL,
                                DEVCTRL_SR_CTL_I2C_SEL_MASK);
 
        switch(tps65910_chip_id(tps65910)) {
index a885911bb5fce9c3adf4640b2e65fbbe97c7bf9b..099da11e989fde4e9ed793c3cdee9c6452c58a0f 100644 (file)
@@ -535,7 +535,7 @@ static __devinit int wm831x_buckv_probe(struct platform_device *pdev)
                goto err;
        }
 
-       irq = platform_get_irq_byname(pdev, "UV");
+       irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "UV"));
        ret = request_threaded_irq(irq, NULL, wm831x_dcdc_uv_irq,
                                   IRQF_TRIGGER_RISING, dcdc->name, dcdc);
        if (ret != 0) {
@@ -544,7 +544,7 @@ static __devinit int wm831x_buckv_probe(struct platform_device *pdev)
                goto err_regulator;
        }
 
-       irq = platform_get_irq_byname(pdev, "HC");
+       irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "HC"));
        ret = request_threaded_irq(irq, NULL, wm831x_dcdc_oc_irq,
                                   IRQF_TRIGGER_RISING, dcdc->name, dcdc);
        if (ret != 0) {
@@ -558,7 +558,8 @@ static __devinit int wm831x_buckv_probe(struct platform_device *pdev)
        return 0;
 
 err_uv:
-       free_irq(platform_get_irq_byname(pdev, "UV"), dcdc);
+       free_irq(wm831x_irq(wm831x, platform_get_irq_byname(pdev, "UV")),
+                dcdc);
 err_regulator:
        regulator_unregister(dcdc->regulator);
 err:
@@ -570,11 +571,14 @@ err:
 static __devexit int wm831x_buckv_remove(struct platform_device *pdev)
 {
        struct wm831x_dcdc *dcdc = platform_get_drvdata(pdev);
+       struct wm831x *wm831x = dcdc->wm831x;
 
        platform_set_drvdata(pdev, NULL);
 
-       free_irq(platform_get_irq_byname(pdev, "HC"), dcdc);
-       free_irq(platform_get_irq_byname(pdev, "UV"), dcdc);
+       free_irq(wm831x_irq(wm831x, platform_get_irq_byname(pdev, "HC")),
+                           dcdc);
+       free_irq(wm831x_irq(wm831x, platform_get_irq_byname(pdev, "UV")),
+                           dcdc);
        regulator_unregister(dcdc->regulator);
        if (dcdc->dvs_gpio)
                gpio_free(dcdc->dvs_gpio);
@@ -726,7 +730,7 @@ static __devinit int wm831x_buckp_probe(struct platform_device *pdev)
                goto err;
        }
 
-       irq = platform_get_irq_byname(pdev, "UV");
+       irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "UV"));
        ret = request_threaded_irq(irq, NULL, wm831x_dcdc_uv_irq,
                                   IRQF_TRIGGER_RISING, dcdc->name, dcdc);
        if (ret != 0) {
@@ -751,7 +755,8 @@ static __devexit int wm831x_buckp_remove(struct platform_device *pdev)
 
        platform_set_drvdata(pdev, NULL);
 
-       free_irq(platform_get_irq_byname(pdev, "UV"), dcdc);
+       free_irq(wm831x_irq(dcdc->wm831x, platform_get_irq_byname(pdev, "UV")),
+                           dcdc);
        regulator_unregister(dcdc->regulator);
 
        return 0;
@@ -859,7 +864,7 @@ static __devinit int wm831x_boostp_probe(struct platform_device *pdev)
                goto err;
        }
 
-       irq = platform_get_irq_byname(pdev, "UV");
+       irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "UV"));
        ret = request_threaded_irq(irq, NULL, wm831x_dcdc_uv_irq,
                                   IRQF_TRIGGER_RISING, dcdc->name,
                                   dcdc);
@@ -885,7 +890,8 @@ static __devexit int wm831x_boostp_remove(struct platform_device *pdev)
 
        platform_set_drvdata(pdev, NULL);
 
-       free_irq(platform_get_irq_byname(pdev, "UV"), dcdc);
+       free_irq(wm831x_irq(dcdc->wm831x, platform_get_irq_byname(pdev, "UV")),
+                dcdc);
        regulator_unregister(dcdc->regulator);
 
        return 0;
index b50ab778b098274965667a0408ead22253efe8d2..0d207c297714ed2c7fff51e1e3c06de18c29d361 100644 (file)
@@ -202,7 +202,7 @@ static __devinit int wm831x_isink_probe(struct platform_device *pdev)
                goto err;
        }
 
-       irq = platform_get_irq(pdev, 0);
+       irq = wm831x_irq(wm831x, platform_get_irq(pdev, 0));
        ret = request_threaded_irq(irq, NULL, wm831x_isink_irq,
                                   IRQF_TRIGGER_RISING, isink->name, isink);
        if (ret != 0) {
@@ -227,7 +227,7 @@ static __devexit int wm831x_isink_remove(struct platform_device *pdev)
 
        platform_set_drvdata(pdev, NULL);
 
-       free_irq(platform_get_irq(pdev, 0), isink);
+       free_irq(wm831x_irq(isink->wm831x, platform_get_irq(pdev, 0)), isink);
 
        regulator_unregister(isink->regulator);
 
index aa1f8b3fbe16c7a6e1683b537db9721e77c56b0d..a9a28d8ac18591d4c7126364dbd40197024191f8 100644 (file)
@@ -321,7 +321,7 @@ static __devinit int wm831x_gp_ldo_probe(struct platform_device *pdev)
                goto err;
        }
 
-       irq = platform_get_irq_byname(pdev, "UV");
+       irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "UV"));
        ret = request_threaded_irq(irq, NULL, wm831x_ldo_uv_irq,
                                   IRQF_TRIGGER_RISING, ldo->name,
                                   ldo);
@@ -347,7 +347,8 @@ static __devexit int wm831x_gp_ldo_remove(struct platform_device *pdev)
 
        platform_set_drvdata(pdev, NULL);
 
-       free_irq(platform_get_irq_byname(pdev, "UV"), ldo);
+       free_irq(wm831x_irq(ldo->wm831x,
+                           platform_get_irq_byname(pdev, "UV")), ldo);
        regulator_unregister(ldo->regulator);
 
        return 0;
@@ -582,7 +583,7 @@ static __devinit int wm831x_aldo_probe(struct platform_device *pdev)
                goto err;
        }
 
-       irq = platform_get_irq_byname(pdev, "UV");
+       irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "UV"));
        ret = request_threaded_irq(irq, NULL, wm831x_ldo_uv_irq,
                                   IRQF_TRIGGER_RISING, ldo->name, ldo);
        if (ret != 0) {
@@ -605,7 +606,8 @@ static __devexit int wm831x_aldo_remove(struct platform_device *pdev)
 {
        struct wm831x_ldo *ldo = platform_get_drvdata(pdev);
 
-       free_irq(platform_get_irq_byname(pdev, "UV"), ldo);
+       free_irq(wm831x_irq(ldo->wm831x, platform_get_irq_byname(pdev, "UV")),
+                ldo);
        regulator_unregister(ldo->regulator);
 
        return 0;
index d6f8adaa26efb1fd0916253f2daf3f420e4ff486..8ea7bccc71007fd94132cf7c65da5f5ed5095a12 100644 (file)
@@ -78,7 +78,7 @@ typedef int (*rproc_handle_resource_t)(struct rproc *rproc, void *, int avail);
  * the recovery of the remote processor.
  */
 static int rproc_iommu_fault(struct iommu_domain *domain, struct device *dev,
-               unsigned long iova, int flags)
+               unsigned long iova, int flags, void *token)
 {
        dev_err(dev, "iommu fault: da 0x%lx flags 0x%x\n", iova, flags);
 
@@ -117,7 +117,7 @@ static int rproc_enable_iommu(struct rproc *rproc)
                return -ENOMEM;
        }
 
-       iommu_set_fault_handler(domain, rproc_iommu_fault);
+       iommu_set_fault_handler(domain, rproc_iommu_fault, rproc);
 
        ret = iommu_attach_device(domain, dev);
        if (ret) {
index 4161bfe462cd5f958c1f1d790bd9ef795df8b530..08cbdb900a18a290a7065bc73850015f8278ca10 100644 (file)
@@ -620,27 +620,6 @@ config RTC_DRV_MSM6242
          This driver can also be built as a module. If so, the module
          will be called rtc-msm6242.
 
-config RTC_DRV_IMXDI
-       tristate "Freescale IMX DryIce Real Time Clock"
-       depends on ARCH_MX25
-       depends on RTC_CLASS
-       help
-          Support for Freescale IMX DryIce RTC
-
-          This driver can also be built as a module, if so, the module
-          will be called "rtc-imxdi".
-
-config RTC_MXC
-       tristate "Freescale MXC Real Time Clock"
-       depends on ARCH_MXC
-       depends on RTC_CLASS
-       help
-          If you say yes here you get support for the Freescale MXC
-          RTC module.
-
-          This driver can also be built as a module, if so, the module
-          will be called "rtc-mxc".
-
 config RTC_DRV_BQ4802
        tristate "TI BQ4802"
        help
@@ -738,6 +717,16 @@ config RTC_DRV_DAVINCI
          This driver can also be built as a module. If so, the module
          will be called rtc-davinci.
 
+config RTC_DRV_IMXDI
+       tristate "Freescale IMX DryIce Real Time Clock"
+       depends on SOC_IMX25
+       depends on RTC_CLASS
+       help
+          Support for Freescale IMX DryIce RTC
+
+          This driver can also be built as a module, if so, the module
+          will be called "rtc-imxdi".
+
 config RTC_DRV_OMAP
        tristate "TI OMAP1"
        depends on ARCH_OMAP15XX || ARCH_OMAP16XX || ARCH_OMAP730 || ARCH_DAVINCI_DA8XX
@@ -1087,4 +1076,15 @@ config RTC_DRV_LOONGSON1
          This driver can also be built as a module. If so, the module
          will be called rtc-ls1x.
 
+config RTC_DRV_MXC
+       tristate "Freescale MXC Real Time Clock"
+       depends on ARCH_MXC
+       depends on RTC_CLASS
+       help
+          If you say yes here you get support for the Freescale MXC
+          RTC module.
+
+          This driver can also be built as a module, if so, the module
+          will be called "rtc-mxc".
+
 endif # RTC_CLASS
index 727ae7786e6c3806face77564b82e76c52a5b01f..2973921c30d84d70eafea434dd3e678e4c54c3c1 100644 (file)
@@ -61,7 +61,7 @@ obj-$(CONFIG_RTC_DRV_M41T94)  += rtc-m41t94.o
 obj-$(CONFIG_RTC_DRV_M48T35)   += rtc-m48t35.o
 obj-$(CONFIG_RTC_DRV_M48T59)   += rtc-m48t59.o
 obj-$(CONFIG_RTC_DRV_M48T86)   += rtc-m48t86.o
-obj-$(CONFIG_RTC_MXC)          += rtc-mxc.o
+obj-$(CONFIG_RTC_DRV_MXC)      += rtc-mxc.o
 obj-$(CONFIG_RTC_DRV_MAX6900)  += rtc-max6900.o
 obj-$(CONFIG_RTC_DRV_MAX8925)  += rtc-max8925.o
 obj-$(CONFIG_RTC_DRV_MAX8998)  += rtc-max8998.o
index c293d0cdb10483502784653f8617d0f0ecb55562..836710ce750e703ae06f8761aca9d00b64bd84f9 100644 (file)
@@ -17,8 +17,7 @@
 #include <linux/string.h>
 #include <linux/rtc.h>
 #include <linux/bcd.h>
-
-
+#include <linux/rtc/ds1307.h>
 
 /*
  * We can't determine type by probing, but if we expect pre-Linux code
@@ -92,7 +91,8 @@ enum ds_type {
 #      define DS1337_BIT_A2I           0x02
 #      define DS1337_BIT_A1I           0x01
 #define DS1339_REG_ALARM1_SECS 0x07
-#define DS1339_REG_TRICKLE     0x10
+
+#define DS13XX_TRICKLE_CHARGER_MAGIC   0xa0
 
 #define RX8025_REG_CTRL1       0x0e
 #      define RX8025_BIT_2412          0x20
@@ -124,6 +124,7 @@ struct chip_desc {
        unsigned                alarm:1;
        u16                     nvram_offset;
        u16                     nvram_size;
+       u16                     trickle_charger_reg;
 };
 
 static const struct chip_desc chips[last_ds_type] = {
@@ -140,6 +141,13 @@ static const struct chip_desc chips[last_ds_type] = {
        },
        [ds_1339] = {
                .alarm          = 1,
+               .trickle_charger_reg = 0x10,
+       },
+       [ds_1340] = {
+               .trickle_charger_reg = 0x08,
+       },
+       [ds_1388] = {
+               .trickle_charger_reg = 0x0a,
        },
        [ds_3231] = {
                .alarm          = 1,
@@ -619,6 +627,7 @@ static int __devinit ds1307_probe(struct i2c_client *client,
        struct i2c_adapter      *adapter = to_i2c_adapter(client->dev.parent);
        int                     want_irq = false;
        unsigned char           *buf;
+       struct ds1307_platform_data *pdata = client->dev.platform_data;
        static const int        bbsqi_bitpos[] = {
                [ds_1337] = 0,
                [ds_1339] = DS1339_BIT_BBSQI,
@@ -637,7 +646,10 @@ static int __devinit ds1307_probe(struct i2c_client *client,
 
        ds1307->client  = client;
        ds1307->type    = id->driver_data;
-       ds1307->offset  = 0;
+
+       if (pdata && pdata->trickle_charger_setup && chip->trickle_charger_reg)
+               i2c_smbus_write_byte_data(client, chip->trickle_charger_reg,
+                       DS13XX_TRICKLE_CHARGER_MAGIC | pdata->trickle_charger_setup);
 
        buf = ds1307->regs;
        if (i2c_check_functionality(adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) {
index 14a42a1edc66d55be04a79c67aa622db7b10b38d..9602278ff988df3affbb58b03d88dd7908508447 100644 (file)
@@ -127,7 +127,7 @@ static const struct attribute_group ep93xx_rtc_sysfs_files = {
        .attrs  = ep93xx_rtc_attrs,
 };
 
-static int __init ep93xx_rtc_probe(struct platform_device *pdev)
+static int __devinit ep93xx_rtc_probe(struct platform_device *pdev)
 {
        struct ep93xx_rtc *ep93xx_rtc;
        struct resource *res;
@@ -174,7 +174,7 @@ exit:
        return err;
 }
 
-static int __exit ep93xx_rtc_remove(struct platform_device *pdev)
+static int __devexit ep93xx_rtc_remove(struct platform_device *pdev)
 {
        struct ep93xx_rtc *ep93xx_rtc = platform_get_drvdata(pdev);
 
@@ -186,31 +186,19 @@ static int __exit ep93xx_rtc_remove(struct platform_device *pdev)
        return 0;
 }
 
-/* work with hotplug and coldplug */
-MODULE_ALIAS("platform:ep93xx-rtc");
-
 static struct platform_driver ep93xx_rtc_driver = {
        .driver         = {
                .name   = "ep93xx-rtc",
                .owner  = THIS_MODULE,
        },
-       .remove         = __exit_p(ep93xx_rtc_remove),
+       .probe          = ep93xx_rtc_probe,
+       .remove         = __devexit_p(ep93xx_rtc_remove),
 };
 
-static int __init ep93xx_rtc_init(void)
-{
-        return platform_driver_probe(&ep93xx_rtc_driver, ep93xx_rtc_probe);
-}
-
-static void __exit ep93xx_rtc_exit(void)
-{
-       platform_driver_unregister(&ep93xx_rtc_driver);
-}
+module_platform_driver(ep93xx_rtc_driver);
 
 MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>");
 MODULE_DESCRIPTION("EP93XX RTC driver");
 MODULE_LICENSE("GPL");
 MODULE_VERSION(DRV_VERSION);
-
-module_init(ep93xx_rtc_init);
-module_exit(ep93xx_rtc_exit);
+MODULE_ALIAS("platform:ep93xx-rtc");
index 63c72189c64b38d76d7d1e31721f359f724d983d..d5218553741ff54f871799f51b5969d74eabe85c 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/rtc.h>
 #include <linux/slab.h>
 #include <linux/io.h>
+#include <linux/of.h>
 
 /*
  * Clock and Power control register offsets
@@ -386,13 +387,22 @@ static const struct dev_pm_ops lpc32xx_rtc_pm_ops = {
 #define LPC32XX_RTC_PM_OPS NULL
 #endif
 
+#ifdef CONFIG_OF
+static const struct of_device_id lpc32xx_rtc_match[] = {
+       { .compatible = "nxp,lpc3220-rtc" },
+       { }
+};
+MODULE_DEVICE_TABLE(of, lpc32xx_rtc_match);
+#endif
+
 static struct platform_driver lpc32xx_rtc_driver = {
        .probe          = lpc32xx_rtc_probe,
        .remove         = __devexit_p(lpc32xx_rtc_remove),
        .driver = {
                .name   = RTC_NAME,
                .owner  = THIS_MODULE,
-               .pm     = LPC32XX_RTC_PM_OPS
+               .pm     = LPC32XX_RTC_PM_OPS,
+               .of_match_table = of_match_ptr(lpc32xx_rtc_match),
        },
 };
 
index 10f1c29436ec59946123e5f2830f83e44a6c2ebc..efab3d48cb153314e8fdbf600da18eb6289dd5fe 100644 (file)
@@ -48,6 +48,7 @@ static inline int m41t93_set_reg(struct spi_device *spi, u8 addr, u8 data)
 static int m41t93_set_time(struct device *dev, struct rtc_time *tm)
 {
        struct spi_device *spi = to_spi_device(dev);
+       int tmp;
        u8 buf[9] = {0x80};        /* write cmd + 8 data bytes */
        u8 * const data = &buf[1]; /* ptr to first data byte */
 
@@ -62,6 +63,30 @@ static int m41t93_set_time(struct device *dev, struct rtc_time *tm)
                return -EINVAL;
        }
 
+       tmp = spi_w8r8(spi, M41T93_REG_FLAGS);
+       if (tmp < 0)
+               return tmp;
+
+       if (tmp & M41T93_FLAG_OF) {
+               dev_warn(&spi->dev, "OF bit is set, resetting.\n");
+               m41t93_set_reg(spi, M41T93_REG_FLAGS, tmp & ~M41T93_FLAG_OF);
+
+               tmp = spi_w8r8(spi, M41T93_REG_FLAGS);
+               if (tmp < 0) {
+                       return tmp;
+               } else if (tmp & M41T93_FLAG_OF) {
+                       /* OF cannot be immediately reset: oscillator has to be
+                        * restarted. */
+                       u8 reset_osc = buf[M41T93_REG_ST_SEC] | M41T93_FLAG_ST;
+
+                       dev_warn(&spi->dev,
+                                "OF bit is still set, kickstarting clock.\n");
+                       m41t93_set_reg(spi, M41T93_REG_ST_SEC, reset_osc);
+                       reset_osc &= ~M41T93_FLAG_ST;
+                       m41t93_set_reg(spi, M41T93_REG_ST_SEC, reset_osc);
+               }
+       }
+
        data[M41T93_REG_SSEC]           = 0;
        data[M41T93_REG_ST_SEC]         = bin2bcd(tm->tm_sec);
        data[M41T93_REG_MIN]            = bin2bcd(tm->tm_min);
@@ -89,10 +114,7 @@ static int m41t93_get_time(struct device *dev, struct rtc_time *tm)
           1. halt bit (HT) is set: the clock is running but update of readout
              registers has been disabled due to power failure. This is normal
              case after poweron. Time is valid after resetting HT bit.
-          2. oscillator fail bit (OF) is set. Oscillator has be stopped and
-             time is invalid:
-             a) OF can be immeditely reset.
-             b) OF cannot be immediately reset: oscillator has to be restarted.
+          2. oscillator fail bit (OF) is set: time is invalid.
        */
        tmp = spi_w8r8(spi, M41T93_REG_ALM_HOUR_HT);
        if (tmp < 0)
@@ -110,21 +132,7 @@ static int m41t93_get_time(struct device *dev, struct rtc_time *tm)
 
        if (tmp & M41T93_FLAG_OF) {
                ret = -EINVAL;
-               dev_warn(&spi->dev, "OF bit is set, resetting.\n");
-               m41t93_set_reg(spi, M41T93_REG_FLAGS, tmp & ~M41T93_FLAG_OF);
-
-               tmp = spi_w8r8(spi, M41T93_REG_FLAGS);
-               if (tmp < 0)
-                       return tmp;
-               else if (tmp & M41T93_FLAG_OF) {
-                       u8 reset_osc = buf[M41T93_REG_ST_SEC] | M41T93_FLAG_ST;
-
-                       dev_warn(&spi->dev,
-                                "OF bit is still set, kickstarting clock.\n");
-                       m41t93_set_reg(spi, M41T93_REG_ST_SEC, reset_osc);
-                       reset_osc &= ~M41T93_FLAG_ST;
-                       m41t93_set_reg(spi, M41T93_REG_ST_SEC, reset_osc);
-               }
+               dev_warn(&spi->dev, "OF bit is set, write time to restart.\n");
        }
 
        if (tmp & M41T93_FLAG_BL)
index bc0677de1996d93a13e0bf37c080bbd30a49e403..97a3284bb7c60920be7204156fab79b2dabafec6 100644 (file)
@@ -64,6 +64,7 @@ struct pcf8563 {
         * 1970...2069.
         */
        int c_polarity; /* 0: MO_C=1 means 19xx, otherwise MO_C=1 means 20xx */
+       int voltage_low; /* incicates if a low_voltage was detected */
 };
 
 /*
@@ -86,9 +87,11 @@ static int pcf8563_get_datetime(struct i2c_client *client, struct rtc_time *tm)
                return -EIO;
        }
 
-       if (buf[PCF8563_REG_SC] & PCF8563_SC_LV)
+       if (buf[PCF8563_REG_SC] & PCF8563_SC_LV) {
+               pcf8563->voltage_low = 1;
                dev_info(&client->dev,
                        "low voltage detected, date/time is not reliable.\n");
+       }
 
        dev_dbg(&client->dev,
                "%s: raw data is st1=%02x, st2=%02x, sec=%02x, min=%02x, hr=%02x, "
@@ -173,6 +176,44 @@ static int pcf8563_set_datetime(struct i2c_client *client, struct rtc_time *tm)
        return 0;
 }
 
+#ifdef CONFIG_RTC_INTF_DEV
+static int pcf8563_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
+{
+       struct pcf8563 *pcf8563 = i2c_get_clientdata(to_i2c_client(dev));
+       struct rtc_time tm;
+
+       switch (cmd) {
+       case RTC_VL_READ:
+               if (pcf8563->voltage_low)
+                       dev_info(dev, "low voltage detected, date/time is not reliable.\n");
+
+               if (copy_to_user((void __user *)arg, &pcf8563->voltage_low,
+                                       sizeof(int)))
+                       return -EFAULT;
+               return 0;
+       case RTC_VL_CLR:
+               /*
+                * Clear the VL bit in the seconds register in case
+                * the time has not been set already (which would
+                * have cleared it). This does not really matter
+                * because of the cached voltage_low value but do it
+                * anyway for consistency.
+                */
+               if (pcf8563_get_datetime(to_i2c_client(dev), &tm))
+                       pcf8563_set_datetime(to_i2c_client(dev), &tm);
+
+               /* Clear the cached value. */
+               pcf8563->voltage_low = 0;
+
+               return 0;
+       default:
+               return -ENOIOCTLCMD;
+       }
+}
+#else
+#define pcf8563_rtc_ioctl NULL
+#endif
+
 static int pcf8563_rtc_read_time(struct device *dev, struct rtc_time *tm)
 {
        return pcf8563_get_datetime(to_i2c_client(dev), tm);
@@ -184,6 +225,7 @@ static int pcf8563_rtc_set_time(struct device *dev, struct rtc_time *tm)
 }
 
 static const struct rtc_class_ops pcf8563_rtc_ops = {
+       .ioctl          = pcf8563_rtc_ioctl,
        .read_time      = pcf8563_rtc_read_time,
        .set_time       = pcf8563_rtc_set_time,
 };
index f027c063fb20312a15ab3ed57d9949b18fb40f65..cc0533994f6e0650bbd7c2bc2a4976cfb947593e 100644 (file)
@@ -220,17 +220,9 @@ static irqreturn_t pl031_interrupt(int irq, void *dev_id)
        unsigned long events = 0;
 
        rtcmis = readl(ldata->base + RTC_MIS);
-       if (rtcmis) {
-               writel(rtcmis, ldata->base + RTC_ICR);
-
-               if (rtcmis & RTC_BIT_AI)
-                       events |= (RTC_AF | RTC_IRQF);
-
-               /* Timer interrupt is only available in ST variants */
-               if ((rtcmis & RTC_BIT_PI) &&
-                       (ldata->hw_designer == AMBA_VENDOR_ST))
-                       events |= (RTC_PF | RTC_IRQF);
-
+       if (rtcmis & RTC_BIT_AI) {
+               writel(RTC_BIT_AI, ldata->base + RTC_ICR);
+               events |= (RTC_AF | RTC_IRQF);
                rtc_update_irq(ldata->rtc, 1, events);
 
                return IRQ_HANDLED;
index 3f3a29752369b092c7b99cca4ff33232f6fa605b..7e6af0b22f17d0e3d54bad2e8981689e82be8e81 100644 (file)
@@ -670,6 +670,7 @@ static int s3c_rtc_resume(struct platform_device *pdev)
 #define s3c_rtc_resume  NULL
 #endif
 
+#ifdef CONFIG_OF
 static struct s3c_rtc_drv_data s3c_rtc_drv_data_array[] = {
        [TYPE_S3C2410] = { TYPE_S3C2410 },
        [TYPE_S3C2416] = { TYPE_S3C2416 },
@@ -677,7 +678,6 @@ static struct s3c_rtc_drv_data s3c_rtc_drv_data_array[] = {
        [TYPE_S3C64XX] = { TYPE_S3C64XX },
 };
 
-#ifdef CONFIG_OF
 static const struct of_device_id s3c_rtc_dt_match[] = {
        {
                .compatible = "samsung,s3c2410-rtc",
index e38da0dc41872070e4d0abe40da9280b6ddf747a..1f76320e545b1cf15d563cc8996b8eed7122f14a 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/io.h>
 #include <linux/irq.h>
 #include <linux/module.h>
+#include <linux/of.h>
 #include <linux/platform_device.h>
 #include <linux/rtc.h>
 #include <linux/slab.h>
@@ -519,6 +520,14 @@ static void spear_rtc_shutdown(struct platform_device *pdev)
        clk_disable(config->clk);
 }
 
+#ifdef CONFIG_OF
+static const struct of_device_id spear_rtc_id_table[] = {
+       { .compatible = "st,spear600-rtc" },
+       {}
+};
+MODULE_DEVICE_TABLE(of, spear_rtc_id_table);
+#endif
+
 static struct platform_driver spear_rtc_driver = {
        .probe = spear_rtc_probe,
        .remove = __devexit_p(spear_rtc_remove),
@@ -527,6 +536,7 @@ static struct platform_driver spear_rtc_driver = {
        .shutdown = spear_rtc_shutdown,
        .driver = {
                .name = "rtc-spear",
+               .of_match_table = of_match_ptr(spear_rtc_id_table),
        },
 };
 
index 75259fe38602af5952193d7a2a411e6eba661da9..c006025cecc809ac3b00d1abbe59a3f4e3527254 100644 (file)
@@ -309,7 +309,8 @@ static int __devinit tegra_rtc_probe(struct platform_device *pdev)
        struct resource *res;
        int ret;
 
-       info = kzalloc(sizeof(struct tegra_rtc_info), GFP_KERNEL);
+       info = devm_kzalloc(&pdev->dev, sizeof(struct tegra_rtc_info),
+               GFP_KERNEL);
        if (!info)
                return -ENOMEM;
 
@@ -317,29 +318,18 @@ static int __devinit tegra_rtc_probe(struct platform_device *pdev)
        if (!res) {
                dev_err(&pdev->dev,
                        "Unable to allocate resources for device.\n");
-               ret = -EBUSY;
-               goto err_free_info;
+               return -EBUSY;
        }
 
-       if (!request_mem_region(res->start, resource_size(res), pdev->name)) {
-               dev_err(&pdev->dev,
-                       "Unable to request mem region for device.\n");
-               ret = -EBUSY;
-               goto err_free_info;
+       info->rtc_base = devm_request_and_ioremap(&pdev->dev, res);
+       if (!info->rtc_base) {
+               dev_err(&pdev->dev, "Unable to request mem region and grab IOs for device.\n");
+               return -EBUSY;
        }
 
        info->tegra_rtc_irq = platform_get_irq(pdev, 0);
-       if (info->tegra_rtc_irq <= 0) {
-               ret = -EBUSY;
-               goto err_release_mem_region;
-       }
-
-       info->rtc_base = ioremap_nocache(res->start, resource_size(res));
-       if (!info->rtc_base) {
-               dev_err(&pdev->dev, "Unable to grab IOs for device.\n");
-               ret = -EBUSY;
-               goto err_release_mem_region;
-       }
+       if (info->tegra_rtc_irq <= 0)
+               return -EBUSY;
 
        /* set context info. */
        info->pdev = pdev;
@@ -362,11 +352,12 @@ static int __devinit tegra_rtc_probe(struct platform_device *pdev)
                dev_err(&pdev->dev,
                        "Unable to register device (err=%d).\n",
                        ret);
-               goto err_iounmap;
+               return ret;
        }
 
-       ret = request_irq(info->tegra_rtc_irq, tegra_rtc_irq_handler,
-               IRQF_TRIGGER_HIGH, "rtc alarm", &pdev->dev);
+       ret = devm_request_irq(&pdev->dev, info->tegra_rtc_irq,
+                       tegra_rtc_irq_handler, IRQF_TRIGGER_HIGH,
+                       "rtc alarm", &pdev->dev);
        if (ret) {
                dev_err(&pdev->dev,
                        "Unable to request interrupt for device (err=%d).\n",
@@ -380,12 +371,6 @@ static int __devinit tegra_rtc_probe(struct platform_device *pdev)
 
 err_dev_unreg:
        rtc_device_unregister(info->rtc_dev);
-err_iounmap:
-       iounmap(info->rtc_base);
-err_release_mem_region:
-       release_mem_region(res->start, resource_size(res));
-err_free_info:
-       kfree(info);
 
        return ret;
 }
@@ -393,17 +378,8 @@ err_free_info:
 static int __devexit tegra_rtc_remove(struct platform_device *pdev)
 {
        struct tegra_rtc_info *info = platform_get_drvdata(pdev);
-       struct resource *res;
-
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (!res)
-               return -EBUSY;
 
-       free_irq(info->tegra_rtc_irq, &pdev->dev);
        rtc_device_unregister(info->rtc_dev);
-       iounmap(info->rtc_base);
-       release_mem_region(res->start, resource_size(res));
-       kfree(info);
 
        platform_set_drvdata(pdev, NULL);
 
index 3b6e6a67e765b34e7efd7e5cb82eea63a930d31e..59c6245e0421f21a70548108a2607ebdaa4bbd33 100644 (file)
@@ -396,7 +396,7 @@ static int wm831x_rtc_probe(struct platform_device *pdev)
 {
        struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
        struct wm831x_rtc *wm831x_rtc;
-       int alm_irq = platform_get_irq_byname(pdev, "ALM");
+       int alm_irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "ALM"));
        int ret = 0;
 
        wm831x_rtc = devm_kzalloc(&pdev->dev, sizeof(*wm831x_rtc), GFP_KERNEL);
index 33a6743ddc558c11e9b07e09e230efb40e80da28..c05da00583f06c94f5fbb12bb110c7f5a60875c8 100644 (file)
@@ -10,8 +10,6 @@
 #ifndef DASD_INT_H
 #define DASD_INT_H
 
-#ifdef __KERNEL__
-
 /* we keep old device allocation scheme; IOW, minors are still in 0..255 */
 #define DASD_PER_MAJOR (1U << (MINORBITS - DASD_PARTN_BITS))
 #define DASD_PARTN_MASK ((1 << DASD_PARTN_BITS) - 1)
@@ -791,6 +789,4 @@ static inline int dasd_eer_enabled(struct dasd_device *device)
 #define dasd_eer_enabled(d)    (0)
 #endif /* CONFIG_DASD_ERR */
 
-#endif                         /* __KERNEL__ */
-
 #endif                         /* DASD_H */
index 69e6c50d4cfb25c341d0e115d2ad2d4a3034520a..50f7115990fffc3954cc99f24bdd06d1caa4fa7a 100644 (file)
@@ -211,7 +211,7 @@ int sclp_sdias_copy(void *dest, int start_blk, int nr_blks)
        sccb.evbuf.event_qual = EQ_STORE_DATA;
        sccb.evbuf.data_id = DI_FCP_DUMP;
        sccb.evbuf.event_id = 4712;
-#ifdef __s390x__
+#ifdef CONFIG_64BIT
        sccb.evbuf.asa_size = ASA_SIZE_64;
 #else
        sccb.evbuf.asa_size = ASA_SIZE_32;
index 01bb04cd9e7516a567b714fcd1f8a1896096aba9..2a096795b9aa86768ef18561872d948800eb6402 100644 (file)
@@ -571,13 +571,12 @@ free_cmd:
 static int mgmt_alloc_cmd_data(struct beiscsi_hba *phba, struct be_dma_mem *cmd,
                               int iscsi_cmd, int size)
 {
-       cmd->va = pci_alloc_consistent(phba->ctrl.pdev, sizeof(size),
-                                      &cmd->dma);
+       cmd->va = pci_alloc_consistent(phba->ctrl.pdev, size, &cmd->dma);
        if (!cmd->va) {
                SE_DEBUG(DBG_LVL_1, "Failed to allocate memory for if info\n");
                return -ENOMEM;
        }
-       memset(cmd->va, 0, sizeof(size));
+       memset(cmd->va, 0, size);
        cmd->size = size;
        be_cmd_hdr_prepare(cmd->va, CMD_SUBSYSTEM_ISCSI, iscsi_cmd, size);
        return 0;
index 8b6c6bf7837e7e09a0d6fec5d88733b3477a0d77..b83927440171810b4d6442c992c2c01a5db4558d 100644 (file)
@@ -426,6 +426,23 @@ bfad_im_vport_create(struct fc_vport *fc_vport, bool disable)
                vshost = vport->drv_port.im_port->shost;
                fc_host_node_name(vshost) = wwn_to_u64((u8 *)&port_cfg.nwwn);
                fc_host_port_name(vshost) = wwn_to_u64((u8 *)&port_cfg.pwwn);
+               fc_host_supported_classes(vshost) = FC_COS_CLASS3;
+
+               memset(fc_host_supported_fc4s(vshost), 0,
+                       sizeof(fc_host_supported_fc4s(vshost)));
+
+               /* For FCP type 0x08 */
+               if (supported_fc4s & BFA_LPORT_ROLE_FCP_IM)
+                       fc_host_supported_fc4s(vshost)[2] = 1;
+
+               /* For fibre channel services type 0x20 */
+               fc_host_supported_fc4s(vshost)[7] = 1;
+
+               fc_host_supported_speeds(vshost) =
+                               bfad_im_supported_speeds(&bfad->bfa);
+               fc_host_maxframe_size(vshost) =
+                               bfa_fcport_get_maxfrsize(&bfad->bfa);
+
                fc_vport->dd_data = vport;
                vport->drv_port.im_port->fc_vport = fc_vport;
        } else if (rc == BFA_STATUS_INVALID_WWN)
index 3153923f5b6027f1c16d806e14092e1df5356218..1ac09afe35ee17a6a23916e41ede98cd3f63a2ad 100644 (file)
@@ -987,7 +987,7 @@ done:
        return 0;
 }
 
-static u32
+u32
 bfad_im_supported_speeds(struct bfa_s *bfa)
 {
        struct bfa_ioc_attr_s *ioc_attr;
index 0814367ef101a1c075c0cfd4f5a52bc34dea920d..f6c1023e502a13cd04f19551d4893ec5f9014129 100644 (file)
@@ -37,6 +37,7 @@ int  bfad_im_scsi_host_alloc(struct bfad_s *bfad,
                struct bfad_im_port_s *im_port, struct device *dev);
 void bfad_im_scsi_host_free(struct bfad_s *bfad,
                                struct bfad_im_port_s *im_port);
+u32 bfad_im_supported_speeds(struct bfa_s *bfa);
 
 #define MAX_FCP_TARGET 1024
 #define MAX_FCP_LUN 16384
index a4953ef9e53accf67b5933e3bc1259511f31fa22..0578fa0dc14b73e6c26113d163759e1fe33b735d 100644 (file)
@@ -62,7 +62,7 @@
 #include "bnx2fc_constants.h"
 
 #define BNX2FC_NAME            "bnx2fc"
-#define BNX2FC_VERSION         "1.0.10"
+#define BNX2FC_VERSION         "1.0.11"
 
 #define PFX                    "bnx2fc: "
 
@@ -228,13 +228,16 @@ struct bnx2fc_interface {
        struct packet_type fip_packet_type;
        struct workqueue_struct *timer_work_queue;
        struct kref kref;
-       struct fcoe_ctlr ctlr;
        u8 vlan_enabled;
        int vlan_id;
        bool enabled;
 };
 
-#define bnx2fc_from_ctlr(fip) container_of(fip, struct bnx2fc_interface, ctlr)
+#define bnx2fc_from_ctlr(x)                    \
+       ((struct bnx2fc_interface *)((x) + 1))
+
+#define bnx2fc_to_ctlr(x)                                      \
+       ((struct fcoe_ctlr *)(((struct fcoe_ctlr *)(x)) - 1))
 
 struct bnx2fc_lport {
        struct list_head list;
index ce0ce3e32f336aaf711d6129a781c0aa035120fa..bdbbb13b8534c2318464b1b9a803c554af2b17ac 100644 (file)
@@ -854,7 +854,6 @@ static void bnx2fc_flogi_resp(struct fc_seq *seq, struct fc_frame *fp,
        struct fc_exch *exch = fc_seq_exch(seq);
        struct fc_lport *lport = exch->lp;
        u8 *mac;
-       struct fc_frame_header *fh;
        u8 op;
 
        if (IS_ERR(fp))
@@ -862,13 +861,6 @@ static void bnx2fc_flogi_resp(struct fc_seq *seq, struct fc_frame *fp,
 
        mac = fr_cb(fp)->granted_mac;
        if (is_zero_ether_addr(mac)) {
-               fh = fc_frame_header_get(fp);
-               if (fh->fh_type != FC_TYPE_ELS) {
-                       printk(KERN_ERR PFX "bnx2fc_flogi_resp:"
-                               "fh_type != FC_TYPE_ELS\n");
-                       fc_frame_free(fp);
-                       return;
-               }
                op = fc_frame_payload_op(fp);
                if (lport->vport) {
                        if (op == ELS_LS_RJT) {
@@ -878,12 +870,10 @@ static void bnx2fc_flogi_resp(struct fc_seq *seq, struct fc_frame *fp,
                                return;
                        }
                }
-               if (fcoe_ctlr_recv_flogi(fip, lport, fp)) {
-                       fc_frame_free(fp);
-                       return;
-               }
+               fcoe_ctlr_recv_flogi(fip, lport, fp);
        }
-       fip->update_mac(lport, mac);
+       if (!is_zero_ether_addr(mac))
+               fip->update_mac(lport, mac);
 done:
        fc_lport_flogi_resp(seq, fp, lport);
 }
@@ -910,7 +900,7 @@ struct fc_seq *bnx2fc_elsct_send(struct fc_lport *lport, u32 did,
 {
        struct fcoe_port *port = lport_priv(lport);
        struct bnx2fc_interface *interface = port->priv;
-       struct fcoe_ctlr *fip = &interface->ctlr;
+       struct fcoe_ctlr *fip = bnx2fc_to_ctlr(interface);
        struct fc_frame_header *fh = fc_frame_header_get(fp);
 
        switch (op) {
index c1c6a92a0b989737c9f8a15b81e8e24cd86b758e..f52f668fd247b5601e27ac9572c99b6832302ca8 100644 (file)
@@ -22,7 +22,7 @@ DEFINE_PER_CPU(struct bnx2fc_percpu_s, bnx2fc_percpu);
 
 #define DRV_MODULE_NAME                "bnx2fc"
 #define DRV_MODULE_VERSION     BNX2FC_VERSION
-#define DRV_MODULE_RELDATE     "Jan 22, 2011"
+#define DRV_MODULE_RELDATE     "Apr 24, 2012"
 
 
 static char version[] __devinitdata =
@@ -54,6 +54,7 @@ static struct cnic_ulp_ops bnx2fc_cnic_cb;
 static struct libfc_function_template bnx2fc_libfc_fcn_templ;
 static struct scsi_host_template bnx2fc_shost_template;
 static struct fc_function_template bnx2fc_transport_function;
+static struct fcoe_sysfs_function_template bnx2fc_fcoe_sysfs_templ;
 static struct fc_function_template bnx2fc_vport_xport_function;
 static int bnx2fc_create(struct net_device *netdev, enum fip_state fip_mode);
 static void __bnx2fc_destroy(struct bnx2fc_interface *interface);
@@ -88,6 +89,7 @@ static void bnx2fc_port_shutdown(struct fc_lport *lport);
 static void bnx2fc_stop(struct bnx2fc_interface *interface);
 static int __init bnx2fc_mod_init(void);
 static void __exit bnx2fc_mod_exit(void);
+static void bnx2fc_ctlr_get_lesb(struct fcoe_ctlr_device *ctlr_dev);
 
 unsigned int bnx2fc_debug_level;
 module_param_named(debug_logging, bnx2fc_debug_level, int, S_IRUGO|S_IWUSR);
@@ -118,6 +120,41 @@ static void bnx2fc_get_lesb(struct fc_lport *lport,
        __fcoe_get_lesb(lport, fc_lesb, netdev);
 }
 
+static void bnx2fc_ctlr_get_lesb(struct fcoe_ctlr_device *ctlr_dev)
+{
+       struct fcoe_ctlr *fip = fcoe_ctlr_device_priv(ctlr_dev);
+       struct net_device *netdev = bnx2fc_netdev(fip->lp);
+       struct fcoe_fc_els_lesb *fcoe_lesb;
+       struct fc_els_lesb fc_lesb;
+
+       __fcoe_get_lesb(fip->lp, &fc_lesb, netdev);
+       fcoe_lesb = (struct fcoe_fc_els_lesb *)(&fc_lesb);
+
+       ctlr_dev->lesb.lesb_link_fail =
+               ntohl(fcoe_lesb->lesb_link_fail);
+       ctlr_dev->lesb.lesb_vlink_fail =
+               ntohl(fcoe_lesb->lesb_vlink_fail);
+       ctlr_dev->lesb.lesb_miss_fka =
+               ntohl(fcoe_lesb->lesb_miss_fka);
+       ctlr_dev->lesb.lesb_symb_err =
+               ntohl(fcoe_lesb->lesb_symb_err);
+       ctlr_dev->lesb.lesb_err_block =
+               ntohl(fcoe_lesb->lesb_err_block);
+       ctlr_dev->lesb.lesb_fcs_error =
+               ntohl(fcoe_lesb->lesb_fcs_error);
+}
+EXPORT_SYMBOL(bnx2fc_ctlr_get_lesb);
+
+static void bnx2fc_fcf_get_vlan_id(struct fcoe_fcf_device *fcf_dev)
+{
+       struct fcoe_ctlr_device *ctlr_dev =
+               fcoe_fcf_dev_to_ctlr_dev(fcf_dev);
+       struct fcoe_ctlr *ctlr = fcoe_ctlr_device_priv(ctlr_dev);
+       struct bnx2fc_interface *fcoe = fcoe_ctlr_priv(ctlr);
+
+       fcf_dev->vlan_id = fcoe->vlan_id;
+}
+
 static void bnx2fc_clean_rx_queue(struct fc_lport *lp)
 {
        struct fcoe_percpu_s *bg;
@@ -244,6 +281,7 @@ static int bnx2fc_xmit(struct fc_lport *lport, struct fc_frame *fp)
        struct sk_buff          *skb;
        struct fc_frame_header  *fh;
        struct bnx2fc_interface *interface;
+       struct fcoe_ctlr        *ctlr;
        struct bnx2fc_hba *hba;
        struct fcoe_port        *port;
        struct fcoe_hdr         *hp;
@@ -256,6 +294,7 @@ static int bnx2fc_xmit(struct fc_lport *lport, struct fc_frame *fp)
 
        port = (struct fcoe_port *)lport_priv(lport);
        interface = port->priv;
+       ctlr = bnx2fc_to_ctlr(interface);
        hba = interface->hba;
 
        fh = fc_frame_header_get(fp);
@@ -268,12 +307,12 @@ static int bnx2fc_xmit(struct fc_lport *lport, struct fc_frame *fp)
        }
 
        if (unlikely(fh->fh_r_ctl == FC_RCTL_ELS_REQ)) {
-               if (!interface->ctlr.sel_fcf) {
+               if (!ctlr->sel_fcf) {
                        BNX2FC_HBA_DBG(lport, "FCF not selected yet!\n");
                        kfree_skb(skb);
                        return -EINVAL;
                }
-               if (fcoe_ctlr_els_send(&interface->ctlr, lport, skb))
+               if (fcoe_ctlr_els_send(ctlr, lport, skb))
                        return 0;
        }
 
@@ -346,14 +385,14 @@ static int bnx2fc_xmit(struct fc_lport *lport, struct fc_frame *fp)
        /* fill up mac and fcoe headers */
        eh = eth_hdr(skb);
        eh->h_proto = htons(ETH_P_FCOE);
-       if (interface->ctlr.map_dest)
+       if (ctlr->map_dest)
                fc_fcoe_set_mac(eh->h_dest, fh->fh_d_id);
        else
                /* insert GW address */
-               memcpy(eh->h_dest, interface->ctlr.dest_addr, ETH_ALEN);
+               memcpy(eh->h_dest, ctlr->dest_addr, ETH_ALEN);
 
-       if (unlikely(interface->ctlr.flogi_oxid != FC_XID_UNKNOWN))
-               memcpy(eh->h_source, interface->ctlr.ctl_src_addr, ETH_ALEN);
+       if (unlikely(ctlr->flogi_oxid != FC_XID_UNKNOWN))
+               memcpy(eh->h_source, ctlr->ctl_src_addr, ETH_ALEN);
        else
                memcpy(eh->h_source, port->data_src_addr, ETH_ALEN);
 
@@ -403,6 +442,7 @@ static int bnx2fc_rcv(struct sk_buff *skb, struct net_device *dev,
 {
        struct fc_lport *lport;
        struct bnx2fc_interface *interface;
+       struct fcoe_ctlr *ctlr;
        struct fc_frame_header *fh;
        struct fcoe_rcv_info *fr;
        struct fcoe_percpu_s *bg;
@@ -410,7 +450,8 @@ static int bnx2fc_rcv(struct sk_buff *skb, struct net_device *dev,
 
        interface = container_of(ptype, struct bnx2fc_interface,
                                 fcoe_packet_type);
-       lport = interface->ctlr.lp;
+       ctlr = bnx2fc_to_ctlr(interface);
+       lport = ctlr->lp;
 
        if (unlikely(lport == NULL)) {
                printk(KERN_ERR PFX "bnx2fc_rcv: lport is NULL\n");
@@ -758,11 +799,13 @@ static int bnx2fc_net_config(struct fc_lport *lport, struct net_device *netdev)
 {
        struct bnx2fc_hba *hba;
        struct bnx2fc_interface *interface;
+       struct fcoe_ctlr *ctlr;
        struct fcoe_port *port;
        u64 wwnn, wwpn;
 
        port = lport_priv(lport);
        interface = port->priv;
+       ctlr = bnx2fc_to_ctlr(interface);
        hba = interface->hba;
 
        /* require support for get_pauseparam ethtool op. */
@@ -781,13 +824,13 @@ static int bnx2fc_net_config(struct fc_lport *lport, struct net_device *netdev)
 
        if (!lport->vport) {
                if (fcoe_get_wwn(netdev, &wwnn, NETDEV_FCOE_WWNN))
-                       wwnn = fcoe_wwn_from_mac(interface->ctlr.ctl_src_addr,
+                       wwnn = fcoe_wwn_from_mac(ctlr->ctl_src_addr,
                                                 1, 0);
                BNX2FC_HBA_DBG(lport, "WWNN = 0x%llx\n", wwnn);
                fc_set_wwnn(lport, wwnn);
 
                if (fcoe_get_wwn(netdev, &wwpn, NETDEV_FCOE_WWPN))
-                       wwpn = fcoe_wwn_from_mac(interface->ctlr.ctl_src_addr,
+                       wwpn = fcoe_wwn_from_mac(ctlr->ctl_src_addr,
                                                 2, 0);
 
                BNX2FC_HBA_DBG(lport, "WWPN = 0x%llx\n", wwpn);
@@ -824,6 +867,7 @@ static void bnx2fc_indicate_netevent(void *context, unsigned long event,
        struct fc_lport *lport;
        struct fc_lport *vport;
        struct bnx2fc_interface *interface, *tmp;
+       struct fcoe_ctlr *ctlr;
        int wait_for_upload = 0;
        u32 link_possible = 1;
 
@@ -874,7 +918,8 @@ static void bnx2fc_indicate_netevent(void *context, unsigned long event,
                if (interface->hba != hba)
                        continue;
 
-               lport = interface->ctlr.lp;
+               ctlr = bnx2fc_to_ctlr(interface);
+               lport = ctlr->lp;
                BNX2FC_HBA_DBG(lport, "netevent handler - event=%s %ld\n",
                                interface->netdev->name, event);
 
@@ -889,8 +934,8 @@ static void bnx2fc_indicate_netevent(void *context, unsigned long event,
                         * on a stale vlan
                         */
                        if (interface->enabled)
-                               fcoe_ctlr_link_up(&interface->ctlr);
-               } else if (fcoe_ctlr_link_down(&interface->ctlr)) {
+                               fcoe_ctlr_link_up(ctlr);
+               } else if (fcoe_ctlr_link_down(ctlr)) {
                        mutex_lock(&lport->lp_mutex);
                        list_for_each_entry(vport, &lport->vports, list)
                                fc_host_port_type(vport->host) =
@@ -995,9 +1040,11 @@ static int bnx2fc_fip_recv(struct sk_buff *skb, struct net_device *dev,
                           struct net_device *orig_dev)
 {
        struct bnx2fc_interface *interface;
+       struct fcoe_ctlr *ctlr;
        interface = container_of(ptype, struct bnx2fc_interface,
                                 fip_packet_type);
-       fcoe_ctlr_recv(&interface->ctlr, skb);
+       ctlr = bnx2fc_to_ctlr(interface);
+       fcoe_ctlr_recv(ctlr, skb);
        return 0;
 }
 
@@ -1155,6 +1202,7 @@ static int bnx2fc_interface_setup(struct bnx2fc_interface *interface)
 {
        struct net_device *netdev = interface->netdev;
        struct net_device *physdev = interface->hba->phys_dev;
+       struct fcoe_ctlr *ctlr = bnx2fc_to_ctlr(interface);
        struct netdev_hw_addr *ha;
        int sel_san_mac = 0;
 
@@ -1169,7 +1217,7 @@ static int bnx2fc_interface_setup(struct bnx2fc_interface *interface)
 
                if ((ha->type == NETDEV_HW_ADDR_T_SAN) &&
                    (is_valid_ether_addr(ha->addr))) {
-                       memcpy(interface->ctlr.ctl_src_addr, ha->addr,
+                       memcpy(ctlr->ctl_src_addr, ha->addr,
                               ETH_ALEN);
                        sel_san_mac = 1;
                        BNX2FC_MISC_DBG("Found SAN MAC\n");
@@ -1224,19 +1272,23 @@ static void bnx2fc_release_transport(void)
 
 static void bnx2fc_interface_release(struct kref *kref)
 {
+       struct fcoe_ctlr_device *ctlr_dev;
        struct bnx2fc_interface *interface;
+       struct fcoe_ctlr *ctlr;
        struct net_device *netdev;
 
        interface = container_of(kref, struct bnx2fc_interface, kref);
        BNX2FC_MISC_DBG("Interface is being released\n");
 
+       ctlr = bnx2fc_to_ctlr(interface);
+       ctlr_dev = fcoe_ctlr_to_ctlr_dev(ctlr);
        netdev = interface->netdev;
 
        /* tear-down FIP controller */
        if (test_and_clear_bit(BNX2FC_CTLR_INIT_DONE, &interface->if_flags))
-               fcoe_ctlr_destroy(&interface->ctlr);
+               fcoe_ctlr_destroy(ctlr);
 
-       kfree(interface);
+       fcoe_ctlr_device_delete(ctlr_dev);
 
        dev_put(netdev);
        module_put(THIS_MODULE);
@@ -1329,33 +1381,40 @@ struct bnx2fc_interface *bnx2fc_interface_create(struct bnx2fc_hba *hba,
                                      struct net_device *netdev,
                                      enum fip_state fip_mode)
 {
+       struct fcoe_ctlr_device *ctlr_dev;
        struct bnx2fc_interface *interface;
+       struct fcoe_ctlr *ctlr;
+       int size;
        int rc = 0;
 
-       interface = kzalloc(sizeof(*interface), GFP_KERNEL);
-       if (!interface) {
+       size = (sizeof(*interface) + sizeof(struct fcoe_ctlr));
+       ctlr_dev = fcoe_ctlr_device_add(&netdev->dev, &bnx2fc_fcoe_sysfs_templ,
+                                        size);
+       if (!ctlr_dev) {
                printk(KERN_ERR PFX "Unable to allocate interface structure\n");
                return NULL;
        }
+       ctlr = fcoe_ctlr_device_priv(ctlr_dev);
+       interface = fcoe_ctlr_priv(ctlr);
        dev_hold(netdev);
        kref_init(&interface->kref);
        interface->hba = hba;
        interface->netdev = netdev;
 
        /* Initialize FIP */
-       fcoe_ctlr_init(&interface->ctlr, fip_mode);
-       interface->ctlr.send = bnx2fc_fip_send;
-       interface->ctlr.update_mac = bnx2fc_update_src_mac;
-       interface->ctlr.get_src_addr = bnx2fc_get_src_mac;
+       fcoe_ctlr_init(ctlr, fip_mode);
+       ctlr->send = bnx2fc_fip_send;
+       ctlr->update_mac = bnx2fc_update_src_mac;
+       ctlr->get_src_addr = bnx2fc_get_src_mac;
        set_bit(BNX2FC_CTLR_INIT_DONE, &interface->if_flags);
 
        rc = bnx2fc_interface_setup(interface);
        if (!rc)
                return interface;
 
-       fcoe_ctlr_destroy(&interface->ctlr);
+       fcoe_ctlr_destroy(ctlr);
        dev_put(netdev);
-       kfree(interface);
+       fcoe_ctlr_device_delete(ctlr_dev);
        return NULL;
 }
 
@@ -1373,6 +1432,7 @@ struct bnx2fc_interface *bnx2fc_interface_create(struct bnx2fc_hba *hba,
 static struct fc_lport *bnx2fc_if_create(struct bnx2fc_interface *interface,
                                  struct device *parent, int npiv)
 {
+       struct fcoe_ctlr        *ctlr = bnx2fc_to_ctlr(interface);
        struct fc_lport         *lport, *n_port;
        struct fcoe_port        *port;
        struct Scsi_Host        *shost;
@@ -1383,7 +1443,7 @@ static struct fc_lport *bnx2fc_if_create(struct bnx2fc_interface *interface,
 
        blport = kzalloc(sizeof(struct bnx2fc_lport), GFP_KERNEL);
        if (!blport) {
-               BNX2FC_HBA_DBG(interface->ctlr.lp, "Unable to alloc blport\n");
+               BNX2FC_HBA_DBG(ctlr->lp, "Unable to alloc blport\n");
                return NULL;
        }
 
@@ -1479,7 +1539,8 @@ static void bnx2fc_net_cleanup(struct bnx2fc_interface *interface)
 
 static void bnx2fc_interface_cleanup(struct bnx2fc_interface *interface)
 {
-       struct fc_lport *lport = interface->ctlr.lp;
+       struct fcoe_ctlr *ctlr = bnx2fc_to_ctlr(interface);
+       struct fc_lport *lport = ctlr->lp;
        struct fcoe_port *port = lport_priv(lport);
        struct bnx2fc_hba *hba = interface->hba;
 
@@ -1519,7 +1580,8 @@ static void bnx2fc_if_destroy(struct fc_lport *lport)
 
 static void __bnx2fc_destroy(struct bnx2fc_interface *interface)
 {
-       struct fc_lport *lport = interface->ctlr.lp;
+       struct fcoe_ctlr *ctlr = bnx2fc_to_ctlr(interface);
+       struct fc_lport *lport = ctlr->lp;
        struct fcoe_port *port = lport_priv(lport);
 
        bnx2fc_interface_cleanup(interface);
@@ -1543,13 +1605,15 @@ static int bnx2fc_destroy(struct net_device *netdev)
 {
        struct bnx2fc_interface *interface = NULL;
        struct workqueue_struct *timer_work_queue;
+       struct fcoe_ctlr *ctlr;
        int rc = 0;
 
        rtnl_lock();
        mutex_lock(&bnx2fc_dev_lock);
 
        interface = bnx2fc_interface_lookup(netdev);
-       if (!interface || !interface->ctlr.lp) {
+       ctlr = bnx2fc_to_ctlr(interface);
+       if (!interface || !ctlr->lp) {
                rc = -ENODEV;
                printk(KERN_ERR PFX "bnx2fc_destroy: interface or lport not found\n");
                goto netdev_err;
@@ -1646,6 +1710,7 @@ static void bnx2fc_ulp_start(void *handle)
 {
        struct bnx2fc_hba *hba = handle;
        struct bnx2fc_interface *interface;
+       struct fcoe_ctlr *ctlr;
        struct fc_lport *lport;
 
        mutex_lock(&bnx2fc_dev_lock);
@@ -1657,7 +1722,8 @@ static void bnx2fc_ulp_start(void *handle)
 
        list_for_each_entry(interface, &if_list, list) {
                if (interface->hba == hba) {
-                       lport = interface->ctlr.lp;
+                       ctlr = bnx2fc_to_ctlr(interface);
+                       lport = ctlr->lp;
                        /* Kick off Fabric discovery*/
                        printk(KERN_ERR PFX "ulp_init: start discovery\n");
                        lport->tt.frame_send = bnx2fc_xmit;
@@ -1677,13 +1743,14 @@ static void bnx2fc_port_shutdown(struct fc_lport *lport)
 
 static void bnx2fc_stop(struct bnx2fc_interface *interface)
 {
+       struct fcoe_ctlr *ctlr = bnx2fc_to_ctlr(interface);
        struct fc_lport *lport;
        struct fc_lport *vport;
 
        if (!test_bit(BNX2FC_FLAG_FW_INIT_DONE, &interface->hba->flags))
                return;
 
-       lport = interface->ctlr.lp;
+       lport = ctlr->lp;
        bnx2fc_port_shutdown(lport);
 
        mutex_lock(&lport->lp_mutex);
@@ -1692,7 +1759,7 @@ static void bnx2fc_stop(struct bnx2fc_interface *interface)
                                        FC_PORTTYPE_UNKNOWN;
        mutex_unlock(&lport->lp_mutex);
        fc_host_port_type(lport->host) = FC_PORTTYPE_UNKNOWN;
-       fcoe_ctlr_link_down(&interface->ctlr);
+       fcoe_ctlr_link_down(ctlr);
        fcoe_clean_pending_queue(lport);
 }
 
@@ -1804,6 +1871,7 @@ exit:
 
 static void bnx2fc_start_disc(struct bnx2fc_interface *interface)
 {
+       struct fcoe_ctlr *ctlr = bnx2fc_to_ctlr(interface);
        struct fc_lport *lport;
        int wait_cnt = 0;
 
@@ -1814,18 +1882,18 @@ static void bnx2fc_start_disc(struct bnx2fc_interface *interface)
                return;
        }
 
-       lport = interface->ctlr.lp;
+       lport = ctlr->lp;
        BNX2FC_HBA_DBG(lport, "calling fc_fabric_login\n");
 
        if (!bnx2fc_link_ok(lport) && interface->enabled) {
                BNX2FC_HBA_DBG(lport, "ctlr_link_up\n");
-               fcoe_ctlr_link_up(&interface->ctlr);
+               fcoe_ctlr_link_up(ctlr);
                fc_host_port_type(lport->host) = FC_PORTTYPE_NPORT;
                set_bit(ADAPTER_STATE_READY, &interface->hba->adapter_state);
        }
 
        /* wait for the FCF to be selected before issuing FLOGI */
-       while (!interface->ctlr.sel_fcf) {
+       while (!ctlr->sel_fcf) {
                msleep(250);
                /* give up after 3 secs */
                if (++wait_cnt > 12)
@@ -1889,19 +1957,21 @@ static void bnx2fc_ulp_init(struct cnic_dev *dev)
 static int bnx2fc_disable(struct net_device *netdev)
 {
        struct bnx2fc_interface *interface;
+       struct fcoe_ctlr *ctlr;
        int rc = 0;
 
        rtnl_lock();
        mutex_lock(&bnx2fc_dev_lock);
 
        interface = bnx2fc_interface_lookup(netdev);
-       if (!interface || !interface->ctlr.lp) {
+       ctlr = bnx2fc_to_ctlr(interface);
+       if (!interface || !ctlr->lp) {
                rc = -ENODEV;
                printk(KERN_ERR PFX "bnx2fc_disable: interface or lport not found\n");
        } else {
                interface->enabled = false;
-               fcoe_ctlr_link_down(&interface->ctlr);
-               fcoe_clean_pending_queue(interface->ctlr.lp);
+               fcoe_ctlr_link_down(ctlr);
+               fcoe_clean_pending_queue(ctlr->lp);
        }
 
        mutex_unlock(&bnx2fc_dev_lock);
@@ -1913,17 +1983,19 @@ static int bnx2fc_disable(struct net_device *netdev)
 static int bnx2fc_enable(struct net_device *netdev)
 {
        struct bnx2fc_interface *interface;
+       struct fcoe_ctlr *ctlr;
        int rc = 0;
 
        rtnl_lock();
        mutex_lock(&bnx2fc_dev_lock);
 
        interface = bnx2fc_interface_lookup(netdev);
-       if (!interface || !interface->ctlr.lp) {
+       ctlr = bnx2fc_to_ctlr(interface);
+       if (!interface || !ctlr->lp) {
                rc = -ENODEV;
                printk(KERN_ERR PFX "bnx2fc_enable: interface or lport not found\n");
-       } else if (!bnx2fc_link_ok(interface->ctlr.lp)) {
-               fcoe_ctlr_link_up(&interface->ctlr);
+       } else if (!bnx2fc_link_ok(ctlr->lp)) {
+               fcoe_ctlr_link_up(ctlr);
                interface->enabled = true;
        }
 
@@ -1944,6 +2016,7 @@ static int bnx2fc_enable(struct net_device *netdev)
  */
 static int bnx2fc_create(struct net_device *netdev, enum fip_state fip_mode)
 {
+       struct fcoe_ctlr *ctlr;
        struct bnx2fc_interface *interface;
        struct bnx2fc_hba *hba;
        struct net_device *phys_dev;
@@ -2010,6 +2083,7 @@ static int bnx2fc_create(struct net_device *netdev, enum fip_state fip_mode)
                goto ifput_err;
        }
 
+       ctlr = bnx2fc_to_ctlr(interface);
        interface->vlan_id = vlan_id;
        interface->vlan_enabled = 1;
 
@@ -2035,10 +2109,10 @@ static int bnx2fc_create(struct net_device *netdev, enum fip_state fip_mode)
        lport->boot_time = jiffies;
 
        /* Make this master N_port */
-       interface->ctlr.lp = lport;
+       ctlr->lp = lport;
 
        if (!bnx2fc_link_ok(lport)) {
-               fcoe_ctlr_link_up(&interface->ctlr);
+               fcoe_ctlr_link_up(ctlr);
                fc_host_port_type(lport->host) = FC_PORTTYPE_NPORT;
                set_bit(ADAPTER_STATE_READY, &interface->hba->adapter_state);
        }
@@ -2439,6 +2513,19 @@ static void __exit bnx2fc_mod_exit(void)
 module_init(bnx2fc_mod_init);
 module_exit(bnx2fc_mod_exit);
 
+static struct fcoe_sysfs_function_template bnx2fc_fcoe_sysfs_templ = {
+       .get_fcoe_ctlr_mode = fcoe_ctlr_get_fip_mode,
+       .get_fcoe_ctlr_link_fail = bnx2fc_ctlr_get_lesb,
+       .get_fcoe_ctlr_vlink_fail = bnx2fc_ctlr_get_lesb,
+       .get_fcoe_ctlr_miss_fka = bnx2fc_ctlr_get_lesb,
+       .get_fcoe_ctlr_symb_err = bnx2fc_ctlr_get_lesb,
+       .get_fcoe_ctlr_err_block = bnx2fc_ctlr_get_lesb,
+       .get_fcoe_ctlr_fcs_error = bnx2fc_ctlr_get_lesb,
+
+       .get_fcoe_fcf_selected = fcoe_fcf_get_selected,
+       .get_fcoe_fcf_vlan_id = bnx2fc_fcf_get_vlan_id,
+};
+
 static struct fc_function_template bnx2fc_transport_function = {
        .show_host_node_name = 1,
        .show_host_port_name = 1,
index afd570962b8c288636fad351671a376ad03992e7..2ca6bfe4ce5e38fe3a7be6b75cd39191c4a6ca2d 100644 (file)
@@ -167,6 +167,7 @@ int bnx2fc_send_session_ofld_req(struct fcoe_port *port,
 {
        struct fc_lport *lport = port->lport;
        struct bnx2fc_interface *interface = port->priv;
+       struct fcoe_ctlr *ctlr = bnx2fc_to_ctlr(interface);
        struct bnx2fc_hba *hba = interface->hba;
        struct kwqe *kwqe_arr[4];
        struct fcoe_kwqe_conn_offload1 ofld_req1;
@@ -314,13 +315,13 @@ int bnx2fc_send_session_ofld_req(struct fcoe_port *port,
        ofld_req4.src_mac_addr_mid[1] =  port->data_src_addr[2];
        ofld_req4.src_mac_addr_hi[0] =  port->data_src_addr[1];
        ofld_req4.src_mac_addr_hi[1] =  port->data_src_addr[0];
-       ofld_req4.dst_mac_addr_lo[0] =  interface->ctlr.dest_addr[5];
+       ofld_req4.dst_mac_addr_lo[0] =  ctlr->dest_addr[5];
                                                        /* fcf mac */
-       ofld_req4.dst_mac_addr_lo[1] =  interface->ctlr.dest_addr[4];
-       ofld_req4.dst_mac_addr_mid[0] =  interface->ctlr.dest_addr[3];
-       ofld_req4.dst_mac_addr_mid[1] =  interface->ctlr.dest_addr[2];
-       ofld_req4.dst_mac_addr_hi[0] =  interface->ctlr.dest_addr[1];
-       ofld_req4.dst_mac_addr_hi[1] =  interface->ctlr.dest_addr[0];
+       ofld_req4.dst_mac_addr_lo[1] = ctlr->dest_addr[4];
+       ofld_req4.dst_mac_addr_mid[0] = ctlr->dest_addr[3];
+       ofld_req4.dst_mac_addr_mid[1] = ctlr->dest_addr[2];
+       ofld_req4.dst_mac_addr_hi[0] = ctlr->dest_addr[1];
+       ofld_req4.dst_mac_addr_hi[1] = ctlr->dest_addr[0];
 
        ofld_req4.lcq_addr_lo = (u32) tgt->lcq_dma;
        ofld_req4.lcq_addr_hi = (u32)((u64) tgt->lcq_dma >> 32);
@@ -351,6 +352,7 @@ static int bnx2fc_send_session_enable_req(struct fcoe_port *port,
 {
        struct kwqe *kwqe_arr[2];
        struct bnx2fc_interface *interface = port->priv;
+       struct fcoe_ctlr *ctlr = bnx2fc_to_ctlr(interface);
        struct bnx2fc_hba *hba = interface->hba;
        struct fcoe_kwqe_conn_enable_disable enbl_req;
        struct fc_lport *lport = port->lport;
@@ -374,12 +376,12 @@ static int bnx2fc_send_session_enable_req(struct fcoe_port *port,
        enbl_req.src_mac_addr_hi[1] =  port->data_src_addr[0];
        memcpy(tgt->src_addr, port->data_src_addr, ETH_ALEN);
 
-       enbl_req.dst_mac_addr_lo[0] =  interface->ctlr.dest_addr[5];
-       enbl_req.dst_mac_addr_lo[1] =  interface->ctlr.dest_addr[4];
-       enbl_req.dst_mac_addr_mid[0] =  interface->ctlr.dest_addr[3];
-       enbl_req.dst_mac_addr_mid[1] =  interface->ctlr.dest_addr[2];
-       enbl_req.dst_mac_addr_hi[0] =  interface->ctlr.dest_addr[1];
-       enbl_req.dst_mac_addr_hi[1] =  interface->ctlr.dest_addr[0];
+       enbl_req.dst_mac_addr_lo[0] =  ctlr->dest_addr[5];
+       enbl_req.dst_mac_addr_lo[1] =  ctlr->dest_addr[4];
+       enbl_req.dst_mac_addr_mid[0] = ctlr->dest_addr[3];
+       enbl_req.dst_mac_addr_mid[1] = ctlr->dest_addr[2];
+       enbl_req.dst_mac_addr_hi[0] = ctlr->dest_addr[1];
+       enbl_req.dst_mac_addr_hi[1] = ctlr->dest_addr[0];
 
        port_id = fc_host_port_id(lport->host);
        if (port_id != tgt->sid) {
@@ -419,6 +421,7 @@ int bnx2fc_send_session_disable_req(struct fcoe_port *port,
                                    struct bnx2fc_rport *tgt)
 {
        struct bnx2fc_interface *interface = port->priv;
+       struct fcoe_ctlr *ctlr = bnx2fc_to_ctlr(interface);
        struct bnx2fc_hba *hba = interface->hba;
        struct fcoe_kwqe_conn_enable_disable disable_req;
        struct kwqe *kwqe_arr[2];
@@ -440,12 +443,12 @@ int bnx2fc_send_session_disable_req(struct fcoe_port *port,
        disable_req.src_mac_addr_hi[0] =  tgt->src_addr[1];
        disable_req.src_mac_addr_hi[1] =  tgt->src_addr[0];
 
-       disable_req.dst_mac_addr_lo[0] =  interface->ctlr.dest_addr[5];
-       disable_req.dst_mac_addr_lo[1] =  interface->ctlr.dest_addr[4];
-       disable_req.dst_mac_addr_mid[0] =  interface->ctlr.dest_addr[3];
-       disable_req.dst_mac_addr_mid[1] =  interface->ctlr.dest_addr[2];
-       disable_req.dst_mac_addr_hi[0] =  interface->ctlr.dest_addr[1];
-       disable_req.dst_mac_addr_hi[1] =  interface->ctlr.dest_addr[0];
+       disable_req.dst_mac_addr_lo[0] =  ctlr->dest_addr[5];
+       disable_req.dst_mac_addr_lo[1] =  ctlr->dest_addr[4];
+       disable_req.dst_mac_addr_mid[0] = ctlr->dest_addr[3];
+       disable_req.dst_mac_addr_mid[1] = ctlr->dest_addr[2];
+       disable_req.dst_mac_addr_hi[0] = ctlr->dest_addr[1];
+       disable_req.dst_mac_addr_hi[1] = ctlr->dest_addr[0];
 
        port_id = tgt->sid;
        disable_req.s_id[0] = (port_id & 0x000000FF);
index e897ce975bb8b15f7ae74f56d0028f1b0f6adb05..4f7453b9e41e2486b662d7fc3d8f55b1762fe154 100644 (file)
@@ -810,8 +810,22 @@ retry_tmf:
        spin_lock_bh(&tgt->tgt_lock);
 
        io_req->wait_for_comp = 0;
-       if (!(test_bit(BNX2FC_FLAG_TM_COMPL, &io_req->req_flags)))
+       if (!(test_bit(BNX2FC_FLAG_TM_COMPL, &io_req->req_flags))) {
                set_bit(BNX2FC_FLAG_TM_TIMEOUT, &io_req->req_flags);
+               if (io_req->on_tmf_queue) {
+                       list_del_init(&io_req->link);
+                       io_req->on_tmf_queue = 0;
+               }
+               io_req->wait_for_comp = 1;
+               bnx2fc_initiate_cleanup(io_req);
+               spin_unlock_bh(&tgt->tgt_lock);
+               rc = wait_for_completion_timeout(&io_req->tm_done,
+                                                BNX2FC_FW_TIMEOUT);
+               spin_lock_bh(&tgt->tgt_lock);
+               io_req->wait_for_comp = 0;
+               if (!rc)
+                       kref_put(&io_req->refcount, bnx2fc_cmd_release);
+       }
 
        spin_unlock_bh(&tgt->tgt_lock);
 
@@ -1089,6 +1103,48 @@ int bnx2fc_eh_device_reset(struct scsi_cmnd *sc_cmd)
        return bnx2fc_initiate_tmf(sc_cmd, FCP_TMF_LUN_RESET);
 }
 
+int bnx2fc_expl_logo(struct fc_lport *lport, struct bnx2fc_cmd *io_req)
+{
+       struct bnx2fc_rport *tgt = io_req->tgt;
+       struct fc_rport_priv *rdata = tgt->rdata;
+       int logo_issued;
+       int rc = SUCCESS;
+       int wait_cnt = 0;
+
+       BNX2FC_IO_DBG(io_req, "Expl logo - tgt flags = 0x%lx\n",
+                     tgt->flags);
+       logo_issued = test_and_set_bit(BNX2FC_FLAG_EXPL_LOGO,
+                                      &tgt->flags);
+       io_req->wait_for_comp = 1;
+       bnx2fc_initiate_cleanup(io_req);
+
+       spin_unlock_bh(&tgt->tgt_lock);
+
+       wait_for_completion(&io_req->tm_done);
+
+       io_req->wait_for_comp = 0;
+       /*
+        * release the reference taken in eh_abort to allow the
+        * target to re-login after flushing IOs
+        */
+        kref_put(&io_req->refcount, bnx2fc_cmd_release);
+
+       if (!logo_issued) {
+               clear_bit(BNX2FC_FLAG_SESSION_READY, &tgt->flags);
+               mutex_lock(&lport->disc.disc_mutex);
+               lport->tt.rport_logoff(rdata);
+               mutex_unlock(&lport->disc.disc_mutex);
+               do {
+                       msleep(BNX2FC_RELOGIN_WAIT_TIME);
+                       if (wait_cnt++ > BNX2FC_RELOGIN_WAIT_CNT) {
+                               rc = FAILED;
+                               break;
+                       }
+               } while (!test_bit(BNX2FC_FLAG_SESSION_READY, &tgt->flags));
+       }
+       spin_lock_bh(&tgt->tgt_lock);
+       return rc;
+}
 /**
  * bnx2fc_eh_abort - eh_abort_handler api to abort an outstanding
  *                     SCSI command
@@ -1103,10 +1159,7 @@ int bnx2fc_eh_abort(struct scsi_cmnd *sc_cmd)
        struct fc_rport_libfc_priv *rp = rport->dd_data;
        struct bnx2fc_cmd *io_req;
        struct fc_lport *lport;
-       struct fc_rport_priv *rdata;
        struct bnx2fc_rport *tgt;
-       int logo_issued;
-       int wait_cnt = 0;
        int rc = FAILED;
 
 
@@ -1183,58 +1236,31 @@ int bnx2fc_eh_abort(struct scsi_cmnd *sc_cmd)
        list_add_tail(&io_req->link, &tgt->io_retire_queue);
 
        init_completion(&io_req->tm_done);
-       io_req->wait_for_comp = 1;
 
-       if (!test_and_set_bit(BNX2FC_FLAG_ISSUE_ABTS, &io_req->req_flags)) {
-               /* Cancel the current timer running on this io_req */
-               if (cancel_delayed_work(&io_req->timeout_work))
-                       kref_put(&io_req->refcount,
-                                bnx2fc_cmd_release); /* drop timer hold */
-               set_bit(BNX2FC_FLAG_EH_ABORT, &io_req->req_flags);
-               rc = bnx2fc_initiate_abts(io_req);
-       } else {
+       if (test_and_set_bit(BNX2FC_FLAG_ISSUE_ABTS, &io_req->req_flags)) {
                printk(KERN_ERR PFX "eh_abort: io_req (xid = 0x%x) "
                                "already in abts processing\n", io_req->xid);
                if (cancel_delayed_work(&io_req->timeout_work))
                        kref_put(&io_req->refcount,
                                 bnx2fc_cmd_release); /* drop timer hold */
-               bnx2fc_initiate_cleanup(io_req);
+               rc = bnx2fc_expl_logo(lport, io_req);
+               goto out;
+       }
 
+       /* Cancel the current timer running on this io_req */
+       if (cancel_delayed_work(&io_req->timeout_work))
+               kref_put(&io_req->refcount,
+                        bnx2fc_cmd_release); /* drop timer hold */
+       set_bit(BNX2FC_FLAG_EH_ABORT, &io_req->req_flags);
+       io_req->wait_for_comp = 1;
+       rc = bnx2fc_initiate_abts(io_req);
+       if (rc == FAILED) {
+               bnx2fc_initiate_cleanup(io_req);
                spin_unlock_bh(&tgt->tgt_lock);
-
                wait_for_completion(&io_req->tm_done);
-
                spin_lock_bh(&tgt->tgt_lock);
                io_req->wait_for_comp = 0;
-               rdata = io_req->tgt->rdata;
-               logo_issued = test_and_set_bit(BNX2FC_FLAG_EXPL_LOGO,
-                                              &tgt->flags);
-               kref_put(&io_req->refcount, bnx2fc_cmd_release);
-               spin_unlock_bh(&tgt->tgt_lock);
-
-               if (!logo_issued) {
-                       BNX2FC_IO_DBG(io_req, "Expl logo - tgt flags = 0x%lx\n",
-                                     tgt->flags);
-                       mutex_lock(&lport->disc.disc_mutex);
-                       lport->tt.rport_logoff(rdata);
-                       mutex_unlock(&lport->disc.disc_mutex);
-                       do {
-                               msleep(BNX2FC_RELOGIN_WAIT_TIME);
-                               /*
-                                * If session not recovered, let SCSI-ml
-                                * escalate error recovery.
-                                */
-                               if (wait_cnt++ > BNX2FC_RELOGIN_WAIT_CNT)
-                                       return FAILED;
-                       } while (!test_bit(BNX2FC_FLAG_SESSION_READY,
-                                          &tgt->flags));
-               }
-               return SUCCESS;
-       }
-       if (rc == FAILED) {
-               kref_put(&io_req->refcount, bnx2fc_cmd_release);
-               spin_unlock_bh(&tgt->tgt_lock);
-               return rc;
+               goto done;
        }
        spin_unlock_bh(&tgt->tgt_lock);
 
@@ -1247,7 +1273,8 @@ int bnx2fc_eh_abort(struct scsi_cmnd *sc_cmd)
                /* Let the scsi-ml try to recover this command */
                printk(KERN_ERR PFX "abort failed, xid = 0x%x\n",
                       io_req->xid);
-               rc = FAILED;
+               rc = bnx2fc_expl_logo(lport, io_req);
+               goto out;
        } else {
                /*
                 * We come here even when there was a race condition
@@ -1259,9 +1286,10 @@ int bnx2fc_eh_abort(struct scsi_cmnd *sc_cmd)
                bnx2fc_scsi_done(io_req, DID_ABORT);
                kref_put(&io_req->refcount, bnx2fc_cmd_release);
        }
-
+done:
        /* release the reference taken in eh_abort */
        kref_put(&io_req->refcount, bnx2fc_cmd_release);
+out:
        spin_unlock_bh(&tgt->tgt_lock);
        return rc;
 }
index c1800b5312708a914cf424319dd7376fbb008186..082a25c3117e58cf961c383803e743ac92b7da24 100644 (file)
@@ -185,6 +185,16 @@ void bnx2fc_flush_active_ios(struct bnx2fc_rport *tgt)
                BUG_ON(rc);
        }
 
+       list_for_each_safe(list, tmp, &tgt->active_tm_queue) {
+               i++;
+               io_req = (struct bnx2fc_cmd *)list;
+               list_del_init(&io_req->link);
+               io_req->on_tmf_queue = 0;
+               BNX2FC_IO_DBG(io_req, "tm_queue cleanup\n");
+               if (io_req->wait_for_comp)
+                       complete(&io_req->tm_done);
+       }
+
        list_for_each_safe(list, tmp, &tgt->els_queue) {
                i++;
                io_req = (struct bnx2fc_cmd *)list;
@@ -213,8 +223,17 @@ void bnx2fc_flush_active_ios(struct bnx2fc_rport *tgt)
 
                BNX2FC_IO_DBG(io_req, "retire_queue flush\n");
 
-               if (cancel_delayed_work(&io_req->timeout_work))
+               if (cancel_delayed_work(&io_req->timeout_work)) {
+                       if (test_and_clear_bit(BNX2FC_FLAG_EH_ABORT,
+                                               &io_req->req_flags)) {
+                               /* Handle eh_abort timeout */
+                               BNX2FC_IO_DBG(io_req, "eh_abort for IO "
+                                             "in retire_q\n");
+                               if (io_req->wait_for_comp)
+                                       complete(&io_req->tm_done);
+                       }
                        kref_put(&io_req->refcount, bnx2fc_cmd_release);
+               }
 
                clear_bit(BNX2FC_FLAG_ISSUE_RRQ, &io_req->req_flags);
        }
index f6d37d0271f73bd08538bc3028e388e0e569b9db..aed0f5db36684c67c8a01817cad83fa553f48cb2 100644 (file)
@@ -1,4 +1,4 @@
 obj-$(CONFIG_FCOE) += fcoe.o
 obj-$(CONFIG_LIBFCOE) += libfcoe.o
 
-libfcoe-objs := fcoe_ctlr.o fcoe_transport.o
+libfcoe-objs := fcoe_ctlr.o fcoe_transport.o fcoe_sysfs.o
index 76e3d0b5bfa676212156d800dd295b46cddfc1de..fe30b1b65e1d3ddc879823a79404efdcc60d4982 100644 (file)
@@ -41,6 +41,7 @@
 
 #include <scsi/fc/fc_encaps.h>
 #include <scsi/fc/fc_fip.h>
+#include <scsi/fc/fc_fcoe.h>
 
 #include <scsi/libfc.h>
 #include <scsi/fc_frame.h>
@@ -150,6 +151,21 @@ static int fcoe_vport_create(struct fc_vport *, bool disabled);
 static int fcoe_vport_disable(struct fc_vport *, bool disable);
 static void fcoe_set_vport_symbolic_name(struct fc_vport *);
 static void fcoe_set_port_id(struct fc_lport *, u32, struct fc_frame *);
+static void fcoe_ctlr_get_lesb(struct fcoe_ctlr_device *);
+static void fcoe_fcf_get_vlan_id(struct fcoe_fcf_device *);
+
+static struct fcoe_sysfs_function_template fcoe_sysfs_templ = {
+       .get_fcoe_ctlr_mode = fcoe_ctlr_get_fip_mode,
+       .get_fcoe_ctlr_link_fail = fcoe_ctlr_get_lesb,
+       .get_fcoe_ctlr_vlink_fail = fcoe_ctlr_get_lesb,
+       .get_fcoe_ctlr_miss_fka = fcoe_ctlr_get_lesb,
+       .get_fcoe_ctlr_symb_err = fcoe_ctlr_get_lesb,
+       .get_fcoe_ctlr_err_block = fcoe_ctlr_get_lesb,
+       .get_fcoe_ctlr_fcs_error = fcoe_ctlr_get_lesb,
+
+       .get_fcoe_fcf_selected = fcoe_fcf_get_selected,
+       .get_fcoe_fcf_vlan_id = fcoe_fcf_get_vlan_id,
+};
 
 static struct libfc_function_template fcoe_libfc_fcn_templ = {
        .frame_send = fcoe_xmit,
@@ -282,7 +298,7 @@ static struct scsi_host_template fcoe_shost_template = {
 static int fcoe_interface_setup(struct fcoe_interface *fcoe,
                                struct net_device *netdev)
 {
-       struct fcoe_ctlr *fip = &fcoe->ctlr;
+       struct fcoe_ctlr *fip = fcoe_to_ctlr(fcoe);
        struct netdev_hw_addr *ha;
        struct net_device *real_dev;
        u8 flogi_maddr[ETH_ALEN];
@@ -366,7 +382,10 @@ static int fcoe_interface_setup(struct fcoe_interface *fcoe,
 static struct fcoe_interface *fcoe_interface_create(struct net_device *netdev,
                                                    enum fip_state fip_mode)
 {
+       struct fcoe_ctlr_device *ctlr_dev;
+       struct fcoe_ctlr *ctlr;
        struct fcoe_interface *fcoe;
+       int size;
        int err;
 
        if (!try_module_get(THIS_MODULE)) {
@@ -376,27 +395,32 @@ static struct fcoe_interface *fcoe_interface_create(struct net_device *netdev,
                goto out;
        }
 
-       fcoe = kzalloc(sizeof(*fcoe), GFP_KERNEL);
-       if (!fcoe) {
-               FCOE_NETDEV_DBG(netdev, "Could not allocate fcoe structure\n");
+       size = sizeof(struct fcoe_ctlr) + sizeof(struct fcoe_interface);
+       ctlr_dev = fcoe_ctlr_device_add(&netdev->dev, &fcoe_sysfs_templ,
+                                       size);
+       if (!ctlr_dev) {
+               FCOE_DBG("Failed to add fcoe_ctlr_device\n");
                fcoe = ERR_PTR(-ENOMEM);
                goto out_putmod;
        }
 
+       ctlr = fcoe_ctlr_device_priv(ctlr_dev);
+       fcoe = fcoe_ctlr_priv(ctlr);
+
        dev_hold(netdev);
 
        /*
         * Initialize FIP.
         */
-       fcoe_ctlr_init(&fcoe->ctlr, fip_mode);
-       fcoe->ctlr.send = fcoe_fip_send;
-       fcoe->ctlr.update_mac = fcoe_update_src_mac;
-       fcoe->ctlr.get_src_addr = fcoe_get_src_mac;
+       fcoe_ctlr_init(ctlr, fip_mode);
+       ctlr->send = fcoe_fip_send;
+       ctlr->update_mac = fcoe_update_src_mac;
+       ctlr->get_src_addr = fcoe_get_src_mac;
 
        err = fcoe_interface_setup(fcoe, netdev);
        if (err) {
-               fcoe_ctlr_destroy(&fcoe->ctlr);
-               kfree(fcoe);
+               fcoe_ctlr_destroy(ctlr);
+               fcoe_ctlr_device_delete(ctlr_dev);
                dev_put(netdev);
                fcoe = ERR_PTR(err);
                goto out_putmod;
@@ -419,7 +443,7 @@ out:
 static void fcoe_interface_remove(struct fcoe_interface *fcoe)
 {
        struct net_device *netdev = fcoe->netdev;
-       struct fcoe_ctlr *fip = &fcoe->ctlr;
+       struct fcoe_ctlr *fip = fcoe_to_ctlr(fcoe);
        u8 flogi_maddr[ETH_ALEN];
        const struct net_device_ops *ops;
 
@@ -462,7 +486,8 @@ static void fcoe_interface_remove(struct fcoe_interface *fcoe)
 static void fcoe_interface_cleanup(struct fcoe_interface *fcoe)
 {
        struct net_device *netdev = fcoe->netdev;
-       struct fcoe_ctlr *fip = &fcoe->ctlr;
+       struct fcoe_ctlr *fip = fcoe_to_ctlr(fcoe);
+       struct fcoe_ctlr_device *ctlr_dev = fcoe_ctlr_to_ctlr_dev(fip);
 
        rtnl_lock();
        if (!fcoe->removed)
@@ -472,8 +497,8 @@ static void fcoe_interface_cleanup(struct fcoe_interface *fcoe)
        /* Release the self-reference taken during fcoe_interface_create() */
        /* tear-down the FCoE controller */
        fcoe_ctlr_destroy(fip);
-       scsi_host_put(fcoe->ctlr.lp->host);
-       kfree(fcoe);
+       scsi_host_put(fip->lp->host);
+       fcoe_ctlr_device_delete(ctlr_dev);
        dev_put(netdev);
        module_put(THIS_MODULE);
 }
@@ -493,9 +518,11 @@ static int fcoe_fip_recv(struct sk_buff *skb, struct net_device *netdev,
                         struct net_device *orig_dev)
 {
        struct fcoe_interface *fcoe;
+       struct fcoe_ctlr *ctlr;
 
        fcoe = container_of(ptype, struct fcoe_interface, fip_packet_type);
-       fcoe_ctlr_recv(&fcoe->ctlr, skb);
+       ctlr = fcoe_to_ctlr(fcoe);
+       fcoe_ctlr_recv(ctlr, skb);
        return 0;
 }
 
@@ -645,11 +672,13 @@ static int fcoe_netdev_config(struct fc_lport *lport, struct net_device *netdev)
        u32 mfs;
        u64 wwnn, wwpn;
        struct fcoe_interface *fcoe;
+       struct fcoe_ctlr *ctlr;
        struct fcoe_port *port;
 
        /* Setup lport private data to point to fcoe softc */
        port = lport_priv(lport);
        fcoe = port->priv;
+       ctlr = fcoe_to_ctlr(fcoe);
 
        /*
         * Determine max frame size based on underlying device and optional
@@ -676,10 +705,10 @@ static int fcoe_netdev_config(struct fc_lport *lport, struct net_device *netdev)
 
        if (!lport->vport) {
                if (fcoe_get_wwn(netdev, &wwnn, NETDEV_FCOE_WWNN))
-                       wwnn = fcoe_wwn_from_mac(fcoe->ctlr.ctl_src_addr, 1, 0);
+                       wwnn = fcoe_wwn_from_mac(ctlr->ctl_src_addr, 1, 0);
                fc_set_wwnn(lport, wwnn);
                if (fcoe_get_wwn(netdev, &wwpn, NETDEV_FCOE_WWPN))
-                       wwpn = fcoe_wwn_from_mac(fcoe->ctlr.ctl_src_addr,
+                       wwpn = fcoe_wwn_from_mac(ctlr->ctl_src_addr,
                                                 2, 0);
                fc_set_wwpn(lport, wwpn);
        }
@@ -1056,6 +1085,7 @@ static int fcoe_ddp_done(struct fc_lport *lport, u16 xid)
 static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe,
                                       struct device *parent, int npiv)
 {
+       struct fcoe_ctlr *ctlr = fcoe_to_ctlr(fcoe);
        struct net_device *netdev = fcoe->netdev;
        struct fc_lport *lport, *n_port;
        struct fcoe_port *port;
@@ -1119,7 +1149,7 @@ static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe,
        }
 
        /* Initialize the library */
-       rc = fcoe_libfc_config(lport, &fcoe->ctlr, &fcoe_libfc_fcn_templ, 1);
+       rc = fcoe_libfc_config(lport, ctlr, &fcoe_libfc_fcn_templ, 1);
        if (rc) {
                FCOE_NETDEV_DBG(netdev, "Could not configure libfc for the "
                                "interface\n");
@@ -1386,6 +1416,7 @@ static int fcoe_rcv(struct sk_buff *skb, struct net_device *netdev,
 {
        struct fc_lport *lport;
        struct fcoe_rcv_info *fr;
+       struct fcoe_ctlr *ctlr;
        struct fcoe_interface *fcoe;
        struct fc_frame_header *fh;
        struct fcoe_percpu_s *fps;
@@ -1393,7 +1424,8 @@ static int fcoe_rcv(struct sk_buff *skb, struct net_device *netdev,
        unsigned int cpu;
 
        fcoe = container_of(ptype, struct fcoe_interface, fcoe_packet_type);
-       lport = fcoe->ctlr.lp;
+       ctlr = fcoe_to_ctlr(fcoe);
+       lport = ctlr->lp;
        if (unlikely(!lport)) {
                FCOE_NETDEV_DBG(netdev, "Cannot find hba structure");
                goto err2;
@@ -1409,8 +1441,8 @@ static int fcoe_rcv(struct sk_buff *skb, struct net_device *netdev,
 
        eh = eth_hdr(skb);
 
-       if (is_fip_mode(&fcoe->ctlr) &&
-           compare_ether_addr(eh->h_source, fcoe->ctlr.dest_addr)) {
+       if (is_fip_mode(ctlr) &&
+           compare_ether_addr(eh->h_source, ctlr->dest_addr)) {
                FCOE_NETDEV_DBG(netdev, "wrong source mac address:%pM\n",
                                eh->h_source);
                goto err;
@@ -1544,6 +1576,7 @@ static int fcoe_xmit(struct fc_lport *lport, struct fc_frame *fp)
        unsigned int elen;              /* eth header, may include vlan */
        struct fcoe_port *port = lport_priv(lport);
        struct fcoe_interface *fcoe = port->priv;
+       struct fcoe_ctlr *ctlr = fcoe_to_ctlr(fcoe);
        u8 sof, eof;
        struct fcoe_hdr *hp;
 
@@ -1559,7 +1592,7 @@ static int fcoe_xmit(struct fc_lport *lport, struct fc_frame *fp)
        }
 
        if (unlikely(fh->fh_type == FC_TYPE_ELS) &&
-           fcoe_ctlr_els_send(&fcoe->ctlr, lport, skb))
+           fcoe_ctlr_els_send(ctlr, lport, skb))
                return 0;
 
        sof = fr_sof(fp);
@@ -1623,12 +1656,12 @@ static int fcoe_xmit(struct fc_lport *lport, struct fc_frame *fp)
        /* fill up mac and fcoe headers */
        eh = eth_hdr(skb);
        eh->h_proto = htons(ETH_P_FCOE);
-       memcpy(eh->h_dest, fcoe->ctlr.dest_addr, ETH_ALEN);
-       if (fcoe->ctlr.map_dest)
+       memcpy(eh->h_dest, ctlr->dest_addr, ETH_ALEN);
+       if (ctlr->map_dest)
                memcpy(eh->h_dest + 3, fh->fh_d_id, 3);
 
-       if (unlikely(fcoe->ctlr.flogi_oxid != FC_XID_UNKNOWN))
-               memcpy(eh->h_source, fcoe->ctlr.ctl_src_addr, ETH_ALEN);
+       if (unlikely(ctlr->flogi_oxid != FC_XID_UNKNOWN))
+               memcpy(eh->h_source, ctlr->ctl_src_addr, ETH_ALEN);
        else
                memcpy(eh->h_source, port->data_src_addr, ETH_ALEN);
 
@@ -1677,6 +1710,7 @@ static void fcoe_percpu_flush_done(struct sk_buff *skb)
 static inline int fcoe_filter_frames(struct fc_lport *lport,
                                     struct fc_frame *fp)
 {
+       struct fcoe_ctlr *ctlr;
        struct fcoe_interface *fcoe;
        struct fc_frame_header *fh;
        struct sk_buff *skb = (struct sk_buff *)fp;
@@ -1698,7 +1732,8 @@ static inline int fcoe_filter_frames(struct fc_lport *lport,
                return 0;
 
        fcoe = ((struct fcoe_port *)lport_priv(lport))->priv;
-       if (is_fip_mode(&fcoe->ctlr) && fc_frame_payload_op(fp) == ELS_LOGO &&
+       ctlr = fcoe_to_ctlr(fcoe);
+       if (is_fip_mode(ctlr) && fc_frame_payload_op(fp) == ELS_LOGO &&
            ntoh24(fh->fh_s_id) == FC_FID_FLOGI) {
                FCOE_DBG("fcoe: dropping FCoE lport LOGO in fip mode\n");
                return -EINVAL;
@@ -1877,6 +1912,7 @@ static int fcoe_dcb_app_notification(struct notifier_block *notifier,
                                     ulong event, void *ptr)
 {
        struct dcb_app_type *entry = ptr;
+       struct fcoe_ctlr *ctlr;
        struct fcoe_interface *fcoe;
        struct net_device *netdev;
        struct fcoe_port *port;
@@ -1894,6 +1930,8 @@ static int fcoe_dcb_app_notification(struct notifier_block *notifier,
        if (!fcoe)
                return NOTIFY_OK;
 
+       ctlr = fcoe_to_ctlr(fcoe);
+
        if (entry->dcbx & DCB_CAP_DCBX_VER_CEE)
                prio = ffs(entry->app.priority) - 1;
        else
@@ -1904,10 +1942,10 @@ static int fcoe_dcb_app_notification(struct notifier_block *notifier,
 
        if (entry->app.protocol == ETH_P_FIP ||
            entry->app.protocol == ETH_P_FCOE)
-               fcoe->ctlr.priority = prio;
+               ctlr->priority = prio;
 
        if (entry->app.protocol == ETH_P_FCOE) {
-               port = lport_priv(fcoe->ctlr.lp);
+               port = lport_priv(ctlr->lp);
                port->priority = prio;
        }
 
@@ -1929,6 +1967,7 @@ static int fcoe_device_notification(struct notifier_block *notifier,
 {
        struct fc_lport *lport = NULL;
        struct net_device *netdev = ptr;
+       struct fcoe_ctlr *ctlr;
        struct fcoe_interface *fcoe;
        struct fcoe_port *port;
        struct fcoe_dev_stats *stats;
@@ -1938,7 +1977,8 @@ static int fcoe_device_notification(struct notifier_block *notifier,
 
        list_for_each_entry(fcoe, &fcoe_hostlist, list) {
                if (fcoe->netdev == netdev) {
-                       lport = fcoe->ctlr.lp;
+                       ctlr = fcoe_to_ctlr(fcoe);
+                       lport = ctlr->lp;
                        break;
                }
        }
@@ -1967,7 +2007,7 @@ static int fcoe_device_notification(struct notifier_block *notifier,
                break;
        case NETDEV_UNREGISTER:
                list_del(&fcoe->list);
-               port = lport_priv(fcoe->ctlr.lp);
+               port = lport_priv(ctlr->lp);
                queue_work(fcoe_wq, &port->destroy_work);
                goto out;
                break;
@@ -1982,8 +2022,8 @@ static int fcoe_device_notification(struct notifier_block *notifier,
        fcoe_link_speed_update(lport);
 
        if (link_possible && !fcoe_link_ok(lport))
-               fcoe_ctlr_link_up(&fcoe->ctlr);
-       else if (fcoe_ctlr_link_down(&fcoe->ctlr)) {
+               fcoe_ctlr_link_up(ctlr);
+       else if (fcoe_ctlr_link_down(ctlr)) {
                stats = per_cpu_ptr(lport->dev_stats, get_cpu());
                stats->LinkFailureCount++;
                put_cpu();
@@ -2003,6 +2043,7 @@ out:
  */
 static int fcoe_disable(struct net_device *netdev)
 {
+       struct fcoe_ctlr *ctlr;
        struct fcoe_interface *fcoe;
        int rc = 0;
 
@@ -2013,8 +2054,9 @@ static int fcoe_disable(struct net_device *netdev)
        rtnl_unlock();
 
        if (fcoe) {
-               fcoe_ctlr_link_down(&fcoe->ctlr);
-               fcoe_clean_pending_queue(fcoe->ctlr.lp);
+               ctlr = fcoe_to_ctlr(fcoe);
+               fcoe_ctlr_link_down(ctlr);
+               fcoe_clean_pending_queue(ctlr->lp);
        } else
                rc = -ENODEV;
 
@@ -2032,6 +2074,7 @@ static int fcoe_disable(struct net_device *netdev)
  */
 static int fcoe_enable(struct net_device *netdev)
 {
+       struct fcoe_ctlr *ctlr;
        struct fcoe_interface *fcoe;
        int rc = 0;
 
@@ -2040,11 +2083,17 @@ static int fcoe_enable(struct net_device *netdev)
        fcoe = fcoe_hostlist_lookup_port(netdev);
        rtnl_unlock();
 
-       if (!fcoe)
+       if (!fcoe) {
                rc = -ENODEV;
-       else if (!fcoe_link_ok(fcoe->ctlr.lp))
-               fcoe_ctlr_link_up(&fcoe->ctlr);
+               goto out;
+       }
+
+       ctlr = fcoe_to_ctlr(fcoe);
+
+       if (!fcoe_link_ok(ctlr->lp))
+               fcoe_ctlr_link_up(ctlr);
 
+out:
        mutex_unlock(&fcoe_config_mutex);
        return rc;
 }
@@ -2059,6 +2108,7 @@ static int fcoe_enable(struct net_device *netdev)
  */
 static int fcoe_destroy(struct net_device *netdev)
 {
+       struct fcoe_ctlr *ctlr;
        struct fcoe_interface *fcoe;
        struct fc_lport *lport;
        struct fcoe_port *port;
@@ -2071,7 +2121,8 @@ static int fcoe_destroy(struct net_device *netdev)
                rc = -ENODEV;
                goto out_nodev;
        }
-       lport = fcoe->ctlr.lp;
+       ctlr = fcoe_to_ctlr(fcoe);
+       lport = ctlr->lp;
        port = lport_priv(lport);
        list_del(&fcoe->list);
        queue_work(fcoe_wq, &port->destroy_work);
@@ -2126,7 +2177,8 @@ static void fcoe_dcb_create(struct fcoe_interface *fcoe)
        int dcbx;
        u8 fup, up;
        struct net_device *netdev = fcoe->realdev;
-       struct fcoe_port *port = lport_priv(fcoe->ctlr.lp);
+       struct fcoe_ctlr *ctlr = fcoe_to_ctlr(fcoe);
+       struct fcoe_port *port = lport_priv(ctlr->lp);
        struct dcb_app app = {
                                .priority = 0,
                                .protocol = ETH_P_FCOE
@@ -2149,7 +2201,7 @@ static void fcoe_dcb_create(struct fcoe_interface *fcoe)
                }
 
                port->priority = ffs(up) ? ffs(up) - 1 : 0;
-               fcoe->ctlr.priority = ffs(fup) ? ffs(fup) - 1 : port->priority;
+               ctlr->priority = ffs(fup) ? ffs(fup) - 1 : port->priority;
        }
 #endif
 }
@@ -2166,6 +2218,8 @@ static void fcoe_dcb_create(struct fcoe_interface *fcoe)
 static int fcoe_create(struct net_device *netdev, enum fip_state fip_mode)
 {
        int rc = 0;
+       struct fcoe_ctlr_device *ctlr_dev;
+       struct fcoe_ctlr *ctlr;
        struct fcoe_interface *fcoe;
        struct fc_lport *lport;
 
@@ -2184,7 +2238,9 @@ static int fcoe_create(struct net_device *netdev, enum fip_state fip_mode)
                goto out_nodev;
        }
 
-       lport = fcoe_if_create(fcoe, &netdev->dev, 0);
+       ctlr = fcoe_to_ctlr(fcoe);
+       ctlr_dev = fcoe_ctlr_to_ctlr_dev(ctlr);
+       lport = fcoe_if_create(fcoe, &ctlr_dev->dev, 0);
        if (IS_ERR(lport)) {
                printk(KERN_ERR "fcoe: Failed to create interface (%s)\n",
                       netdev->name);
@@ -2195,7 +2251,7 @@ static int fcoe_create(struct net_device *netdev, enum fip_state fip_mode)
        }
 
        /* Make this the "master" N_Port */
-       fcoe->ctlr.lp = lport;
+       ctlr->lp = lport;
 
        /* setup DCB priority attributes. */
        fcoe_dcb_create(fcoe);
@@ -2208,7 +2264,7 @@ static int fcoe_create(struct net_device *netdev, enum fip_state fip_mode)
        fc_fabric_login(lport);
        if (!fcoe_link_ok(lport)) {
                rtnl_unlock();
-               fcoe_ctlr_link_up(&fcoe->ctlr);
+               fcoe_ctlr_link_up(ctlr);
                mutex_unlock(&fcoe_config_mutex);
                return rc;
        }
@@ -2320,11 +2376,12 @@ static int fcoe_reset(struct Scsi_Host *shost)
        struct fc_lport *lport = shost_priv(shost);
        struct fcoe_port *port = lport_priv(lport);
        struct fcoe_interface *fcoe = port->priv;
+       struct fcoe_ctlr *ctlr = fcoe_to_ctlr(fcoe);
 
-       fcoe_ctlr_link_down(&fcoe->ctlr);
-       fcoe_clean_pending_queue(fcoe->ctlr.lp);
-       if (!fcoe_link_ok(fcoe->ctlr.lp))
-               fcoe_ctlr_link_up(&fcoe->ctlr);
+       fcoe_ctlr_link_down(ctlr);
+       fcoe_clean_pending_queue(ctlr->lp);
+       if (!fcoe_link_ok(ctlr->lp))
+               fcoe_ctlr_link_up(ctlr);
        return 0;
 }
 
@@ -2359,10 +2416,12 @@ fcoe_hostlist_lookup_port(const struct net_device *netdev)
  */
 static struct fc_lport *fcoe_hostlist_lookup(const struct net_device *netdev)
 {
+       struct fcoe_ctlr *ctlr;
        struct fcoe_interface *fcoe;
 
        fcoe = fcoe_hostlist_lookup_port(netdev);
-       return (fcoe) ? fcoe->ctlr.lp : NULL;
+       ctlr = fcoe_to_ctlr(fcoe);
+       return (fcoe) ? ctlr->lp : NULL;
 }
 
 /**
@@ -2466,6 +2525,7 @@ module_init(fcoe_init);
 static void __exit fcoe_exit(void)
 {
        struct fcoe_interface *fcoe, *tmp;
+       struct fcoe_ctlr *ctlr;
        struct fcoe_port *port;
        unsigned int cpu;
 
@@ -2477,7 +2537,8 @@ static void __exit fcoe_exit(void)
        rtnl_lock();
        list_for_each_entry_safe(fcoe, tmp, &fcoe_hostlist, list) {
                list_del(&fcoe->list);
-               port = lport_priv(fcoe->ctlr.lp);
+               ctlr = fcoe_to_ctlr(fcoe);
+               port = lport_priv(ctlr->lp);
                queue_work(fcoe_wq, &port->destroy_work);
        }
        rtnl_unlock();
@@ -2573,7 +2634,7 @@ static struct fc_seq *fcoe_elsct_send(struct fc_lport *lport, u32 did,
 {
        struct fcoe_port *port = lport_priv(lport);
        struct fcoe_interface *fcoe = port->priv;
-       struct fcoe_ctlr *fip = &fcoe->ctlr;
+       struct fcoe_ctlr *fip = fcoe_to_ctlr(fcoe);
        struct fc_frame_header *fh = fc_frame_header_get(fp);
 
        switch (op) {
@@ -2730,6 +2791,40 @@ static void fcoe_get_lesb(struct fc_lport *lport,
        __fcoe_get_lesb(lport, fc_lesb, netdev);
 }
 
+static void fcoe_ctlr_get_lesb(struct fcoe_ctlr_device *ctlr_dev)
+{
+       struct fcoe_ctlr *fip = fcoe_ctlr_device_priv(ctlr_dev);
+       struct net_device *netdev = fcoe_netdev(fip->lp);
+       struct fcoe_fc_els_lesb *fcoe_lesb;
+       struct fc_els_lesb fc_lesb;
+
+       __fcoe_get_lesb(fip->lp, &fc_lesb, netdev);
+       fcoe_lesb = (struct fcoe_fc_els_lesb *)(&fc_lesb);
+
+       ctlr_dev->lesb.lesb_link_fail =
+               ntohl(fcoe_lesb->lesb_link_fail);
+       ctlr_dev->lesb.lesb_vlink_fail =
+               ntohl(fcoe_lesb->lesb_vlink_fail);
+       ctlr_dev->lesb.lesb_miss_fka =
+               ntohl(fcoe_lesb->lesb_miss_fka);
+       ctlr_dev->lesb.lesb_symb_err =
+               ntohl(fcoe_lesb->lesb_symb_err);
+       ctlr_dev->lesb.lesb_err_block =
+               ntohl(fcoe_lesb->lesb_err_block);
+       ctlr_dev->lesb.lesb_fcs_error =
+               ntohl(fcoe_lesb->lesb_fcs_error);
+}
+
+static void fcoe_fcf_get_vlan_id(struct fcoe_fcf_device *fcf_dev)
+{
+       struct fcoe_ctlr_device *ctlr_dev =
+               fcoe_fcf_dev_to_ctlr_dev(fcf_dev);
+       struct fcoe_ctlr *ctlr = fcoe_ctlr_device_priv(ctlr_dev);
+       struct fcoe_interface *fcoe = fcoe_ctlr_priv(ctlr);
+
+       fcf_dev->vlan_id = vlan_dev_vlan_id(fcoe->netdev);
+}
+
 /**
  * fcoe_set_port_id() - Callback from libfc when Port_ID is set.
  * @lport: the local port
@@ -2747,7 +2842,8 @@ static void fcoe_set_port_id(struct fc_lport *lport,
 {
        struct fcoe_port *port = lport_priv(lport);
        struct fcoe_interface *fcoe = port->priv;
+       struct fcoe_ctlr *ctlr = fcoe_to_ctlr(fcoe);
 
        if (fp && fc_frame_payload_op(fp) == ELS_FLOGI)
-               fcoe_ctlr_recv_flogi(&fcoe->ctlr, lport, fp);
+               fcoe_ctlr_recv_flogi(ctlr, lport, fp);
 }
index 96ac938d39ccc81bc8acb4f0dc34d3ac4de23bef..a624add4f8ecbae730b01f6daab8b8f88ba1ea4d 100644 (file)
@@ -68,7 +68,6 @@ do {                                                                  \
  * @netdev:          The associated net device
  * @fcoe_packet_type: FCoE packet type
  * @fip_packet_type:  FIP packet type
- * @ctlr:            The FCoE controller (for FIP)
  * @oem:             The offload exchange manager for all local port
  *                   instances associated with this port
  * @removed:         Indicates fcoe interface removed from net device
@@ -80,12 +79,15 @@ struct fcoe_interface {
        struct net_device  *realdev;
        struct packet_type fcoe_packet_type;
        struct packet_type fip_packet_type;
-       struct fcoe_ctlr   ctlr;
        struct fc_exch_mgr *oem;
        u8      removed;
 };
 
-#define fcoe_from_ctlr(fip) container_of(fip, struct fcoe_interface, ctlr)
+#define fcoe_to_ctlr(x)                                                \
+       (struct fcoe_ctlr *)(((struct fcoe_ctlr *)(x)) - 1)
+
+#define fcoe_from_ctlr(x)                      \
+       ((struct fcoe_interface *)((x) + 1))
 
 /**
  * fcoe_netdev() - Return the net device associated with a local port
index 5a4c7250aa77abd218ea52c65da31fb514abfd30..d68d57241ee68227703ce1880e52c2ce31b4684d 100644 (file)
@@ -160,6 +160,76 @@ void fcoe_ctlr_init(struct fcoe_ctlr *fip, enum fip_state mode)
 }
 EXPORT_SYMBOL(fcoe_ctlr_init);
 
+static int fcoe_sysfs_fcf_add(struct fcoe_fcf *new)
+{
+       struct fcoe_ctlr *fip = new->fip;
+       struct fcoe_ctlr_device *ctlr_dev = fcoe_ctlr_to_ctlr_dev(fip);
+       struct fcoe_fcf_device temp, *fcf_dev;
+       int rc = 0;
+
+       LIBFCOE_FIP_DBG(fip, "New FCF fab %16.16llx mac %pM\n",
+                       new->fabric_name, new->fcf_mac);
+
+       mutex_lock(&ctlr_dev->lock);
+
+       temp.fabric_name = new->fabric_name;
+       temp.switch_name = new->switch_name;
+       temp.fc_map = new->fc_map;
+       temp.vfid = new->vfid;
+       memcpy(temp.mac, new->fcf_mac, ETH_ALEN);
+       temp.priority = new->pri;
+       temp.fka_period = new->fka_period;
+       temp.selected = 0; /* default to unselected */
+
+       fcf_dev = fcoe_fcf_device_add(ctlr_dev, &temp);
+       if (unlikely(!fcf_dev)) {
+               rc = -ENOMEM;
+               goto out;
+       }
+
+       /*
+        * The fcoe_sysfs layer can return a CONNECTED fcf that
+        * has a priv (fcf was never deleted) or a CONNECTED fcf
+        * that doesn't have a priv (fcf was deleted). However,
+        * libfcoe will always delete FCFs before trying to add
+        * them. This is ensured because both recv_adv and
+        * age_fcfs are protected by the the fcoe_ctlr's mutex.
+        * This means that we should never get a FCF with a
+        * non-NULL priv pointer.
+        */
+       BUG_ON(fcf_dev->priv);
+
+       fcf_dev->priv = new;
+       new->fcf_dev = fcf_dev;
+
+       list_add(&new->list, &fip->fcfs);
+       fip->fcf_count++;
+
+out:
+       mutex_unlock(&ctlr_dev->lock);
+       return rc;
+}
+
+static void fcoe_sysfs_fcf_del(struct fcoe_fcf *new)
+{
+       struct fcoe_ctlr *fip = new->fip;
+       struct fcoe_ctlr_device *ctlr_dev = fcoe_ctlr_to_ctlr_dev(fip);
+       struct fcoe_fcf_device *fcf_dev;
+
+       list_del(&new->list);
+       fip->fcf_count--;
+
+       mutex_lock(&ctlr_dev->lock);
+
+       fcf_dev = fcoe_fcf_to_fcf_dev(new);
+       WARN_ON(!fcf_dev);
+       new->fcf_dev = NULL;
+       fcoe_fcf_device_delete(fcf_dev);
+       kfree(new);
+
+       mutex_unlock(&ctlr_dev->lock);
+}
+
 /**
  * fcoe_ctlr_reset_fcfs() - Reset and free all FCFs for a controller
  * @fip: The FCoE controller whose FCFs are to be reset
@@ -173,10 +243,10 @@ static void fcoe_ctlr_reset_fcfs(struct fcoe_ctlr *fip)
 
        fip->sel_fcf = NULL;
        list_for_each_entry_safe(fcf, next, &fip->fcfs, list) {
-               list_del(&fcf->list);
-               kfree(fcf);
+               fcoe_sysfs_fcf_del(fcf);
        }
-       fip->fcf_count = 0;
+       WARN_ON(fip->fcf_count);
+
        fip->sel_time = 0;
 }
 
@@ -717,8 +787,11 @@ static unsigned long fcoe_ctlr_age_fcfs(struct fcoe_ctlr *fip)
        unsigned long next_timer = jiffies + msecs_to_jiffies(FIP_VN_KA_PERIOD);
        unsigned long deadline;
        unsigned long sel_time = 0;
+       struct list_head del_list;
        struct fcoe_dev_stats *stats;
 
+       INIT_LIST_HEAD(&del_list);
+
        stats = per_cpu_ptr(fip->lp->dev_stats, get_cpu());
 
        list_for_each_entry_safe(fcf, next, &fip->fcfs, list) {
@@ -739,10 +812,13 @@ static unsigned long fcoe_ctlr_age_fcfs(struct fcoe_ctlr *fip)
                if (time_after_eq(jiffies, deadline)) {
                        if (fip->sel_fcf == fcf)
                                fip->sel_fcf = NULL;
+                       /*
+                        * Move to delete list so we can call
+                        * fcoe_sysfs_fcf_del (which can sleep)
+                        * after the put_cpu().
+                        */
                        list_del(&fcf->list);
-                       WARN_ON(!fip->fcf_count);
-                       fip->fcf_count--;
-                       kfree(fcf);
+                       list_add(&fcf->list, &del_list);
                        stats->VLinkFailureCount++;
                } else {
                        if (time_after(next_timer, deadline))
@@ -753,6 +829,12 @@ static unsigned long fcoe_ctlr_age_fcfs(struct fcoe_ctlr *fip)
                }
        }
        put_cpu();
+
+       list_for_each_entry_safe(fcf, next, &del_list, list) {
+               /* Removes fcf from current list */
+               fcoe_sysfs_fcf_del(fcf);
+       }
+
        if (sel_time && !fip->sel_fcf && !fip->sel_time) {
                sel_time += msecs_to_jiffies(FCOE_CTLR_START_DELAY);
                fip->sel_time = sel_time;
@@ -903,23 +985,23 @@ static void fcoe_ctlr_recv_adv(struct fcoe_ctlr *fip, struct sk_buff *skb)
 {
        struct fcoe_fcf *fcf;
        struct fcoe_fcf new;
-       struct fcoe_fcf *found;
        unsigned long sol_tov = msecs_to_jiffies(FCOE_CTRL_SOL_TOV);
        int first = 0;
        int mtu_valid;
+       int found = 0;
+       int rc = 0;
 
        if (fcoe_ctlr_parse_adv(fip, skb, &new))
                return;
 
        mutex_lock(&fip->ctlr_mutex);
        first = list_empty(&fip->fcfs);
-       found = NULL;
        list_for_each_entry(fcf, &fip->fcfs, list) {
                if (fcf->switch_name == new.switch_name &&
                    fcf->fabric_name == new.fabric_name &&
                    fcf->fc_map == new.fc_map &&
                    compare_ether_addr(fcf->fcf_mac, new.fcf_mac) == 0) {
-                       found = fcf;
+                       found = 1;
                        break;
                }
        }
@@ -931,9 +1013,16 @@ static void fcoe_ctlr_recv_adv(struct fcoe_ctlr *fip, struct sk_buff *skb)
                if (!fcf)
                        goto out;
 
-               fip->fcf_count++;
                memcpy(fcf, &new, sizeof(new));
-               list_add(&fcf->list, &fip->fcfs);
+               fcf->fip = fip;
+               rc = fcoe_sysfs_fcf_add(fcf);
+               if (rc) {
+                       printk(KERN_ERR "Failed to allocate sysfs instance "
+                              "for FCF, fab %16.16llx mac %pM\n",
+                              new.fabric_name, new.fcf_mac);
+                       kfree(fcf);
+                       goto out;
+               }
        } else {
                /*
                 * Update the FCF's keep-alive descriptor flags.
@@ -954,6 +1043,7 @@ static void fcoe_ctlr_recv_adv(struct fcoe_ctlr *fip, struct sk_buff *skb)
                fcf->fka_period = new.fka_period;
                memcpy(fcf->fcf_mac, new.fcf_mac, ETH_ALEN);
        }
+
        mtu_valid = fcoe_ctlr_mtu_valid(fcf);
        fcf->time = jiffies;
        if (!found)
@@ -996,6 +1086,7 @@ static void fcoe_ctlr_recv_adv(struct fcoe_ctlr *fip, struct sk_buff *skb)
                    time_before(fip->sel_time, fip->timer.expires))
                        mod_timer(&fip->timer, fip->sel_time);
        }
+
 out:
        mutex_unlock(&fip->ctlr_mutex);
 }
@@ -2718,9 +2809,9 @@ unlock:
 
 /**
  * fcoe_libfc_config() - Sets up libfc related properties for local port
- * @lp: The local port to configure libfc for
- * @fip: The FCoE controller in use by the local port
- * @tt: The libfc function template
+ * @lport:    The local port to configure libfc for
+ * @fip:      The FCoE controller in use by the local port
+ * @tt:       The libfc function template
  * @init_fcp: If non-zero, the FCP portion of libfc should be initialized
  *
  * Returns : 0 for success
@@ -2753,3 +2844,43 @@ int fcoe_libfc_config(struct fc_lport *lport, struct fcoe_ctlr *fip,
        return 0;
 }
 EXPORT_SYMBOL_GPL(fcoe_libfc_config);
+
+void fcoe_fcf_get_selected(struct fcoe_fcf_device *fcf_dev)
+{
+       struct fcoe_ctlr_device *ctlr_dev = fcoe_fcf_dev_to_ctlr_dev(fcf_dev);
+       struct fcoe_ctlr *fip = fcoe_ctlr_device_priv(ctlr_dev);
+       struct fcoe_fcf *fcf;
+
+       mutex_lock(&fip->ctlr_mutex);
+       mutex_lock(&ctlr_dev->lock);
+
+       fcf = fcoe_fcf_device_priv(fcf_dev);
+       if (fcf)
+               fcf_dev->selected = (fcf == fip->sel_fcf) ? 1 : 0;
+       else
+               fcf_dev->selected = 0;
+
+       mutex_unlock(&ctlr_dev->lock);
+       mutex_unlock(&fip->ctlr_mutex);
+}
+EXPORT_SYMBOL(fcoe_fcf_get_selected);
+
+void fcoe_ctlr_get_fip_mode(struct fcoe_ctlr_device *ctlr_dev)
+{
+       struct fcoe_ctlr *ctlr = fcoe_ctlr_device_priv(ctlr_dev);
+
+       mutex_lock(&ctlr->ctlr_mutex);
+       switch (ctlr->mode) {
+       case FIP_MODE_FABRIC:
+               ctlr_dev->mode = FIP_CONN_TYPE_FABRIC;
+               break;
+       case FIP_MODE_VN2VN:
+               ctlr_dev->mode = FIP_CONN_TYPE_VN2VN;
+               break;
+       default:
+               ctlr_dev->mode = FIP_CONN_TYPE_UNKNOWN;
+               break;
+       }
+       mutex_unlock(&ctlr->ctlr_mutex);
+}
+EXPORT_SYMBOL(fcoe_ctlr_get_fip_mode);
diff --git a/drivers/scsi/fcoe/fcoe_sysfs.c b/drivers/scsi/fcoe/fcoe_sysfs.c
new file mode 100644 (file)
index 0000000..2bc1631
--- /dev/null
@@ -0,0 +1,832 @@
+/*
+ * Copyright(c) 2011 - 2012 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Maintained at www.Open-FCoE.org
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/etherdevice.h>
+
+#include <scsi/fcoe_sysfs.h>
+
+static atomic_t ctlr_num;
+static atomic_t fcf_num;
+
+/*
+ * fcoe_fcf_dev_loss_tmo: the default number of seconds that fcoe sysfs
+ * should insulate the loss of a fcf.
+ */
+static unsigned int fcoe_fcf_dev_loss_tmo = 1800;  /* seconds */
+
+module_param_named(fcf_dev_loss_tmo, fcoe_fcf_dev_loss_tmo,
+                  uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(fcf_dev_loss_tmo,
+                "Maximum number of seconds that libfcoe should"
+                " insulate the loss of a fcf. Once this value is"
+                " exceeded, the fcf is removed.");
+
+/*
+ * These are used by the fcoe_*_show_function routines, they
+ * are intentionally placed in the .c file as they're not intended
+ * for use throughout the code.
+ */
+#define fcoe_ctlr_id(x)                                \
+       ((x)->id)
+#define fcoe_ctlr_work_q_name(x)               \
+       ((x)->work_q_name)
+#define fcoe_ctlr_work_q(x)                    \
+       ((x)->work_q)
+#define fcoe_ctlr_devloss_work_q_name(x)       \
+       ((x)->devloss_work_q_name)
+#define fcoe_ctlr_devloss_work_q(x)            \
+       ((x)->devloss_work_q)
+#define fcoe_ctlr_mode(x)                      \
+       ((x)->mode)
+#define fcoe_ctlr_fcf_dev_loss_tmo(x)          \
+       ((x)->fcf_dev_loss_tmo)
+#define fcoe_ctlr_link_fail(x)                 \
+       ((x)->lesb.lesb_link_fail)
+#define fcoe_ctlr_vlink_fail(x)                        \
+       ((x)->lesb.lesb_vlink_fail)
+#define fcoe_ctlr_miss_fka(x)                  \
+       ((x)->lesb.lesb_miss_fka)
+#define fcoe_ctlr_symb_err(x)                  \
+       ((x)->lesb.lesb_symb_err)
+#define fcoe_ctlr_err_block(x)                 \
+       ((x)->lesb.lesb_err_block)
+#define fcoe_ctlr_fcs_error(x)                 \
+       ((x)->lesb.lesb_fcs_error)
+#define fcoe_fcf_state(x)                      \
+       ((x)->state)
+#define fcoe_fcf_fabric_name(x)                        \
+       ((x)->fabric_name)
+#define fcoe_fcf_switch_name(x)                        \
+       ((x)->switch_name)
+#define fcoe_fcf_fc_map(x)                     \
+       ((x)->fc_map)
+#define fcoe_fcf_vfid(x)                       \
+       ((x)->vfid)
+#define fcoe_fcf_mac(x)                                \
+       ((x)->mac)
+#define fcoe_fcf_priority(x)                   \
+       ((x)->priority)
+#define fcoe_fcf_fka_period(x)                 \
+       ((x)->fka_period)
+#define fcoe_fcf_dev_loss_tmo(x)               \
+       ((x)->dev_loss_tmo)
+#define fcoe_fcf_selected(x)                   \
+       ((x)->selected)
+#define fcoe_fcf_vlan_id(x)                    \
+       ((x)->vlan_id)
+
+/*
+ * dev_loss_tmo attribute
+ */
+static int fcoe_str_to_dev_loss(const char *buf, unsigned long *val)
+{
+       int ret;
+
+       ret = kstrtoul(buf, 0, val);
+       if (ret || *val < 0)
+               return -EINVAL;
+       /*
+        * Check for overflow; dev_loss_tmo is u32
+        */
+       if (*val > UINT_MAX)
+               return -EINVAL;
+
+       return 0;
+}
+
+static int fcoe_fcf_set_dev_loss_tmo(struct fcoe_fcf_device *fcf,
+                                    unsigned long val)
+{
+       if ((fcf->state == FCOE_FCF_STATE_UNKNOWN) ||
+           (fcf->state == FCOE_FCF_STATE_DISCONNECTED) ||
+           (fcf->state == FCOE_FCF_STATE_DELETED))
+               return -EBUSY;
+       /*
+        * Check for overflow; dev_loss_tmo is u32
+        */
+       if (val > UINT_MAX)
+               return -EINVAL;
+
+       fcoe_fcf_dev_loss_tmo(fcf) = val;
+       return 0;
+}
+
+#define FCOE_DEVICE_ATTR(_prefix, _name, _mode, _show, _store) \
+struct device_attribute device_attr_fcoe_##_prefix##_##_name = \
+       __ATTR(_name, _mode, _show, _store)
+
+#define fcoe_ctlr_show_function(field, format_string, sz, cast)        \
+static ssize_t show_fcoe_ctlr_device_##field(struct device *dev, \
+                                           struct device_attribute *attr, \
+                                           char *buf)                  \
+{                                                                      \
+       struct fcoe_ctlr_device *ctlr = dev_to_ctlr(dev);               \
+       if (ctlr->f->get_fcoe_ctlr_##field)                             \
+               ctlr->f->get_fcoe_ctlr_##field(ctlr);                   \
+       return snprintf(buf, sz, format_string,                         \
+                       cast fcoe_ctlr_##field(ctlr));                  \
+}
+
+#define fcoe_fcf_show_function(field, format_string, sz, cast) \
+static ssize_t show_fcoe_fcf_device_##field(struct device *dev,        \
+                                          struct device_attribute *attr, \
+                                          char *buf)                   \
+{                                                                      \
+       struct fcoe_fcf_device *fcf = dev_to_fcf(dev);                  \
+       struct fcoe_ctlr_device *ctlr = fcoe_fcf_dev_to_ctlr_dev(fcf);  \
+       if (ctlr->f->get_fcoe_fcf_##field)                              \
+               ctlr->f->get_fcoe_fcf_##field(fcf);                     \
+       return snprintf(buf, sz, format_string,                         \
+                       cast fcoe_fcf_##field(fcf));                    \
+}
+
+#define fcoe_ctlr_private_show_function(field, format_string, sz, cast)        \
+static ssize_t show_fcoe_ctlr_device_##field(struct device *dev, \
+                                           struct device_attribute *attr, \
+                                           char *buf)                  \
+{                                                                      \
+       struct fcoe_ctlr_device *ctlr = dev_to_ctlr(dev);               \
+       return snprintf(buf, sz, format_string, cast fcoe_ctlr_##field(ctlr)); \
+}
+
+#define fcoe_fcf_private_show_function(field, format_string, sz, cast) \
+static ssize_t show_fcoe_fcf_device_##field(struct device *dev,        \
+                                          struct device_attribute *attr, \
+                                          char *buf)                   \
+{                                                              \
+       struct fcoe_fcf_device *fcf = dev_to_fcf(dev);                  \
+       return snprintf(buf, sz, format_string, cast fcoe_fcf_##field(fcf)); \
+}
+
+#define fcoe_ctlr_private_rd_attr(field, format_string, sz)            \
+       fcoe_ctlr_private_show_function(field, format_string, sz, )     \
+       static FCOE_DEVICE_ATTR(ctlr, field, S_IRUGO,                   \
+                               show_fcoe_ctlr_device_##field, NULL)
+
+#define fcoe_ctlr_rd_attr(field, format_string, sz)                    \
+       fcoe_ctlr_show_function(field, format_string, sz, )             \
+       static FCOE_DEVICE_ATTR(ctlr, field, S_IRUGO,                   \
+                               show_fcoe_ctlr_device_##field, NULL)
+
+#define fcoe_fcf_rd_attr(field, format_string, sz)                     \
+       fcoe_fcf_show_function(field, format_string, sz, )              \
+       static FCOE_DEVICE_ATTR(fcf, field, S_IRUGO,                    \
+                               show_fcoe_fcf_device_##field, NULL)
+
+#define fcoe_fcf_private_rd_attr(field, format_string, sz)             \
+       fcoe_fcf_private_show_function(field, format_string, sz, )      \
+       static FCOE_DEVICE_ATTR(fcf, field, S_IRUGO,                    \
+                               show_fcoe_fcf_device_##field, NULL)
+
+#define fcoe_ctlr_private_rd_attr_cast(field, format_string, sz, cast) \
+       fcoe_ctlr_private_show_function(field, format_string, sz, (cast)) \
+       static FCOE_DEVICE_ATTR(ctlr, field, S_IRUGO,                   \
+                               show_fcoe_ctlr_device_##field, NULL)
+
+#define fcoe_fcf_private_rd_attr_cast(field, format_string, sz, cast)  \
+       fcoe_fcf_private_show_function(field, format_string, sz, (cast)) \
+       static FCOE_DEVICE_ATTR(fcf, field, S_IRUGO,                    \
+                               show_fcoe_fcf_device_##field, NULL)
+
+#define fcoe_enum_name_search(title, table_type, table)                        \
+static const char *get_fcoe_##title##_name(enum table_type table_key)  \
+{                                                                      \
+       int i;                                                          \
+       char *name = NULL;                                              \
+                                                                       \
+       for (i = 0; i < ARRAY_SIZE(table); i++) {                       \
+               if (table[i].value == table_key) {                      \
+                       name = table[i].name;                           \
+                       break;                                          \
+               }                                                       \
+       }                                                               \
+       return name;                                                    \
+}
+
+static struct {
+       enum fcf_state value;
+       char           *name;
+} fcf_state_names[] = {
+       { FCOE_FCF_STATE_UNKNOWN,      "Unknown" },
+       { FCOE_FCF_STATE_DISCONNECTED, "Disconnected" },
+       { FCOE_FCF_STATE_CONNECTED,    "Connected" },
+};
+fcoe_enum_name_search(fcf_state, fcf_state, fcf_state_names)
+#define FCOE_FCF_STATE_MAX_NAMELEN 50
+
+static ssize_t show_fcf_state(struct device *dev,
+                             struct device_attribute *attr,
+                             char *buf)
+{
+       struct fcoe_fcf_device *fcf = dev_to_fcf(dev);
+       const char *name;
+       name = get_fcoe_fcf_state_name(fcf->state);
+       if (!name)
+               return -EINVAL;
+       return snprintf(buf, FCOE_FCF_STATE_MAX_NAMELEN, "%s\n", name);
+}
+static FCOE_DEVICE_ATTR(fcf, state, S_IRUGO, show_fcf_state, NULL);
+
+static struct {
+       enum fip_conn_type value;
+       char               *name;
+} fip_conn_type_names[] = {
+       { FIP_CONN_TYPE_UNKNOWN, "Unknown" },
+       { FIP_CONN_TYPE_FABRIC, "Fabric" },
+       { FIP_CONN_TYPE_VN2VN, "VN2VN" },
+};
+fcoe_enum_name_search(ctlr_mode, fip_conn_type, fip_conn_type_names)
+#define FCOE_CTLR_MODE_MAX_NAMELEN 50
+
+static ssize_t show_ctlr_mode(struct device *dev,
+                             struct device_attribute *attr,
+                             char *buf)
+{
+       struct fcoe_ctlr_device *ctlr = dev_to_ctlr(dev);
+       const char *name;
+
+       if (ctlr->f->get_fcoe_ctlr_mode)
+               ctlr->f->get_fcoe_ctlr_mode(ctlr);
+
+       name = get_fcoe_ctlr_mode_name(ctlr->mode);
+       if (!name)
+               return -EINVAL;
+       return snprintf(buf, FCOE_CTLR_MODE_MAX_NAMELEN,
+                       "%s\n", name);
+}
+static FCOE_DEVICE_ATTR(ctlr, mode, S_IRUGO,
+                       show_ctlr_mode, NULL);
+
+static ssize_t
+store_private_fcoe_ctlr_fcf_dev_loss_tmo(struct device *dev,
+                                        struct device_attribute *attr,
+                                        const char *buf, size_t count)
+{
+       struct fcoe_ctlr_device *ctlr = dev_to_ctlr(dev);
+       struct fcoe_fcf_device *fcf;
+       unsigned long val;
+       int rc;
+
+       rc = fcoe_str_to_dev_loss(buf, &val);
+       if (rc)
+               return rc;
+
+       fcoe_ctlr_fcf_dev_loss_tmo(ctlr) = val;
+       mutex_lock(&ctlr->lock);
+       list_for_each_entry(fcf, &ctlr->fcfs, peers)
+               fcoe_fcf_set_dev_loss_tmo(fcf, val);
+       mutex_unlock(&ctlr->lock);
+       return count;
+}
+fcoe_ctlr_private_show_function(fcf_dev_loss_tmo, "%d\n", 20, );
+static FCOE_DEVICE_ATTR(ctlr, fcf_dev_loss_tmo, S_IRUGO | S_IWUSR,
+                       show_fcoe_ctlr_device_fcf_dev_loss_tmo,
+                       store_private_fcoe_ctlr_fcf_dev_loss_tmo);
+
+/* Link Error Status Block (LESB) */
+fcoe_ctlr_rd_attr(link_fail, "%u\n", 20);
+fcoe_ctlr_rd_attr(vlink_fail, "%u\n", 20);
+fcoe_ctlr_rd_attr(miss_fka, "%u\n", 20);
+fcoe_ctlr_rd_attr(symb_err, "%u\n", 20);
+fcoe_ctlr_rd_attr(err_block, "%u\n", 20);
+fcoe_ctlr_rd_attr(fcs_error, "%u\n", 20);
+
+fcoe_fcf_private_rd_attr_cast(fabric_name, "0x%llx\n", 20, unsigned long long);
+fcoe_fcf_private_rd_attr_cast(switch_name, "0x%llx\n", 20, unsigned long long);
+fcoe_fcf_private_rd_attr(priority, "%u\n", 20);
+fcoe_fcf_private_rd_attr(fc_map, "0x%x\n", 20);
+fcoe_fcf_private_rd_attr(vfid, "%u\n", 20);
+fcoe_fcf_private_rd_attr(mac, "%pM\n", 20);
+fcoe_fcf_private_rd_attr(fka_period, "%u\n", 20);
+fcoe_fcf_rd_attr(selected, "%u\n", 20);
+fcoe_fcf_rd_attr(vlan_id, "%u\n", 20);
+
+fcoe_fcf_private_show_function(dev_loss_tmo, "%d\n", 20, )
+static ssize_t
+store_fcoe_fcf_dev_loss_tmo(struct device *dev, struct device_attribute *attr,
+                           const char *buf, size_t count)
+{
+       struct fcoe_fcf_device *fcf = dev_to_fcf(dev);
+       unsigned long val;
+       int rc;
+
+       rc = fcoe_str_to_dev_loss(buf, &val);
+       if (rc)
+               return rc;
+
+       rc = fcoe_fcf_set_dev_loss_tmo(fcf, val);
+       if (rc)
+               return rc;
+       return count;
+}
+static FCOE_DEVICE_ATTR(fcf, dev_loss_tmo, S_IRUGO | S_IWUSR,
+                       show_fcoe_fcf_device_dev_loss_tmo,
+                       store_fcoe_fcf_dev_loss_tmo);
+
+static struct attribute *fcoe_ctlr_lesb_attrs[] = {
+       &device_attr_fcoe_ctlr_link_fail.attr,
+       &device_attr_fcoe_ctlr_vlink_fail.attr,
+       &device_attr_fcoe_ctlr_miss_fka.attr,
+       &device_attr_fcoe_ctlr_symb_err.attr,
+       &device_attr_fcoe_ctlr_err_block.attr,
+       &device_attr_fcoe_ctlr_fcs_error.attr,
+       NULL,
+};
+
+static struct attribute_group fcoe_ctlr_lesb_attr_group = {
+       .name = "lesb",
+       .attrs = fcoe_ctlr_lesb_attrs,
+};
+
+static struct attribute *fcoe_ctlr_attrs[] = {
+       &device_attr_fcoe_ctlr_fcf_dev_loss_tmo.attr,
+       &device_attr_fcoe_ctlr_mode.attr,
+       NULL,
+};
+
+static struct attribute_group fcoe_ctlr_attr_group = {
+       .attrs = fcoe_ctlr_attrs,
+};
+
+static const struct attribute_group *fcoe_ctlr_attr_groups[] = {
+       &fcoe_ctlr_attr_group,
+       &fcoe_ctlr_lesb_attr_group,
+       NULL,
+};
+
+static struct attribute *fcoe_fcf_attrs[] = {
+       &device_attr_fcoe_fcf_fabric_name.attr,
+       &device_attr_fcoe_fcf_switch_name.attr,
+       &device_attr_fcoe_fcf_dev_loss_tmo.attr,
+       &device_attr_fcoe_fcf_fc_map.attr,
+       &device_attr_fcoe_fcf_vfid.attr,
+       &device_attr_fcoe_fcf_mac.attr,
+       &device_attr_fcoe_fcf_priority.attr,
+       &device_attr_fcoe_fcf_fka_period.attr,
+       &device_attr_fcoe_fcf_state.attr,
+       &device_attr_fcoe_fcf_selected.attr,
+       &device_attr_fcoe_fcf_vlan_id.attr,
+       NULL
+};
+
+static struct attribute_group fcoe_fcf_attr_group = {
+       .attrs = fcoe_fcf_attrs,
+};
+
+static const struct attribute_group *fcoe_fcf_attr_groups[] = {
+       &fcoe_fcf_attr_group,
+       NULL,
+};
+
+struct bus_type fcoe_bus_type;
+
+static int fcoe_bus_match(struct device *dev,
+                         struct device_driver *drv)
+{
+       if (dev->bus == &fcoe_bus_type)
+               return 1;
+       return 0;
+}
+
+/**
+ * fcoe_ctlr_device_release() - Release the FIP ctlr memory
+ * @dev: Pointer to the FIP ctlr's embedded device
+ *
+ * Called when the last FIP ctlr reference is released.
+ */
+static void fcoe_ctlr_device_release(struct device *dev)
+{
+       struct fcoe_ctlr_device *ctlr = dev_to_ctlr(dev);
+       kfree(ctlr);
+}
+
+/**
+ * fcoe_fcf_device_release() - Release the FIP fcf memory
+ * @dev: Pointer to the fcf's embedded device
+ *
+ * Called when the last FIP fcf reference is released.
+ */
+static void fcoe_fcf_device_release(struct device *dev)
+{
+       struct fcoe_fcf_device *fcf = dev_to_fcf(dev);
+       kfree(fcf);
+}
+
+struct device_type fcoe_ctlr_device_type = {
+       .name = "fcoe_ctlr",
+       .groups = fcoe_ctlr_attr_groups,
+       .release = fcoe_ctlr_device_release,
+};
+
+struct device_type fcoe_fcf_device_type = {
+       .name = "fcoe_fcf",
+       .groups = fcoe_fcf_attr_groups,
+       .release = fcoe_fcf_device_release,
+};
+
+struct bus_type fcoe_bus_type = {
+       .name = "fcoe",
+       .match = &fcoe_bus_match,
+};
+
+/**
+ * fcoe_ctlr_device_flush_work() - Flush a FIP ctlr's workqueue
+ * @ctlr: Pointer to the FIP ctlr whose workqueue is to be flushed
+ */
+void fcoe_ctlr_device_flush_work(struct fcoe_ctlr_device *ctlr)
+{
+       if (!fcoe_ctlr_work_q(ctlr)) {
+               printk(KERN_ERR
+                      "ERROR: FIP Ctlr '%d' attempted to flush work, "
+                      "when no workqueue created.\n", ctlr->id);
+               dump_stack();
+               return;
+       }
+
+       flush_workqueue(fcoe_ctlr_work_q(ctlr));
+}
+
+/**
+ * fcoe_ctlr_device_queue_work() - Schedule work for a FIP ctlr's workqueue
+ * @ctlr: Pointer to the FIP ctlr who owns the devloss workqueue
+ * @work:   Work to queue for execution
+ *
+ * Return value:
+ *     1 on success / 0 already queued / < 0 for error
+ */
+int fcoe_ctlr_device_queue_work(struct fcoe_ctlr_device *ctlr,
+                              struct work_struct *work)
+{
+       if (unlikely(!fcoe_ctlr_work_q(ctlr))) {
+               printk(KERN_ERR
+                      "ERROR: FIP Ctlr '%d' attempted to queue work, "
+                      "when no workqueue created.\n", ctlr->id);
+               dump_stack();
+
+               return -EINVAL;
+       }
+
+       return queue_work(fcoe_ctlr_work_q(ctlr), work);
+}
+
+/**
+ * fcoe_ctlr_device_flush_devloss() - Flush a FIP ctlr's devloss workqueue
+ * @ctlr: Pointer to FIP ctlr whose workqueue is to be flushed
+ */
+void fcoe_ctlr_device_flush_devloss(struct fcoe_ctlr_device *ctlr)
+{
+       if (!fcoe_ctlr_devloss_work_q(ctlr)) {
+               printk(KERN_ERR
+                      "ERROR: FIP Ctlr '%d' attempted to flush work, "
+                      "when no workqueue created.\n", ctlr->id);
+               dump_stack();
+               return;
+       }
+
+       flush_workqueue(fcoe_ctlr_devloss_work_q(ctlr));
+}
+
+/**
+ * fcoe_ctlr_device_queue_devloss_work() - Schedule work for a FIP ctlr's devloss workqueue
+ * @ctlr: Pointer to the FIP ctlr who owns the devloss workqueue
+ * @work:   Work to queue for execution
+ * @delay:  jiffies to delay the work queuing
+ *
+ * Return value:
+ *     1 on success / 0 already queued / < 0 for error
+ */
+int fcoe_ctlr_device_queue_devloss_work(struct fcoe_ctlr_device *ctlr,
+                                      struct delayed_work *work,
+                                      unsigned long delay)
+{
+       if (unlikely(!fcoe_ctlr_devloss_work_q(ctlr))) {
+               printk(KERN_ERR
+                      "ERROR: FIP Ctlr '%d' attempted to queue work, "
+                      "when no workqueue created.\n", ctlr->id);
+               dump_stack();
+
+               return -EINVAL;
+       }
+
+       return queue_delayed_work(fcoe_ctlr_devloss_work_q(ctlr), work, delay);
+}
+
+static int fcoe_fcf_device_match(struct fcoe_fcf_device *new,
+                                struct fcoe_fcf_device *old)
+{
+       if (new->switch_name == old->switch_name &&
+           new->fabric_name == old->fabric_name &&
+           new->fc_map == old->fc_map &&
+           compare_ether_addr(new->mac, old->mac) == 0)
+               return 1;
+       return 0;
+}
+
+/**
+ * fcoe_ctlr_device_add() - Add a FIP ctlr to sysfs
+ * @parent:    The parent device to which the fcoe_ctlr instance
+ *             should be attached
+ * @f:         The LLD's FCoE sysfs function template pointer
+ * @priv_size: Size to be allocated with the fcoe_ctlr_device for the LLD
+ *
+ * This routine allocates a FIP ctlr object with some additional memory
+ * for the LLD. The FIP ctlr is initialized, added to sysfs and then
+ * attributes are added to it.
+ */
+struct fcoe_ctlr_device *fcoe_ctlr_device_add(struct device *parent,
+                                   struct fcoe_sysfs_function_template *f,
+                                   int priv_size)
+{
+       struct fcoe_ctlr_device *ctlr;
+       int error = 0;
+
+       ctlr = kzalloc(sizeof(struct fcoe_ctlr_device) + priv_size,
+                      GFP_KERNEL);
+       if (!ctlr)
+               goto out;
+
+       ctlr->id = atomic_inc_return(&ctlr_num) - 1;
+       ctlr->f = f;
+       INIT_LIST_HEAD(&ctlr->fcfs);
+       mutex_init(&ctlr->lock);
+       ctlr->dev.parent = parent;
+       ctlr->dev.bus = &fcoe_bus_type;
+       ctlr->dev.type = &fcoe_ctlr_device_type;
+
+       ctlr->fcf_dev_loss_tmo = fcoe_fcf_dev_loss_tmo;
+
+       snprintf(ctlr->work_q_name, sizeof(ctlr->work_q_name),
+                "ctlr_wq_%d", ctlr->id);
+       ctlr->work_q = create_singlethread_workqueue(
+               ctlr->work_q_name);
+       if (!ctlr->work_q)
+               goto out_del;
+
+       snprintf(ctlr->devloss_work_q_name,
+                sizeof(ctlr->devloss_work_q_name),
+                "ctlr_dl_wq_%d", ctlr->id);
+       ctlr->devloss_work_q = create_singlethread_workqueue(
+               ctlr->devloss_work_q_name);
+       if (!ctlr->devloss_work_q)
+               goto out_del_q;
+
+       dev_set_name(&ctlr->dev, "ctlr_%d", ctlr->id);
+       error = device_register(&ctlr->dev);
+       if (error)
+               goto out_del_q2;
+
+       return ctlr;
+
+out_del_q2:
+       destroy_workqueue(ctlr->devloss_work_q);
+       ctlr->devloss_work_q = NULL;
+out_del_q:
+       destroy_workqueue(ctlr->work_q);
+       ctlr->work_q = NULL;
+out_del:
+       kfree(ctlr);
+out:
+       return NULL;
+}
+EXPORT_SYMBOL_GPL(fcoe_ctlr_device_add);
+
+/**
+ * fcoe_ctlr_device_delete() - Delete a FIP ctlr and its subtree from sysfs
+ * @ctlr: A pointer to the ctlr to be deleted
+ *
+ * Deletes a FIP ctlr and any fcfs attached
+ * to it. Deleting fcfs will cause their childen
+ * to be deleted as well.
+ *
+ * The ctlr is detached from sysfs and it's resources
+ * are freed (work q), but the memory is not freed
+ * until its last reference is released.
+ *
+ * This routine expects no locks to be held before
+ * calling.
+ *
+ * TODO: Currently there are no callbacks to clean up LLD data
+ * for a fcoe_fcf_device. LLDs must keep this in mind as they need
+ * to clean up each of their LLD data for all fcoe_fcf_device before
+ * calling fcoe_ctlr_device_delete.
+ */
+void fcoe_ctlr_device_delete(struct fcoe_ctlr_device *ctlr)
+{
+       struct fcoe_fcf_device *fcf, *next;
+       /* Remove any attached fcfs */
+       mutex_lock(&ctlr->lock);
+       list_for_each_entry_safe(fcf, next,
+                                &ctlr->fcfs, peers) {
+               list_del(&fcf->peers);
+               fcf->state = FCOE_FCF_STATE_DELETED;
+               fcoe_ctlr_device_queue_work(ctlr, &fcf->delete_work);
+       }
+       mutex_unlock(&ctlr->lock);
+
+       fcoe_ctlr_device_flush_work(ctlr);
+
+       destroy_workqueue(ctlr->devloss_work_q);
+       ctlr->devloss_work_q = NULL;
+       destroy_workqueue(ctlr->work_q);
+       ctlr->work_q = NULL;
+
+       device_unregister(&ctlr->dev);
+}
+EXPORT_SYMBOL_GPL(fcoe_ctlr_device_delete);
+
+/**
+ * fcoe_fcf_device_final_delete() - Final delete routine
+ * @work: The FIP fcf's embedded work struct
+ *
+ * It is expected that the fcf has been removed from
+ * the FIP ctlr's list before calling this routine.
+ */
+static void fcoe_fcf_device_final_delete(struct work_struct *work)
+{
+       struct fcoe_fcf_device *fcf =
+               container_of(work, struct fcoe_fcf_device, delete_work);
+       struct fcoe_ctlr_device *ctlr = fcoe_fcf_dev_to_ctlr_dev(fcf);
+
+       /*
+        * Cancel any outstanding timers. These should really exist
+        * only when rmmod'ing the LLDD and we're asking for
+        * immediate termination of the rports
+        */
+       if (!cancel_delayed_work(&fcf->dev_loss_work))
+               fcoe_ctlr_device_flush_devloss(ctlr);
+
+       device_unregister(&fcf->dev);
+}
+
+/**
+ * fip_timeout_deleted_fcf() - Delete a fcf when the devloss timer fires
+ * @work: The FIP fcf's embedded work struct
+ *
+ * Removes the fcf from the FIP ctlr's list of fcfs and
+ * queues the final deletion.
+ */
+static void fip_timeout_deleted_fcf(struct work_struct *work)
+{
+       struct fcoe_fcf_device *fcf =
+               container_of(work, struct fcoe_fcf_device, dev_loss_work.work);
+       struct fcoe_ctlr_device *ctlr = fcoe_fcf_dev_to_ctlr_dev(fcf);
+
+       mutex_lock(&ctlr->lock);
+
+       /*
+        * If the fcf is deleted or reconnected before the timer
+        * fires the devloss queue will be flushed, but the state will
+        * either be CONNECTED or DELETED. If that is the case we
+        * cancel deleting the fcf.
+        */
+       if (fcf->state != FCOE_FCF_STATE_DISCONNECTED)
+               goto out;
+
+       dev_printk(KERN_ERR, &fcf->dev,
+                  "FIP fcf connection time out: removing fcf\n");
+
+       list_del(&fcf->peers);
+       fcf->state = FCOE_FCF_STATE_DELETED;
+       fcoe_ctlr_device_queue_work(ctlr, &fcf->delete_work);
+
+out:
+       mutex_unlock(&ctlr->lock);
+}
+
+/**
+ * fcoe_fcf_device_delete() - Delete a FIP fcf
+ * @fcf: Pointer to the fcf which is to be deleted
+ *
+ * Queues the FIP fcf on the devloss workqueue
+ *
+ * Expects the ctlr_attrs mutex to be held for fcf
+ * state change.
+ */
+void fcoe_fcf_device_delete(struct fcoe_fcf_device *fcf)
+{
+       struct fcoe_ctlr_device *ctlr = fcoe_fcf_dev_to_ctlr_dev(fcf);
+       int timeout = fcf->dev_loss_tmo;
+
+       if (fcf->state != FCOE_FCF_STATE_CONNECTED)
+               return;
+
+       fcf->state = FCOE_FCF_STATE_DISCONNECTED;
+
+       /*
+        * FCF will only be re-connected by the LLD calling
+        * fcoe_fcf_device_add, and it should be setting up
+        * priv then.
+        */
+       fcf->priv = NULL;
+
+       fcoe_ctlr_device_queue_devloss_work(ctlr, &fcf->dev_loss_work,
+                                          timeout * HZ);
+}
+EXPORT_SYMBOL_GPL(fcoe_fcf_device_delete);
+
+/**
+ * fcoe_fcf_device_add() - Add a FCoE sysfs fcoe_fcf_device to the system
+ * @ctlr:    The fcoe_ctlr_device that will be the fcoe_fcf_device parent
+ * @new_fcf: A temporary FCF used for lookups on the current list of fcfs
+ *
+ * Expects to be called with the ctlr->lock held
+ */
+struct fcoe_fcf_device *fcoe_fcf_device_add(struct fcoe_ctlr_device *ctlr,
+                                           struct fcoe_fcf_device *new_fcf)
+{
+       struct fcoe_fcf_device *fcf;
+       int error = 0;
+
+       list_for_each_entry(fcf, &ctlr->fcfs, peers) {
+               if (fcoe_fcf_device_match(new_fcf, fcf)) {
+                       if (fcf->state == FCOE_FCF_STATE_CONNECTED)
+                               return fcf;
+
+                       fcf->state = FCOE_FCF_STATE_CONNECTED;
+
+                       if (!cancel_delayed_work(&fcf->dev_loss_work))
+                               fcoe_ctlr_device_flush_devloss(ctlr);
+
+                       return fcf;
+               }
+       }
+
+       fcf = kzalloc(sizeof(struct fcoe_fcf_device), GFP_ATOMIC);
+       if (unlikely(!fcf))
+               goto out;
+
+       INIT_WORK(&fcf->delete_work, fcoe_fcf_device_final_delete);
+       INIT_DELAYED_WORK(&fcf->dev_loss_work, fip_timeout_deleted_fcf);
+
+       fcf->dev.parent = &ctlr->dev;
+       fcf->dev.bus = &fcoe_bus_type;
+       fcf->dev.type = &fcoe_fcf_device_type;
+       fcf->id = atomic_inc_return(&fcf_num) - 1;
+       fcf->state = FCOE_FCF_STATE_UNKNOWN;
+
+       fcf->dev_loss_tmo = ctlr->fcf_dev_loss_tmo;
+
+       dev_set_name(&fcf->dev, "fcf_%d", fcf->id);
+
+       fcf->fabric_name = new_fcf->fabric_name;
+       fcf->switch_name = new_fcf->switch_name;
+       fcf->fc_map = new_fcf->fc_map;
+       fcf->vfid = new_fcf->vfid;
+       memcpy(fcf->mac, new_fcf->mac, ETH_ALEN);
+       fcf->priority = new_fcf->priority;
+       fcf->fka_period = new_fcf->fka_period;
+       fcf->selected = new_fcf->selected;
+
+       error = device_register(&fcf->dev);
+       if (error)
+               goto out_del;
+
+       fcf->state = FCOE_FCF_STATE_CONNECTED;
+       list_add_tail(&fcf->peers, &ctlr->fcfs);
+
+       return fcf;
+
+out_del:
+       kfree(fcf);
+out:
+       return NULL;
+}
+EXPORT_SYMBOL_GPL(fcoe_fcf_device_add);
+
+int __init fcoe_sysfs_setup(void)
+{
+       int error;
+
+       atomic_set(&ctlr_num, 0);
+       atomic_set(&fcf_num, 0);
+
+       error = bus_register(&fcoe_bus_type);
+       if (error)
+               return error;
+
+       return 0;
+}
+
+void __exit fcoe_sysfs_teardown(void)
+{
+       bus_unregister(&fcoe_bus_type);
+}
index 710e149d41b60b0e1f087acce76b9b074953eb15..b46f43dced78eb6b77337905ff20eeb16d451a0c 100644 (file)
@@ -815,9 +815,17 @@ out_nodev:
  */
 static int __init libfcoe_init(void)
 {
-       fcoe_transport_init();
+       int rc = 0;
 
-       return 0;
+       rc = fcoe_transport_init();
+       if (rc)
+               return rc;
+
+       rc = fcoe_sysfs_setup();
+       if (rc)
+               fcoe_transport_exit();
+
+       return rc;
 }
 module_init(libfcoe_init);
 
@@ -826,6 +834,7 @@ module_init(libfcoe_init);
  */
 static void __exit libfcoe_exit(void)
 {
+       fcoe_sysfs_teardown();
        fcoe_transport_exit();
 }
 module_exit(libfcoe_exit);
index 6208d562890d1cb3cfb3c85845dddf02086c4fb2..317a7fdc3b825064e4a5677f0a64bc5f3e43a8d6 100644 (file)
@@ -25,3 +25,12 @@ config SCSI_QLA_FC
        Firmware images can be retrieved from:
 
                ftp://ftp.qlogic.com/outgoing/linux/firmware/
+
+config TCM_QLA2XXX
+       tristate "TCM_QLA2XXX fabric module for Qlogic 2xxx series target mode HBAs"
+       depends on SCSI_QLA_FC && TARGET_CORE
+       select LIBFC
+       select BTREE
+       default n
+       ---help---
+       Say Y here to enable the TCM_QLA2XXX fabric module for Qlogic 2xxx series target mode HBAs
index 5df782f4a097f9cd8b32455319f3894a5879cd06..dce7d788cdc9c7999795dac0e9819e81943aa223 100644 (file)
@@ -1,5 +1,6 @@
 qla2xxx-y := qla_os.o qla_init.o qla_mbx.o qla_iocb.o qla_isr.o qla_gs.o \
                qla_dbg.o qla_sup.o qla_attr.o qla_mid.o qla_dfs.o qla_bsg.o \
-        qla_nx.o
+        qla_nx.o qla_target.o
 
 obj-$(CONFIG_SCSI_QLA_FC) += qla2xxx.o
+obj-$(CONFIG_TCM_QLA2XXX) += tcm_qla2xxx.o
index 5926f5a87ea8e97b0611d246ea99b96b1f6c7b6c..5ab953029f8d1412ee66057c641771bf69d765cd 100644 (file)
@@ -5,6 +5,7 @@
  * See LICENSE.qla2xxx for copyright and licensing details.
  */
 #include "qla_def.h"
+#include "qla_target.h"
 
 #include <linux/kthread.h>
 #include <linux/vmalloc.h>
@@ -576,6 +577,7 @@ qla2x00_sysfs_write_reset(struct file *filp, struct kobject *kobj,
                scsi_block_requests(vha->host);
                set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
                if (IS_QLA82XX(ha)) {
+                       ha->flags.isp82xx_no_md_cap = 1;
                        qla82xx_idc_lock(ha);
                        qla82xx_set_reset_owner(vha);
                        qla82xx_idc_unlock(ha);
@@ -585,7 +587,7 @@ qla2x00_sysfs_write_reset(struct file *filp, struct kobject *kobj,
                scsi_unblock_requests(vha->host);
                break;
        case 0x2025d:
-               if (!IS_QLA81XX(ha))
+               if (!IS_QLA81XX(ha) || !IS_QLA8031(ha))
                        return -EPERM;
 
                ql_log(ql_log_info, vha, 0x706f,
@@ -1105,9 +1107,8 @@ qla2x00_total_isp_aborts_show(struct device *dev,
                              struct device_attribute *attr, char *buf)
 {
        scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
-       struct qla_hw_data *ha = vha->hw;
        return snprintf(buf, PAGE_SIZE, "%d\n",
-           ha->qla_stats.total_isp_aborts);
+           vha->qla_stats.total_isp_aborts);
 }
 
 static ssize_t
@@ -1154,7 +1155,7 @@ qla2x00_phy_version_show(struct device *dev, struct device_attribute *attr,
        scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
        struct qla_hw_data *ha = vha->hw;
 
-       if (!IS_QLA81XX(ha) && !IS_QLA83XX(ha))
+       if (!IS_QLA81XX(ha) && !IS_QLA8031(ha))
                return snprintf(buf, PAGE_SIZE, "\n");
 
        return snprintf(buf, PAGE_SIZE, "%d.%02d.%02d\n",
@@ -1537,7 +1538,7 @@ qla2x00_get_fc_host_stats(struct Scsi_Host *shost)
        dma_addr_t stats_dma;
        struct fc_host_statistics *pfc_host_stat;
 
-       pfc_host_stat = &ha->fc_host_stat;
+       pfc_host_stat = &vha->fc_host_stat;
        memset(pfc_host_stat, -1, sizeof(struct fc_host_statistics));
 
        if (test_bit(UNLOADING, &vha->dpc_flags))
@@ -1580,8 +1581,8 @@ qla2x00_get_fc_host_stats(struct Scsi_Host *shost)
                pfc_host_stat->dumped_frames = stats->dumped_frames;
                pfc_host_stat->nos_count = stats->nos_rcvd;
        }
-       pfc_host_stat->fcp_input_megabytes = ha->qla_stats.input_bytes >> 20;
-       pfc_host_stat->fcp_output_megabytes = ha->qla_stats.output_bytes >> 20;
+       pfc_host_stat->fcp_input_megabytes = vha->qla_stats.input_bytes >> 20;
+       pfc_host_stat->fcp_output_megabytes = vha->qla_stats.output_bytes >> 20;
 
 done_free:
         dma_pool_free(ha->s_dma_pool, stats, stats_dma);
@@ -1737,6 +1738,7 @@ qla24xx_vport_create(struct fc_vport *fc_vport, bool disable)
        fc_host_supported_speeds(vha->host) =
                fc_host_supported_speeds(base_vha->host);
 
+       qlt_vport_create(vha, ha);
        qla24xx_vport_disable(fc_vport, disable);
 
        if (ha->flags.cpu_affinity_enabled) {
@@ -1951,12 +1953,16 @@ qla2x00_init_host_attr(scsi_qla_host_t *vha)
        fc_host_dev_loss_tmo(vha->host) = ha->port_down_retry_count;
        fc_host_node_name(vha->host) = wwn_to_u64(vha->node_name);
        fc_host_port_name(vha->host) = wwn_to_u64(vha->port_name);
-       fc_host_supported_classes(vha->host) = FC_COS_CLASS3;
+       fc_host_supported_classes(vha->host) = ha->tgt.enable_class_2 ?
+                       (FC_COS_CLASS2|FC_COS_CLASS3) : FC_COS_CLASS3;
        fc_host_max_npiv_vports(vha->host) = ha->max_npiv_vports;
        fc_host_npiv_vports_inuse(vha->host) = ha->cur_vport_count;
 
        if (IS_CNA_CAPABLE(ha))
                speed = FC_PORTSPEED_10GBIT;
+       else if (IS_QLA2031(ha))
+               speed = FC_PORTSPEED_16GBIT | FC_PORTSPEED_8GBIT |
+                   FC_PORTSPEED_4GBIT;
        else if (IS_QLA25XX(ha))
                speed = FC_PORTSPEED_8GBIT | FC_PORTSPEED_4GBIT |
                    FC_PORTSPEED_2GBIT | FC_PORTSPEED_1GBIT;
index bc3cc6d91117ab58b103e889370c89f8402bd6f9..c68883806c54b8092e81ff82f62db06fd8ee822f 100644 (file)
@@ -297,7 +297,6 @@ qla2x00_process_els(struct fc_bsg_job *bsg_job)
 
                /* Initialize all required  fields of fcport */
                fcport->vha = vha;
-               fcport->vp_idx = vha->vp_idx;
                fcport->d_id.b.al_pa =
                        bsg_job->request->rqst_data.h_els.port_id[0];
                fcport->d_id.b.area =
@@ -483,7 +482,6 @@ qla2x00_process_ct(struct fc_bsg_job *bsg_job)
 
        /* Initialize all required  fields of fcport */
        fcport->vha = vha;
-       fcport->vp_idx = vha->vp_idx;
        fcport->d_id.b.al_pa = bsg_job->request->rqst_data.h_ct.port_id[0];
        fcport->d_id.b.area = bsg_job->request->rqst_data.h_ct.port_id[1];
        fcport->d_id.b.domain = bsg_job->request->rqst_data.h_ct.port_id[2];
@@ -544,7 +542,7 @@ qla81xx_set_internal_loopback(scsi_qla_host_t *vha, uint16_t *config,
        int rval = 0;
        struct qla_hw_data *ha = vha->hw;
 
-       if (!IS_QLA81XX(ha) && !IS_QLA83XX(ha))
+       if (!IS_QLA81XX(ha) && !IS_QLA8031(ha))
                goto done_set_internal;
 
        new_config[0] = config[0] | (ENABLE_INTERNAL_LOOPBACK << 1);
@@ -586,7 +584,7 @@ qla81xx_reset_internal_loopback(scsi_qla_host_t *vha, uint16_t *config,
        uint16_t new_config[4];
        struct qla_hw_data *ha = vha->hw;
 
-       if (!IS_QLA81XX(ha) && !IS_QLA83XX(ha))
+       if (!IS_QLA81XX(ha) && !IS_QLA8031(ha))
                goto done_reset_internal;
 
        memset(new_config, 0 , sizeof(new_config));
@@ -710,8 +708,7 @@ qla2x00_process_loopback(struct fc_bsg_job *bsg_job)
        elreq.options = bsg_job->request->rqst_data.h_vendor.vendor_cmd[1];
 
        if ((ha->current_topology == ISP_CFG_F ||
-           (atomic_read(&vha->loop_state) == LOOP_DOWN) ||
-           ((IS_QLA81XX(ha) || IS_QLA83XX(ha)) &&
+           ((IS_QLA81XX(ha) || IS_QLA8031(ha)) &&
            le32_to_cpu(*(uint32_t *)req_data) == ELS_OPCODE_BYTE
            && req_data_len == MAX_ELS_FRAME_PAYLOAD)) &&
                elreq.options == EXTERNAL_LOOPBACK) {
@@ -1402,6 +1399,9 @@ qla2x00_update_optrom(struct fc_bsg_job *bsg_job)
        if (rval)
                return rval;
 
+       /* Set the isp82xx_no_md_cap not to capture minidump */
+       ha->flags.isp82xx_no_md_cap = 1;
+
        sg_copy_to_buffer(bsg_job->request_payload.sg_list,
            bsg_job->request_payload.sg_cnt, ha->optrom_buffer,
            ha->optrom_region_size);
index 62324a1d55737b7a127bcc5bab74ebac66ab62aa..fdee5611f3e2afce938c7f7ad559ce5d8eecffe9 100644 (file)
  * ----------------------------------------------------------------------
  * |             Level            |   Last Value Used  |     Holes     |
  * ----------------------------------------------------------------------
- * | Module Init and Probe        |       0x0120       | 0x4b,0xba,0xfa |
- * | Mailbox commands             |       0x113e       | 0x112c-0x112e  |
+ * | Module Init and Probe        |       0x0122       | 0x4b,0xba,0xfa |
+ * | Mailbox commands             |       0x1140       | 0x111a-0x111b  |
+ * |                              |                    | 0x112c-0x112e  |
  * |                              |                    | 0x113a         |
  * | Device Discovery             |       0x2086       | 0x2020-0x2022  |
  * | Queue Command and IO tracing |       0x3030       | 0x3006,0x3008  |
  * |                              |                    | 0x302d-0x302e  |
- * | DPC Thread                   |       0x401c       |               |
- * | Async Events                 |       0x505d       | 0x502b-0x502f  |
+ * | DPC Thread                   |       0x401c       | 0x4002,0x4013  |
+ * | Async Events                 |       0x505f       | 0x502b-0x502f  |
  * |                              |                    | 0x5047,0x5052  |
- * | Timer Routines               |       0x6011       | 0x600e-0x600f  |
+ * | Timer Routines               |       0x6011       |                |
  * | User Space Interactions      |       0x709f       | 0x7018,0x702e, |
  * |                              |                    | 0x7039,0x7045, |
  * |                              |                    | 0x7073-0x7075, |
  * |                              |                    | 0x708c         |
  * | Task Management              |       0x803c       | 0x8025-0x8026  |
  * |                              |                    | 0x800b,0x8039  |
- * | AER/EEH                      |       0x900f       |               |
+ * | AER/EEH                      |       0x9011       |               |
  * | Virtual Port                 |       0xa007       |               |
- * | ISP82XX Specific             |       0xb054       | 0xb053         |
+ * | ISP82XX Specific             |       0xb054       | 0xb024         |
  * | MultiQ                       |       0xc00c       |               |
  * | Misc                         |       0xd010       |               |
+ * | Target Mode                 |       0xe06f       |                |
+ * | Target Mode Management      |       0xf071       |                |
+ * | Target Mode Task Management  |      0x1000b      |                |
  * ----------------------------------------------------------------------
  */
 
@@ -378,6 +382,54 @@ qla25xx_copy_fce(struct qla_hw_data *ha, void *ptr, uint32_t **last_chain)
        return (char *)iter_reg + ntohl(fcec->size);
 }
 
+static inline void *
+qla2xxx_copy_atioqueues(struct qla_hw_data *ha, void *ptr,
+       uint32_t **last_chain)
+{
+       struct qla2xxx_mqueue_chain *q;
+       struct qla2xxx_mqueue_header *qh;
+       uint32_t num_queues;
+       int que;
+       struct {
+               int length;
+               void *ring;
+       } aq, *aqp;
+
+       if (!ha->tgt.atio_q_length)
+               return ptr;
+
+       num_queues = 1;
+       aqp = &aq;
+       aqp->length = ha->tgt.atio_q_length;
+       aqp->ring = ha->tgt.atio_ring;
+
+       for (que = 0; que < num_queues; que++) {
+               /* aqp = ha->atio_q_map[que]; */
+               q = ptr;
+               *last_chain = &q->type;
+               q->type = __constant_htonl(DUMP_CHAIN_QUEUE);
+               q->chain_size = htonl(
+                   sizeof(struct qla2xxx_mqueue_chain) +
+                   sizeof(struct qla2xxx_mqueue_header) +
+                   (aqp->length * sizeof(request_t)));
+               ptr += sizeof(struct qla2xxx_mqueue_chain);
+
+               /* Add header. */
+               qh = ptr;
+               qh->queue = __constant_htonl(TYPE_ATIO_QUEUE);
+               qh->number = htonl(que);
+               qh->size = htonl(aqp->length * sizeof(request_t));
+               ptr += sizeof(struct qla2xxx_mqueue_header);
+
+               /* Add data. */
+               memcpy(ptr, aqp->ring, aqp->length * sizeof(request_t));
+
+               ptr += aqp->length * sizeof(request_t);
+       }
+
+       return ptr;
+}
+
 static inline void *
 qla25xx_copy_mqueues(struct qla_hw_data *ha, void *ptr, uint32_t **last_chain)
 {
@@ -873,6 +925,8 @@ qla24xx_fw_dump(scsi_qla_host_t *vha, int hardware_locked)
        struct qla24xx_fw_dump *fw;
        uint32_t        ext_mem_cnt;
        void            *nxt;
+       void            *nxt_chain;
+       uint32_t        *last_chain = NULL;
        struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev);
 
        if (IS_QLA82XX(ha))
@@ -1091,6 +1145,16 @@ qla24xx_fw_dump(scsi_qla_host_t *vha, int hardware_locked)
 
        qla24xx_copy_eft(ha, nxt);
 
+       nxt_chain = (void *)ha->fw_dump + ha->chain_offset;
+       nxt_chain = qla2xxx_copy_atioqueues(ha, nxt_chain, &last_chain);
+       if (last_chain) {
+               ha->fw_dump->version |= __constant_htonl(DUMP_CHAIN_VARIANT);
+               *last_chain |= __constant_htonl(DUMP_CHAIN_LAST);
+       }
+
+       /* Adjust valid length. */
+       ha->fw_dump_len = (nxt_chain - (void *)ha->fw_dump);
+
 qla24xx_fw_dump_failed_0:
        qla2xxx_dump_post_process(base_vha, rval);
 
@@ -1399,6 +1463,7 @@ qla25xx_fw_dump(scsi_qla_host_t *vha, int hardware_locked)
        /* Chain entries -- started with MQ. */
        nxt_chain = qla25xx_copy_fce(ha, nxt_chain, &last_chain);
        nxt_chain = qla25xx_copy_mqueues(ha, nxt_chain, &last_chain);
+       nxt_chain = qla2xxx_copy_atioqueues(ha, nxt_chain, &last_chain);
        if (last_chain) {
                ha->fw_dump->version |= __constant_htonl(DUMP_CHAIN_VARIANT);
                *last_chain |= __constant_htonl(DUMP_CHAIN_LAST);
@@ -1717,6 +1782,7 @@ qla81xx_fw_dump(scsi_qla_host_t *vha, int hardware_locked)
        /* Chain entries -- started with MQ. */
        nxt_chain = qla25xx_copy_fce(ha, nxt_chain, &last_chain);
        nxt_chain = qla25xx_copy_mqueues(ha, nxt_chain, &last_chain);
+       nxt_chain = qla2xxx_copy_atioqueues(ha, nxt_chain, &last_chain);
        if (last_chain) {
                ha->fw_dump->version |= __constant_htonl(DUMP_CHAIN_VARIANT);
                *last_chain |= __constant_htonl(DUMP_CHAIN_LAST);
@@ -2218,6 +2284,7 @@ copy_queue:
        /* Chain entries -- started with MQ. */
        nxt_chain = qla25xx_copy_fce(ha, nxt_chain, &last_chain);
        nxt_chain = qla25xx_copy_mqueues(ha, nxt_chain, &last_chain);
+       nxt_chain = qla2xxx_copy_atioqueues(ha, nxt_chain, &last_chain);
        if (last_chain) {
                ha->fw_dump->version |= __constant_htonl(DUMP_CHAIN_VARIANT);
                *last_chain |= __constant_htonl(DUMP_CHAIN_LAST);
index 2157bdf1569a87e3771efbe9a8dcc26833ce47ab..f278df8cce0f02988e95f85e3f65d82381150f47 100644 (file)
@@ -244,6 +244,7 @@ struct qla2xxx_mqueue_header {
        uint32_t queue;
 #define TYPE_REQUEST_QUEUE     0x1
 #define TYPE_RESPONSE_QUEUE    0x2
+#define TYPE_ATIO_QUEUE                0x3
        uint32_t number;
        uint32_t size;
 };
@@ -339,3 +340,11 @@ ql_log_pci(uint32_t, struct pci_dev *pdev, int32_t, const char *fmt, ...);
 #define ql_dbg_misc    0x00010000 /* For dumping everything that is not
                                    * not covered by upper categories
                                    */
+#define ql_dbg_verbose 0x00008000 /* More verbosity for each level
+                                   * This is to be used with other levels where
+                                   * more verbosity is required. It might not
+                                   * be applicable to all the levels.
+                                   */
+#define ql_dbg_tgt     0x00004000 /* Target mode */
+#define ql_dbg_tgt_mgt 0x00002000 /* Target mode management */
+#define ql_dbg_tgt_tmr 0x00001000 /* Target mode task management */
index a2443031dbe76c68c617f26767e269105f075343..39007f53aec0284b9cd855592d786823bbad5786 100644 (file)
 #define RESPONSE_ENTRY_CNT_2100                64      /* Number of response entries.*/
 #define RESPONSE_ENTRY_CNT_2300                512     /* Number of response entries.*/
 #define RESPONSE_ENTRY_CNT_MQ          128     /* Number of response entries.*/
+#define ATIO_ENTRY_CNT_24XX            4096    /* Number of ATIO entries. */
 
 struct req_que;
 
@@ -1234,11 +1235,27 @@ typedef struct {
  * ISP queue - response queue entry definition.
  */
 typedef struct {
-       uint8_t         data[60];
+       uint8_t         entry_type;             /* Entry type. */
+       uint8_t         entry_count;            /* Entry count. */
+       uint8_t         sys_define;             /* System defined. */
+       uint8_t         entry_status;           /* Entry Status. */
+       uint32_t        handle;                 /* System defined handle */
+       uint8_t         data[52];
        uint32_t        signature;
 #define RESPONSE_PROCESSED     0xDEADDEAD      /* Signature */
 } response_t;
 
+/*
+ * ISP queue - ATIO queue entry definition.
+ */
+struct atio {
+       uint8_t         entry_type;             /* Entry type. */
+       uint8_t         entry_count;            /* Entry count. */
+       uint8_t         data[58];
+       uint32_t        signature;
+#define ATIO_PROCESSED 0xDEADDEAD              /* Signature */
+};
+
 typedef union {
        uint16_t extended;
        struct {
@@ -1719,11 +1736,13 @@ typedef struct fc_port {
        struct fc_rport *rport, *drport;
        u32 supported_classes;
 
-       uint16_t vp_idx;
        uint8_t fc4_type;
        uint8_t scan_state;
 } fc_port_t;
 
+#define QLA_FCPORT_SCAN_NONE   0
+#define QLA_FCPORT_SCAN_FOUND  1
+
 /*
  * Fibre channel port/lun states.
  */
@@ -1747,6 +1766,7 @@ static const char * const port_state_str[] = {
 #define FCF_LOGIN_NEEDED       BIT_1
 #define FCF_FCP2_DEVICE                BIT_2
 #define FCF_ASYNC_SENT         BIT_3
+#define FCF_CONF_COMP_SUPPORTED BIT_4
 
 /* No loop ID flag. */
 #define FC_NO_LOOP_ID          0x1000
@@ -2419,6 +2439,40 @@ struct qlfc_fw {
        uint32_t len;
 };
 
+struct qlt_hw_data {
+       /* Protected by hw lock */
+       uint32_t enable_class_2:1;
+       uint32_t enable_explicit_conf:1;
+       uint32_t ini_mode_force_reverse:1;
+       uint32_t node_name_set:1;
+
+       dma_addr_t atio_dma;    /* Physical address. */
+       struct atio *atio_ring; /* Base virtual address */
+       struct atio *atio_ring_ptr;     /* Current address. */
+       uint16_t atio_ring_index; /* Current index. */
+       uint16_t atio_q_length;
+
+       void *target_lport_ptr;
+       struct qla_tgt_func_tmpl *tgt_ops;
+       struct qla_tgt *qla_tgt;
+       struct qla_tgt_cmd *cmds[MAX_OUTSTANDING_COMMANDS];
+       uint16_t current_handle;
+
+       struct qla_tgt_vp_map *tgt_vp_map;
+       struct mutex tgt_mutex;
+       struct mutex tgt_host_action_mutex;
+
+       int saved_set;
+       uint16_t saved_exchange_count;
+       uint32_t saved_firmware_options_1;
+       uint32_t saved_firmware_options_2;
+       uint32_t saved_firmware_options_3;
+       uint8_t saved_firmware_options[2];
+       uint8_t saved_add_firmware_options[2];
+
+       uint8_t tgt_node_name[WWN_SIZE];
+};
+
 /*
  * Qlogic host adapter specific data structure.
 */
@@ -2460,7 +2514,9 @@ struct qla_hw_data {
                uint32_t        thermal_supported:1;
                uint32_t        isp82xx_reset_hdlr_active:1;
                uint32_t        isp82xx_reset_owner:1;
-               /* 28 bits */
+               uint32_t        isp82xx_no_md_cap:1;
+               uint32_t        host_shutting_down:1;
+               /* 30 bits */
        } flags;
 
        /* This spinlock is used to protect "io transactions", you must
@@ -2804,7 +2860,6 @@ struct qla_hw_data {
                                        /* ISP2322: red, green, amber. */
        uint16_t        zio_mode;
        uint16_t        zio_timer;
-       struct fc_host_statistics fc_host_stat;
 
        struct qla_msix_entry *msix_entries;
 
@@ -2817,7 +2872,6 @@ struct qla_hw_data {
        int             cur_vport_count;
 
        struct qla_chip_state_84xx *cs84xx;
-       struct qla_statistics qla_stats;
        struct isp_operations *isp_ops;
        struct workqueue_struct *wq;
        struct qlfc_fw fw_buf;
@@ -2863,6 +2917,8 @@ struct qla_hw_data {
        dma_addr_t      md_tmplt_hdr_dma;
        void            *md_dump;
        uint32_t        md_dump_size;
+
+       struct qlt_hw_data tgt;
 };
 
 /*
@@ -2920,6 +2976,7 @@ typedef struct scsi_qla_host {
 #define FCOE_CTX_RESET_NEEDED  18      /* Initiate FCoE context reset */
 #define MPI_RESET_NEEDED       19      /* Initiate MPI FW reset */
 #define ISP_QUIESCE_NEEDED     20      /* Driver need some quiescence */
+#define SCR_PENDING            21      /* SCR in target mode */
 
        uint32_t        device_flags;
 #define SWITCH_FOUND           BIT_0
@@ -2979,10 +3036,21 @@ typedef struct scsi_qla_host {
        struct req_que *req;
        int             fw_heartbeat_counter;
        int             seconds_since_last_heartbeat;
+       struct fc_host_statistics fc_host_stat;
+       struct qla_statistics qla_stats;
 
        atomic_t        vref_count;
 } scsi_qla_host_t;
 
+#define SET_VP_IDX     1
+#define SET_AL_PA      2
+#define RESET_VP_IDX   3
+#define RESET_AL_PA    4
+struct qla_tgt_vp_map {
+       uint8_t idx;
+       scsi_qla_host_t *vha;
+};
+
 /*
  * Macros to help code, maintain, etc.
  */
index 9f065804bd12b830a458ab11b6a92c7936f0bc58..9eacd2df111b85108dd8b3e75c3ea0320ec927b5 100644 (file)
@@ -175,6 +175,7 @@ extern int  qla2x00_vp_abort_isp(scsi_qla_host_t *);
 /*
  * Global Function Prototypes in qla_iocb.c source file.
  */
+
 extern uint16_t qla2x00_calc_iocbs_32(uint16_t);
 extern uint16_t qla2x00_calc_iocbs_64(uint16_t);
 extern void qla2x00_build_scsi_iocbs_32(srb_t *, cmd_entry_t *, uint16_t);
@@ -188,6 +189,8 @@ extern uint16_t qla24xx_calc_iocbs(scsi_qla_host_t *, uint16_t);
 extern void qla24xx_build_scsi_iocbs(srb_t *, struct cmd_type_7 *, uint16_t);
 extern int qla24xx_dif_start_scsi(srb_t *);
 
+extern void *qla2x00_alloc_iocbs(scsi_qla_host_t *, srb_t *);
+extern int qla2x00_issue_marker(scsi_qla_host_t *, int);
 
 /*
  * Global Function Prototypes in qla_mbx.c source file.
@@ -238,6 +241,9 @@ qla2x00_get_retry_cnt(scsi_qla_host_t *, uint8_t *, uint8_t *, uint16_t *);
 extern int
 qla2x00_init_firmware(scsi_qla_host_t *, uint16_t);
 
+extern int
+qla2x00_get_node_name_list(scsi_qla_host_t *, void **, int *);
+
 extern int
 qla2x00_get_port_database(scsi_qla_host_t *, fc_port_t *, uint8_t);
 
@@ -383,6 +389,8 @@ extern int qla2x00_request_irqs(struct qla_hw_data *, struct rsp_que *);
 extern void qla2x00_free_irqs(scsi_qla_host_t *);
 
 extern int qla2x00_get_data_rate(scsi_qla_host_t *);
+extern char *qla2x00_get_link_speed_str(struct qla_hw_data *);
+
 /*
  * Global Function Prototypes in qla_sup.c source file.
  */
@@ -546,6 +554,7 @@ extern void qla2x00_sp_free(void *, void *);
 extern void qla2x00_sp_timeout(unsigned long);
 extern void qla2x00_bsg_job_done(void *, void *, int);
 extern void qla2x00_bsg_sp_free(void *, void *);
+extern void qla2x00_start_iocbs(struct scsi_qla_host *, struct req_que *);
 
 /* Interrupt related */
 extern irqreturn_t qla82xx_intr_handler(int, void *);
index 3128f80441f5378090156e98edbd1ee6798a2033..05260d25fe469f8e28bfba0807a874c4e2173acd 100644 (file)
@@ -5,6 +5,7 @@
  * See LICENSE.qla2xxx for copyright and licensing details.
  */
 #include "qla_def.h"
+#include "qla_target.h"
 
 static int qla2x00_sns_ga_nxt(scsi_qla_host_t *, fc_port_t *);
 static int qla2x00_sns_gid_pt(scsi_qla_host_t *, sw_info_t *);
@@ -556,7 +557,8 @@ qla2x00_rff_id(scsi_qla_host_t *vha)
        ct_req->req.rff_id.port_id[1] = vha->d_id.b.area;
        ct_req->req.rff_id.port_id[2] = vha->d_id.b.al_pa;
 
-       ct_req->req.rff_id.fc4_feature = BIT_1;
+       qlt_rff_id(vha, ct_req);
+
        ct_req->req.rff_id.fc4_type = 0x08;             /* SCSI - FCP */
 
        /* Execute MS IOCB */
index b9465643396b0c40e7fb7fbab19249f5c5cbded6..ca5084743135cf358c397dfccaa84fbbbbfece95 100644 (file)
@@ -17,6 +17,9 @@
 #include <asm/prom.h>
 #endif
 
+#include <target/target_core_base.h>
+#include "qla_target.h"
+
 /*
 *  QLogic ISP2x00 Hardware Support Function Prototypes.
 */
@@ -518,7 +521,10 @@ qla2x00_initialize_adapter(scsi_qla_host_t *vha)
                        return QLA_FUNCTION_FAILED;
                }
        }
-       rval = qla2x00_init_rings(vha);
+
+       if (qla_ini_mode_enabled(vha))
+               rval = qla2x00_init_rings(vha);
+
        ha->flags.chip_reset_done = 1;
 
        if (rval == QLA_SUCCESS && IS_QLA84XX(ha)) {
@@ -1233,6 +1239,8 @@ qla2x00_alloc_fw_dump(scsi_qla_host_t *vha)
                        mq_size += ha->max_rsp_queues *
                            (rsp->length * sizeof(response_t));
                }
+               if (ha->tgt.atio_q_length)
+                       mq_size += ha->tgt.atio_q_length * sizeof(request_t);
                /* Allocate memory for Fibre Channel Event Buffer. */
                if (!IS_QLA25XX(ha) && !IS_QLA81XX(ha) && !IS_QLA83XX(ha))
                        goto try_eft;
@@ -1696,6 +1704,12 @@ qla24xx_config_rings(struct scsi_qla_host *vha)
        icb->response_q_address[0] = cpu_to_le32(LSD(rsp->dma));
        icb->response_q_address[1] = cpu_to_le32(MSD(rsp->dma));
 
+       /* Setup ATIO queue dma pointers for target mode */
+       icb->atio_q_inpointer = __constant_cpu_to_le16(0);
+       icb->atio_q_length = cpu_to_le16(ha->tgt.atio_q_length);
+       icb->atio_q_address[0] = cpu_to_le32(LSD(ha->tgt.atio_dma));
+       icb->atio_q_address[1] = cpu_to_le32(MSD(ha->tgt.atio_dma));
+
        if (ha->mqenable || IS_QLA83XX(ha)) {
                icb->qos = __constant_cpu_to_le16(QLA_DEFAULT_QUE_QOS);
                icb->rid = __constant_cpu_to_le16(rid);
@@ -1739,6 +1753,8 @@ qla24xx_config_rings(struct scsi_qla_host *vha)
                WRT_REG_DWORD(&reg->isp24.rsp_q_in, 0);
                WRT_REG_DWORD(&reg->isp24.rsp_q_out, 0);
        }
+       qlt_24xx_config_rings(vha, reg);
+
        /* PCI posting */
        RD_REG_DWORD(&ioreg->hccr);
 }
@@ -1794,6 +1810,11 @@ qla2x00_init_rings(scsi_qla_host_t *vha)
 
        spin_unlock(&ha->vport_slock);
 
+       ha->tgt.atio_ring_ptr = ha->tgt.atio_ring;
+       ha->tgt.atio_ring_index = 0;
+       /* Initialize ATIO queue entries */
+       qlt_init_atio_q_entries(vha);
+
        ha->isp_ops->config_rings(vha);
 
        spin_unlock_irqrestore(&ha->hardware_lock, flags);
@@ -2051,6 +2072,10 @@ qla2x00_configure_hba(scsi_qla_host_t *vha)
        vha->d_id.b.area = area;
        vha->d_id.b.al_pa = al_pa;
 
+       spin_lock(&ha->vport_slock);
+       qlt_update_vp_map(vha, SET_AL_PA);
+       spin_unlock(&ha->vport_slock);
+
        if (!vha->flags.init_done)
                ql_log(ql_log_info, vha, 0x2010,
                    "Topology - %s, Host Loop address 0x%x.\n",
@@ -2185,7 +2210,7 @@ qla2x00_nvram_config(scsi_qla_host_t *vha)
            nv->id[2] != 'P' || nv->id[3] != ' ' || nv->nvram_version < 1) {
                /* Reset NVRAM data. */
                ql_log(ql_log_warn, vha, 0x0064,
-                   "Inconisistent NVRAM "
+                   "Inconsistent NVRAM "
                    "detected: checksum=0x%x id=%c version=0x%x.\n",
                    chksum, nv->id[0], nv->nvram_version);
                ql_log(ql_log_warn, vha, 0x0065,
@@ -2270,7 +2295,7 @@ qla2x00_nvram_config(scsi_qla_host_t *vha)
        if (IS_QLA23XX(ha)) {
                nv->firmware_options[0] |= BIT_2;
                nv->firmware_options[0] &= ~BIT_3;
-               nv->firmware_options[0] &= ~BIT_6;
+               nv->special_options[0] &= ~BIT_6;
                nv->add_firmware_options[1] |= BIT_5 | BIT_4;
 
                if (IS_QLA2300(ha)) {
@@ -2467,14 +2492,21 @@ qla2x00_rport_del(void *data)
 {
        fc_port_t *fcport = data;
        struct fc_rport *rport;
+       scsi_qla_host_t *vha = fcport->vha;
        unsigned long flags;
 
        spin_lock_irqsave(fcport->vha->host->host_lock, flags);
        rport = fcport->drport ? fcport->drport: fcport->rport;
        fcport->drport = NULL;
        spin_unlock_irqrestore(fcport->vha->host->host_lock, flags);
-       if (rport)
+       if (rport) {
                fc_remote_port_delete(rport);
+               /*
+                * Release the target mode FC NEXUS in qla_target.c code
+                * if target mod is enabled.
+                */
+               qlt_fc_port_deleted(vha, fcport);
+       }
 }
 
 /**
@@ -2495,11 +2527,11 @@ qla2x00_alloc_fcport(scsi_qla_host_t *vha, gfp_t flags)
 
        /* Setup fcport template structure. */
        fcport->vha = vha;
-       fcport->vp_idx = vha->vp_idx;
        fcport->port_type = FCT_UNKNOWN;
        fcport->loop_id = FC_NO_LOOP_ID;
        qla2x00_set_fcport_state(fcport, FCS_UNCONFIGURED);
        fcport->supported_classes = FC_COS_UNSPECIFIED;
+       fcport->scan_state = QLA_FCPORT_SCAN_NONE;
 
        return fcport;
 }
@@ -2726,7 +2758,6 @@ qla2x00_configure_local_loop(scsi_qla_host_t *vha)
                new_fcport->d_id.b.area = area;
                new_fcport->d_id.b.al_pa = al_pa;
                new_fcport->loop_id = loop_id;
-               new_fcport->vp_idx = vha->vp_idx;
                rval2 = qla2x00_get_port_database(vha, new_fcport, 0);
                if (rval2 != QLA_SUCCESS) {
                        ql_dbg(ql_dbg_disc, vha, 0x201a,
@@ -2760,10 +2791,6 @@ qla2x00_configure_local_loop(scsi_qla_host_t *vha)
 
                if (!found) {
                        /* New device, add to fcports list. */
-                       if (vha->vp_idx) {
-                               new_fcport->vha = vha;
-                               new_fcport->vp_idx = vha->vp_idx;
-                       }
                        list_add_tail(&new_fcport->list, &vha->vp_fcports);
 
                        /* Allocate a new replacement fcport. */
@@ -2800,8 +2827,6 @@ cleanup_allocation:
 static void
 qla2x00_iidma_fcport(scsi_qla_host_t *vha, fc_port_t *fcport)
 {
-#define LS_UNKNOWN      2
-       static char *link_speeds[] = { "1", "2", "?", "4", "8", "10" };
        char *link_speed;
        int rval;
        uint16_t mb[4];
@@ -2829,11 +2854,7 @@ qla2x00_iidma_fcport(scsi_qla_host_t *vha, fc_port_t *fcport)
                    fcport->port_name[6], fcport->port_name[7], rval,
                    fcport->fp_speed, mb[0], mb[1]);
        } else {
-               link_speed = link_speeds[LS_UNKNOWN];
-               if (fcport->fp_speed < 5)
-                       link_speed = link_speeds[fcport->fp_speed];
-               else if (fcport->fp_speed == 0x13)
-                       link_speed = link_speeds[5];
+               link_speed = qla2x00_get_link_speed_str(ha);
                ql_dbg(ql_dbg_disc, vha, 0x2005,
                    "iIDMA adjusted to %s GB/s "
                    "on %02x%02x%02x%02x%02x%02x%02x%02x.\n", link_speed,
@@ -2864,6 +2885,12 @@ qla2x00_reg_remote_port(scsi_qla_host_t *vha, fc_port_t *fcport)
                    "Unable to allocate fc remote port.\n");
                return;
        }
+       /*
+        * Create target mode FC NEXUS in qla_target.c if target mode is
+        * enabled..
+        */
+       qlt_fc_port_added(vha, fcport);
+
        spin_lock_irqsave(fcport->vha->host->host_lock, flags);
        *((fc_port_t **)rport->dd_data) = fcport;
        spin_unlock_irqrestore(fcport->vha->host->host_lock, flags);
@@ -2921,7 +2948,7 @@ static int
 qla2x00_configure_fabric(scsi_qla_host_t *vha)
 {
        int     rval;
-       fc_port_t       *fcport, *fcptemp;
+       fc_port_t       *fcport;
        uint16_t        next_loopid;
        uint16_t        mb[MAILBOX_REGISTER_COUNT];
        uint16_t        loop_id;
@@ -2959,7 +2986,7 @@ qla2x00_configure_fabric(scsi_qla_host_t *vha)
                    0xfc, mb, BIT_1|BIT_0);
                if (rval != QLA_SUCCESS) {
                        set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags);
-                       return rval;
+                       break;
                }
                if (mb[0] != MBS_COMMAND_COMPLETE) {
                        ql_dbg(ql_dbg_disc, vha, 0x2042,
@@ -2991,21 +3018,16 @@ qla2x00_configure_fabric(scsi_qla_host_t *vha)
                        }
                }
 
-#define QLA_FCPORT_SCAN                1
-#define QLA_FCPORT_FOUND       2
-
-               list_for_each_entry(fcport, &vha->vp_fcports, list) {
-                       fcport->scan_state = QLA_FCPORT_SCAN;
-               }
-
                rval = qla2x00_find_all_fabric_devs(vha, &new_fcports);
                if (rval != QLA_SUCCESS)
                        break;
 
-               /*
-                * Logout all previous fabric devices marked lost, except
-                * FCP2 devices.
-                */
+               /* Add new ports to existing port list */
+               list_splice_tail_init(&new_fcports, &vha->vp_fcports);
+
+               /* Starting free loop ID. */
+               next_loopid = ha->min_external_loopid;
+
                list_for_each_entry(fcport, &vha->vp_fcports, list) {
                        if (test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags))
                                break;
@@ -3013,7 +3035,8 @@ qla2x00_configure_fabric(scsi_qla_host_t *vha)
                        if ((fcport->flags & FCF_FABRIC_DEVICE) == 0)
                                continue;
 
-                       if (fcport->scan_state == QLA_FCPORT_SCAN &&
+                       /* Logout lost/gone fabric devices (non-FCP2) */
+                       if (fcport->scan_state != QLA_FCPORT_SCAN_FOUND &&
                            atomic_read(&fcport->state) == FCS_ONLINE) {
                                qla2x00_mark_device_lost(vha, fcport,
                                    ql2xplogiabsentdevice, 0);
@@ -3026,78 +3049,30 @@ qla2x00_configure_fabric(scsi_qla_host_t *vha)
                                            fcport->d_id.b.domain,
                                            fcport->d_id.b.area,
                                            fcport->d_id.b.al_pa);
-                                       fcport->loop_id = FC_NO_LOOP_ID;
                                }
-                       }
-               }
-
-               /* Starting free loop ID. */
-               next_loopid = ha->min_external_loopid;
-
-               /*
-                * Scan through our port list and login entries that need to be
-                * logged in.
-                */
-               list_for_each_entry(fcport, &vha->vp_fcports, list) {
-                       if (atomic_read(&vha->loop_down_timer) ||
-                           test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags))
-                               break;
-
-                       if ((fcport->flags & FCF_FABRIC_DEVICE) == 0 ||
-                           (fcport->flags & FCF_LOGIN_NEEDED) == 0)
                                continue;
-
-                       if (fcport->loop_id == FC_NO_LOOP_ID) {
-                               fcport->loop_id = next_loopid;
-                               rval = qla2x00_find_new_loop_id(
-                                   base_vha, fcport);
-                               if (rval != QLA_SUCCESS) {
-                                       /* Ran out of IDs to use */
-                                       break;
-                               }
                        }
-                       /* Login and update database */
-                       qla2x00_fabric_dev_login(vha, fcport, &next_loopid);
-               }
-
-               /* Exit if out of loop IDs. */
-               if (rval != QLA_SUCCESS) {
-                       break;
-               }
-
-               /*
-                * Login and add the new devices to our port list.
-                */
-               list_for_each_entry_safe(fcport, fcptemp, &new_fcports, list) {
-                       if (atomic_read(&vha->loop_down_timer) ||
-                           test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags))
-                               break;
-
-                       /* Find a new loop ID to use. */
-                       fcport->loop_id = next_loopid;
-                       rval = qla2x00_find_new_loop_id(base_vha, fcport);
-                       if (rval != QLA_SUCCESS) {
-                               /* Ran out of IDs to use */
-                               break;
+                       fcport->scan_state = QLA_FCPORT_SCAN_NONE;
+
+                       /* Login fabric devices that need a login */
+                       if ((fcport->flags & FCF_LOGIN_NEEDED) != 0 &&
+                           atomic_read(&vha->loop_down_timer) == 0) {
+                               if (fcport->loop_id == FC_NO_LOOP_ID) {
+                                       fcport->loop_id = next_loopid;
+                                       rval = qla2x00_find_new_loop_id(
+                                           base_vha, fcport);
+                                       if (rval != QLA_SUCCESS) {
+                                               /* Ran out of IDs to use */
+                                               continue;
+                                       }
+                               }
                        }
 
                        /* Login and update database */
                        qla2x00_fabric_dev_login(vha, fcport, &next_loopid);
-
-                       if (vha->vp_idx) {
-                               fcport->vha = vha;
-                               fcport->vp_idx = vha->vp_idx;
-                       }
-                       list_move_tail(&fcport->list, &vha->vp_fcports);
                }
        } while (0);
 
-       /* Free all new device structures not processed. */
-       list_for_each_entry_safe(fcport, fcptemp, &new_fcports, list) {
-               list_del(&fcport->list);
-               kfree(fcport);
-       }
-
        if (rval) {
                ql_dbg(ql_dbg_disc, vha, 0x2068,
                    "Configure fabric error exit rval=%d.\n", rval);
@@ -3287,7 +3262,7 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *vha,
                            WWN_SIZE))
                                continue;
 
-                       fcport->scan_state = QLA_FCPORT_FOUND;
+                       fcport->scan_state = QLA_FCPORT_SCAN_FOUND;
 
                        found++;
 
@@ -3595,6 +3570,12 @@ qla2x00_fabric_login(scsi_qla_host_t *vha, fc_port_t *fcport,
                        if (mb[10] & BIT_1)
                                fcport->supported_classes |= FC_COS_CLASS3;
 
+                       if (IS_FWI2_CAPABLE(ha)) {
+                               if (mb[10] & BIT_7)
+                                       fcport->flags |=
+                                           FCF_CONF_COMP_SUPPORTED;
+                       }
+
                        rval = QLA_SUCCESS;
                        break;
                } else if (mb[0] == MBS_LOOP_ID_USED) {
@@ -3841,7 +3822,7 @@ qla2x00_abort_isp_cleanup(scsi_qla_host_t *vha)
                vha->flags.online = 0;
        ha->flags.chip_reset_done = 0;
        clear_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
-       ha->qla_stats.total_isp_aborts++;
+       vha->qla_stats.total_isp_aborts++;
 
        ql_log(ql_log_info, vha, 0x00af,
            "Performing ISP error recovery - ha=%p.\n", ha);
@@ -4066,6 +4047,7 @@ qla2x00_restart_isp(scsi_qla_host_t *vha)
        struct qla_hw_data *ha = vha->hw;
        struct req_que *req = ha->req_q_map[0];
        struct rsp_que *rsp = ha->rsp_q_map[0];
+       unsigned long flags;
 
        /* If firmware needs to be loaded */
        if (qla2x00_isp_firmware(vha)) {
@@ -4090,6 +4072,16 @@ qla2x00_restart_isp(scsi_qla_host_t *vha)
                        qla2x00_marker(vha, req, rsp, 0, 0, MK_SYNC_ALL);
 
                        vha->flags.online = 1;
+
+                       /*
+                        * Process any ATIO queue entries that came in
+                        * while we weren't online.
+                        */
+                       spin_lock_irqsave(&ha->hardware_lock, flags);
+                       if (qla_tgt_mode_enabled(vha))
+                               qlt_24xx_process_atio_queue(vha);
+                       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
                        /* Wait at most MAX_TARGET RSCNs for a stable link. */
                        wait_time = 256;
                        do {
@@ -4279,7 +4271,7 @@ qla24xx_nvram_config(scsi_qla_host_t *vha)
            nv->nvram_version < __constant_cpu_to_le16(ICB_VERSION)) {
                /* Reset NVRAM data. */
                ql_log(ql_log_warn, vha, 0x006b,
-                   "Inconisistent NVRAM detected: checksum=0x%x id=%c "
+                   "Inconsistent NVRAM detected: checksum=0x%x id=%c "
                    "version=0x%x.\n", chksum, nv->id[0], nv->nvram_version);
                ql_log(ql_log_warn, vha, 0x006c,
                    "Falling back to functioning (yet invalid -- WWPN) "
@@ -4330,6 +4322,15 @@ qla24xx_nvram_config(scsi_qla_host_t *vha)
                rval = 1;
        }
 
+       if (!qla_ini_mode_enabled(vha)) {
+               /* Don't enable full login after initial LIP */
+               nv->firmware_options_1 &= __constant_cpu_to_le32(~BIT_13);
+               /* Don't enable LIP full login for initiator */
+               nv->host_p &= __constant_cpu_to_le32(~BIT_10);
+       }
+
+       qlt_24xx_config_nvram_stage1(vha, nv);
+
        /* Reset Initialization control block */
        memset(icb, 0, ha->init_cb_size);
 
@@ -4357,8 +4358,10 @@ qla24xx_nvram_config(scsi_qla_host_t *vha)
        qla2x00_set_model_info(vha, nv->model_name, sizeof(nv->model_name),
            "QLA2462");
 
-       /* Use alternate WWN? */
+       qlt_24xx_config_nvram_stage2(vha, icb);
+
        if (nv->host_p & __constant_cpu_to_le32(BIT_15)) {
+               /* Use alternate WWN? */
                memcpy(icb->node_name, nv->alternate_node_name, WWN_SIZE);
                memcpy(icb->port_name, nv->alternate_port_name, WWN_SIZE);
        }
@@ -5029,7 +5032,7 @@ qla81xx_nvram_config(scsi_qla_host_t *vha)
            nv->nvram_version < __constant_cpu_to_le16(ICB_VERSION)) {
                /* Reset NVRAM data. */
                ql_log(ql_log_info, vha, 0x0073,
-                   "Inconisistent NVRAM detected: checksum=0x%x id=%c "
+                   "Inconsistent NVRAM detected: checksum=0x%x id=%c "
                    "version=0x%x.\n", chksum, nv->id[0],
                    le16_to_cpu(nv->nvram_version));
                ql_log(ql_log_info, vha, 0x0074,
index eac9509244971ba9e324803ca531014e3f917dad..70dbf53d9e0f4fe0762073cb0bfec1508251fc8f 100644 (file)
@@ -5,6 +5,7 @@
  * See LICENSE.qla2xxx for copyright and licensing details.
  */
 #include "qla_def.h"
+#include "qla_target.h"
 
 #include <linux/blkdev.h>
 #include <linux/delay.h>
@@ -23,18 +24,17 @@ qla2x00_get_cmd_direction(srb_t *sp)
 {
        uint16_t cflags;
        struct scsi_cmnd *cmd = GET_CMD_SP(sp);
+       struct scsi_qla_host *vha = sp->fcport->vha;
 
        cflags = 0;
 
        /* Set transfer direction */
        if (cmd->sc_data_direction == DMA_TO_DEVICE) {
                cflags = CF_WRITE;
-               sp->fcport->vha->hw->qla_stats.output_bytes +=
-                   scsi_bufflen(cmd);
+               vha->qla_stats.output_bytes += scsi_bufflen(cmd);
        } else if (cmd->sc_data_direction == DMA_FROM_DEVICE) {
                cflags = CF_READ;
-               sp->fcport->vha->hw->qla_stats.input_bytes +=
-                   scsi_bufflen(cmd);
+               vha->qla_stats.input_bytes += scsi_bufflen(cmd);
        }
        return (cflags);
 }
@@ -385,9 +385,10 @@ qla2x00_start_scsi(srb_t *sp)
                else
                        req->cnt = req->length -
                            (req->ring_index - cnt);
+               /* If still no head room then bail out */
+               if (req->cnt < (req_cnt + 2))
+                       goto queuing_error;
        }
-       if (req->cnt < (req_cnt + 2))
-               goto queuing_error;
 
        /* Build command packet */
        req->current_outstanding_cmd = handle;
@@ -470,7 +471,7 @@ queuing_error:
 /**
  * qla2x00_start_iocbs() - Execute the IOCB command
  */
-static void
+void
 qla2x00_start_iocbs(struct scsi_qla_host *vha, struct req_que *req)
 {
        struct qla_hw_data *ha = vha->hw;
@@ -571,6 +572,29 @@ qla2x00_marker(struct scsi_qla_host *vha, struct req_que *req,
        return (ret);
 }
 
+/*
+ * qla2x00_issue_marker
+ *
+ * Issue marker
+ * Caller CAN have hardware lock held as specified by ha_locked parameter.
+ * Might release it, then reaquire.
+ */
+int qla2x00_issue_marker(scsi_qla_host_t *vha, int ha_locked)
+{
+       if (ha_locked) {
+               if (__qla2x00_marker(vha, vha->req, vha->req->rsp, 0, 0,
+                                       MK_SYNC_ALL) != QLA_SUCCESS)
+                       return QLA_FUNCTION_FAILED;
+       } else {
+               if (qla2x00_marker(vha, vha->req, vha->req->rsp, 0, 0,
+                                       MK_SYNC_ALL) != QLA_SUCCESS)
+                       return QLA_FUNCTION_FAILED;
+       }
+       vha->marker_needed = 0;
+
+       return QLA_SUCCESS;
+}
+
 /**
  * qla24xx_calc_iocbs() - Determine number of Command Type 3 and
  * Continuation Type 1 IOCBs to allocate.
@@ -629,11 +653,11 @@ qla24xx_build_scsi_type_6_iocbs(srb_t *sp, struct cmd_type_6 *cmd_pkt,
        if (cmd->sc_data_direction == DMA_TO_DEVICE) {
                cmd_pkt->control_flags =
                    __constant_cpu_to_le16(CF_WRITE_DATA);
-               ha->qla_stats.output_bytes += scsi_bufflen(cmd);
+               vha->qla_stats.output_bytes += scsi_bufflen(cmd);
        } else if (cmd->sc_data_direction == DMA_FROM_DEVICE) {
                cmd_pkt->control_flags =
                    __constant_cpu_to_le16(CF_READ_DATA);
-               ha->qla_stats.input_bytes += scsi_bufflen(cmd);
+               vha->qla_stats.input_bytes += scsi_bufflen(cmd);
        }
 
        cur_seg = scsi_sglist(cmd);
@@ -745,13 +769,11 @@ qla24xx_build_scsi_iocbs(srb_t *sp, struct cmd_type_7 *cmd_pkt,
        if (cmd->sc_data_direction == DMA_TO_DEVICE) {
                cmd_pkt->task_mgmt_flags =
                    __constant_cpu_to_le16(TMF_WRITE_DATA);
-               sp->fcport->vha->hw->qla_stats.output_bytes +=
-                   scsi_bufflen(cmd);
+               vha->qla_stats.output_bytes += scsi_bufflen(cmd);
        } else if (cmd->sc_data_direction == DMA_FROM_DEVICE) {
                cmd_pkt->task_mgmt_flags =
                    __constant_cpu_to_le16(TMF_READ_DATA);
-               sp->fcport->vha->hw->qla_stats.input_bytes +=
-                   scsi_bufflen(cmd);
+               vha->qla_stats.input_bytes += scsi_bufflen(cmd);
        }
 
        /* One DSD is available in the Command Type 3 IOCB */
@@ -1245,7 +1267,7 @@ qla24xx_build_scsi_crc_2_iocbs(srb_t *sp, struct cmd_type_crc_2 *cmd_pkt,
                return QLA_SUCCESS;
        }
 
-       cmd_pkt->vp_index = sp->fcport->vp_idx;
+       cmd_pkt->vp_index = sp->fcport->vha->vp_idx;
 
        /* Set transfer direction */
        if (cmd->sc_data_direction == DMA_TO_DEVICE) {
@@ -1502,9 +1524,9 @@ qla24xx_start_scsi(srb_t *sp)
                else
                        req->cnt = req->length -
                                (req->ring_index - cnt);
+               if (req->cnt < (req_cnt + 2))
+                       goto queuing_error;
        }
-       if (req->cnt < (req_cnt + 2))
-               goto queuing_error;
 
        /* Build command packet. */
        req->current_outstanding_cmd = handle;
@@ -1527,7 +1549,7 @@ qla24xx_start_scsi(srb_t *sp)
        cmd_pkt->port_id[0] = sp->fcport->d_id.b.al_pa;
        cmd_pkt->port_id[1] = sp->fcport->d_id.b.area;
        cmd_pkt->port_id[2] = sp->fcport->d_id.b.domain;
-       cmd_pkt->vp_index = sp->fcport->vp_idx;
+       cmd_pkt->vp_index = sp->fcport->vha->vp_idx;
 
        int_to_scsilun(cmd->device->lun, &cmd_pkt->lun);
        host_to_fcp_swap((uint8_t *)&cmd_pkt->lun, sizeof(cmd_pkt->lun));
@@ -1717,11 +1739,10 @@ qla24xx_dif_start_scsi(srb_t *sp)
                else
                        req->cnt = req->length -
                                (req->ring_index - cnt);
+               if (req->cnt < (req_cnt + 2))
+                       goto queuing_error;
        }
 
-       if (req->cnt < (req_cnt + 2))
-               goto queuing_error;
-
        status |= QDSS_GOT_Q_SPACE;
 
        /* Build header part of command packet (excluding the OPCODE). */
@@ -1898,7 +1919,7 @@ qla24xx_login_iocb(srb_t *sp, struct logio_entry_24xx *logio)
        logio->port_id[0] = sp->fcport->d_id.b.al_pa;
        logio->port_id[1] = sp->fcport->d_id.b.area;
        logio->port_id[2] = sp->fcport->d_id.b.domain;
-       logio->vp_index = sp->fcport->vp_idx;
+       logio->vp_index = sp->fcport->vha->vp_idx;
 }
 
 static void
@@ -1922,7 +1943,7 @@ qla2x00_login_iocb(srb_t *sp, struct mbx_entry *mbx)
        mbx->mb2 = cpu_to_le16(sp->fcport->d_id.b.domain);
        mbx->mb3 = cpu_to_le16(sp->fcport->d_id.b.area << 8 |
            sp->fcport->d_id.b.al_pa);
-       mbx->mb9 = cpu_to_le16(sp->fcport->vp_idx);
+       mbx->mb9 = cpu_to_le16(sp->fcport->vha->vp_idx);
 }
 
 static void
@@ -1935,7 +1956,7 @@ qla24xx_logout_iocb(srb_t *sp, struct logio_entry_24xx *logio)
        logio->port_id[0] = sp->fcport->d_id.b.al_pa;
        logio->port_id[1] = sp->fcport->d_id.b.area;
        logio->port_id[2] = sp->fcport->d_id.b.domain;
-       logio->vp_index = sp->fcport->vp_idx;
+       logio->vp_index = sp->fcport->vha->vp_idx;
 }
 
 static void
@@ -1952,7 +1973,7 @@ qla2x00_logout_iocb(srb_t *sp, struct mbx_entry *mbx)
        mbx->mb2 = cpu_to_le16(sp->fcport->d_id.b.domain);
        mbx->mb3 = cpu_to_le16(sp->fcport->d_id.b.area << 8 |
            sp->fcport->d_id.b.al_pa);
-       mbx->mb9 = cpu_to_le16(sp->fcport->vp_idx);
+       mbx->mb9 = cpu_to_le16(sp->fcport->vha->vp_idx);
        /* Implicit: mbx->mbx10 = 0. */
 }
 
@@ -1962,7 +1983,7 @@ qla24xx_adisc_iocb(srb_t *sp, struct logio_entry_24xx *logio)
        logio->entry_type = LOGINOUT_PORT_IOCB_TYPE;
        logio->control_flags = cpu_to_le16(LCF_COMMAND_ADISC);
        logio->nport_handle = cpu_to_le16(sp->fcport->loop_id);
-       logio->vp_index = sp->fcport->vp_idx;
+       logio->vp_index = sp->fcport->vha->vp_idx;
 }
 
 static void
@@ -1983,7 +2004,7 @@ qla2x00_adisc_iocb(srb_t *sp, struct mbx_entry *mbx)
        mbx->mb3 = cpu_to_le16(LSW(ha->async_pd_dma));
        mbx->mb6 = cpu_to_le16(MSW(MSD(ha->async_pd_dma)));
        mbx->mb7 = cpu_to_le16(LSW(MSD(ha->async_pd_dma)));
-       mbx->mb9 = cpu_to_le16(sp->fcport->vp_idx);
+       mbx->mb9 = cpu_to_le16(sp->fcport->vha->vp_idx);
 }
 
 static void
@@ -2009,7 +2030,7 @@ qla24xx_tm_iocb(srb_t *sp, struct tsk_mgmt_entry *tsk)
        tsk->port_id[0] = fcport->d_id.b.al_pa;
        tsk->port_id[1] = fcport->d_id.b.area;
        tsk->port_id[2] = fcport->d_id.b.domain;
-       tsk->vp_index = fcport->vp_idx;
+       tsk->vp_index = fcport->vha->vp_idx;
 
        if (flags == TCF_LUN_RESET) {
                int_to_scsilun(lun, &tsk->lun);
@@ -2030,7 +2051,7 @@ qla24xx_els_iocb(srb_t *sp, struct els_entry_24xx *els_iocb)
         els_iocb->handle = sp->handle;
         els_iocb->nport_handle = cpu_to_le16(sp->fcport->loop_id);
         els_iocb->tx_dsd_count = __constant_cpu_to_le16(bsg_job->request_payload.sg_cnt);
-        els_iocb->vp_index = sp->fcport->vp_idx;
+       els_iocb->vp_index = sp->fcport->vha->vp_idx;
         els_iocb->sof_type = EST_SOFI3;
         els_iocb->rx_dsd_count = __constant_cpu_to_le16(bsg_job->reply_payload.sg_cnt);
 
@@ -2160,7 +2181,7 @@ qla24xx_ct_iocb(srb_t *sp, struct ct_entry_24xx *ct_iocb)
         ct_iocb->handle = sp->handle;
 
        ct_iocb->nport_handle = cpu_to_le16(sp->fcport->loop_id);
-       ct_iocb->vp_index = sp->fcport->vp_idx;
+       ct_iocb->vp_index = sp->fcport->vha->vp_idx;
         ct_iocb->comp_status = __constant_cpu_to_le16(0);
 
        ct_iocb->cmd_dsd_count =
@@ -2343,11 +2364,10 @@ sufficient_dsds:
                        else
                                req->cnt = req->length -
                                        (req->ring_index - cnt);
+                       if (req->cnt < (req_cnt + 2))
+                               goto queuing_error;
                }
 
-               if (req->cnt < (req_cnt + 2))
-                       goto queuing_error;
-
                ctx = sp->u.scmd.ctx =
                    mempool_alloc(ha->ctx_mempool, GFP_ATOMIC);
                if (!ctx) {
@@ -2362,7 +2382,7 @@ sufficient_dsds:
                if (!ctx->fcp_cmnd) {
                        ql_log(ql_log_fatal, vha, 0x3011,
                            "Failed to allocate fcp_cmnd for cmd=%p.\n", cmd);
-                       goto queuing_error_fcp_cmnd;
+                       goto queuing_error;
                }
 
                /* Initialize the DSD list and dma handle */
@@ -2400,7 +2420,7 @@ sufficient_dsds:
                cmd_pkt->port_id[0] = sp->fcport->d_id.b.al_pa;
                cmd_pkt->port_id[1] = sp->fcport->d_id.b.area;
                cmd_pkt->port_id[2] = sp->fcport->d_id.b.domain;
-               cmd_pkt->vp_index = sp->fcport->vp_idx;
+               cmd_pkt->vp_index = sp->fcport->vha->vp_idx;
 
                /* Build IOCB segments */
                if (qla24xx_build_scsi_type_6_iocbs(sp, cmd_pkt, tot_dsds))
@@ -2489,7 +2509,7 @@ sufficient_dsds:
                cmd_pkt->port_id[0] = sp->fcport->d_id.b.al_pa;
                cmd_pkt->port_id[1] = sp->fcport->d_id.b.area;
                cmd_pkt->port_id[2] = sp->fcport->d_id.b.domain;
-               cmd_pkt->vp_index = sp->fcport->vp_idx;
+               cmd_pkt->vp_index = sp->fcport->vha->vp_idx;
 
                int_to_scsilun(cmd->device->lun, &cmd_pkt->lun);
                host_to_fcp_swap((uint8_t *)&cmd_pkt->lun,
index ce42288049b5a20a5c57646ecf077886a1239ebb..6f67a9d4998b6d43fbab53a0a75918c6f607fbf0 100644 (file)
@@ -5,6 +5,7 @@
  * See LICENSE.qla2xxx for copyright and licensing details.
  */
 #include "qla_def.h"
+#include "qla_target.h"
 
 #include <linux/delay.h>
 #include <linux/slab.h>
@@ -309,6 +310,28 @@ qla81xx_idc_event(scsi_qla_host_t *vha, uint16_t aen, uint16_t descr)
                    "IDC failed to post ACK.\n");
 }
 
+#define LS_UNKNOWN     2
+char *
+qla2x00_get_link_speed_str(struct qla_hw_data *ha)
+{
+       static char *link_speeds[] = {"1", "2", "?", "4", "8", "16", "10"};
+       char *link_speed;
+       int fw_speed = ha->link_data_rate;
+
+       if (IS_QLA2100(ha) || IS_QLA2200(ha))
+               link_speed = link_speeds[0];
+       else if (fw_speed == 0x13)
+               link_speed = link_speeds[6];
+       else {
+               link_speed = link_speeds[LS_UNKNOWN];
+               if (fw_speed < 6)
+                       link_speed =
+                           link_speeds[fw_speed];
+       }
+
+       return link_speed;
+}
+
 /**
  * qla2x00_async_event() - Process aynchronous events.
  * @ha: SCSI driver HA context
@@ -317,9 +340,6 @@ qla81xx_idc_event(scsi_qla_host_t *vha, uint16_t aen, uint16_t descr)
 void
 qla2x00_async_event(scsi_qla_host_t *vha, struct rsp_que *rsp, uint16_t *mb)
 {
-#define LS_UNKNOWN     2
-       static char *link_speeds[] = { "1", "2", "?", "4", "8", "16", "10" };
-       char            *link_speed;
        uint16_t        handle_cnt;
        uint16_t        cnt, mbx;
        uint32_t        handles[5];
@@ -454,8 +474,8 @@ skip_rio:
        case MBA_WAKEUP_THRES:          /* Request Queue Wake-up */
                ql_dbg(ql_dbg_async, vha, 0x5008,
                    "Asynchronous WAKEUP_THRES.\n");
-               break;
 
+               break;
        case MBA_LIP_OCCURRED:          /* Loop Initialization Procedure */
                ql_dbg(ql_dbg_async, vha, 0x5009,
                    "LIP occurred (%x).\n", mb[1]);
@@ -479,20 +499,14 @@ skip_rio:
                break;
 
        case MBA_LOOP_UP:               /* Loop Up Event */
-               if (IS_QLA2100(ha) || IS_QLA2200(ha)) {
-                       link_speed = link_speeds[0];
+               if (IS_QLA2100(ha) || IS_QLA2200(ha))
                        ha->link_data_rate = PORT_SPEED_1GB;
-               } else {
-                       link_speed = link_speeds[LS_UNKNOWN];
-                       if (mb[1] < 6)
-                               link_speed = link_speeds[mb[1]];
-                       else if (mb[1] == 0x13)
-                               link_speed = link_speeds[6];
+               else
                        ha->link_data_rate = mb[1];
-               }
 
                ql_dbg(ql_dbg_async, vha, 0x500a,
-                   "LOOP UP detected (%s Gbps).\n", link_speed);
+                   "LOOP UP detected (%s Gbps).\n",
+                   qla2x00_get_link_speed_str(ha));
 
                vha->flags.management_server_logged_in = 0;
                qla2x00_post_aen_work(vha, FCH_EVT_LINKUP, ha->link_data_rate);
@@ -638,6 +652,8 @@ skip_rio:
                        ql_dbg(ql_dbg_async, vha, 0x5010,
                            "Port unavailable %04x %04x %04x.\n",
                            mb[1], mb[2], mb[3]);
+                       ql_log(ql_log_warn, vha, 0x505e,
+                           "Link is offline.\n");
 
                        if (atomic_read(&vha->loop_state) != LOOP_DOWN) {
                                atomic_set(&vha->loop_state, LOOP_DOWN);
@@ -670,12 +686,17 @@ skip_rio:
                        ql_dbg(ql_dbg_async, vha, 0x5011,
                            "Asynchronous PORT UPDATE ignored %04x/%04x/%04x.\n",
                            mb[1], mb[2], mb[3]);
+
+                       qlt_async_event(mb[0], vha, mb);
                        break;
                }
 
                ql_dbg(ql_dbg_async, vha, 0x5012,
                    "Port database changed %04x %04x %04x.\n",
                    mb[1], mb[2], mb[3]);
+               ql_log(ql_log_warn, vha, 0x505f,
+                   "Link is operational (%s Gbps).\n",
+                   qla2x00_get_link_speed_str(ha));
 
                /*
                 * Mark all devices as missing so we will login again.
@@ -684,8 +705,13 @@ skip_rio:
 
                qla2x00_mark_all_devices_lost(vha, 1);
 
+               if (vha->vp_idx == 0 && !qla_ini_mode_enabled(vha))
+                       set_bit(SCR_PENDING, &vha->dpc_flags);
+
                set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags);
                set_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags);
+
+               qlt_async_event(mb[0], vha, mb);
                break;
 
        case MBA_RSCN_UPDATE:           /* State Change Registration */
@@ -807,6 +833,8 @@ skip_rio:
                    mb[0], mb[1], mb[2], mb[3]);
        }
 
+       qlt_async_event(mb[0], vha, mb);
+
        if (!vha->vp_idx && ha->num_vhosts)
                qla2x00_alert_all_vps(rsp, mb);
 }
@@ -1172,6 +1200,9 @@ qla24xx_logio_entry(scsi_qla_host_t *vha, struct req_que *req,
                } else if (iop[0] & BIT_5)
                        fcport->port_type = FCT_INITIATOR;
 
+               if (iop[0] & BIT_7)
+                       fcport->flags |= FCF_CONF_COMP_SUPPORTED;
+
                if (logio->io_parameter[7] || logio->io_parameter[8])
                        fcport->supported_classes |= FC_COS_CLASS2;
                if (logio->io_parameter[9] || logio->io_parameter[10])
@@ -1986,6 +2017,9 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha,
 
                if (pkt->entry_status != 0) {
                        qla2x00_error_entry(vha, rsp, (sts_entry_t *) pkt);
+
+                       (void)qlt_24xx_process_response_error(vha, pkt);
+
                        ((response_t *)pkt)->signature = RESPONSE_PROCESSED;
                        wmb();
                        continue;
@@ -2016,6 +2050,14 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha,
                 case ELS_IOCB_TYPE:
                        qla24xx_els_ct_entry(vha, rsp->req, pkt, ELS_IOCB_TYPE);
                        break;
+               case ABTS_RECV_24XX:
+                       /* ensure that the ATIO queue is empty */
+                       qlt_24xx_process_atio_queue(vha);
+               case ABTS_RESP_24XX:
+               case CTIO_TYPE7:
+               case NOTIFY_ACK_TYPE:
+                       qlt_response_pkt_all_vps(vha, (response_t *)pkt);
+                       break;
                case MARKER_TYPE:
                        /* Do nothing in this case, this check is to prevent it
                         * from falling into default case
@@ -2168,6 +2210,13 @@ qla24xx_intr_handler(int irq, void *dev_id)
                case 0x14:
                        qla24xx_process_response_queue(vha, rsp);
                        break;
+               case 0x1C: /* ATIO queue updated */
+                       qlt_24xx_process_atio_queue(vha);
+                       break;
+               case 0x1D: /* ATIO and response queues updated */
+                       qlt_24xx_process_atio_queue(vha);
+                       qla24xx_process_response_queue(vha, rsp);
+                       break;
                default:
                        ql_dbg(ql_dbg_async, vha, 0x504f,
                            "Unrecognized interrupt type (%d).\n", stat * 0xff);
@@ -2312,6 +2361,13 @@ qla24xx_msix_default(int irq, void *dev_id)
                case 0x14:
                        qla24xx_process_response_queue(vha, rsp);
                        break;
+               case 0x1C: /* ATIO queue updated */
+                       qlt_24xx_process_atio_queue(vha);
+                       break;
+               case 0x1D: /* ATIO and response queues updated */
+                       qlt_24xx_process_atio_queue(vha);
+                       qla24xx_process_response_queue(vha, rsp);
+                       break;
                default:
                        ql_dbg(ql_dbg_async, vha, 0x5051,
                            "Unrecognized interrupt type (%d).\n", stat & 0xff);
@@ -2564,7 +2620,15 @@ void
 qla2x00_free_irqs(scsi_qla_host_t *vha)
 {
        struct qla_hw_data *ha = vha->hw;
-       struct rsp_que *rsp = ha->rsp_q_map[0];
+       struct rsp_que *rsp;
+
+       /*
+        * We need to check that ha->rsp_q_map is valid in case we are called
+        * from a probe failure context.
+        */
+       if (!ha->rsp_q_map || !ha->rsp_q_map[0])
+               return;
+       rsp = ha->rsp_q_map[0];
 
        if (ha->flags.msix_enabled)
                qla24xx_disable_msix(ha);
index b4a23394a7bd8f9a225ba2acd77ee19fb018f691..d5ce92c0a8fcef8e246ef25599e9d635a9a5b454 100644 (file)
@@ -5,6 +5,7 @@
  * See LICENSE.qla2xxx for copyright and licensing details.
  */
 #include "qla_def.h"
+#include "qla_target.h"
 
 #include <linux/delay.h>
 #include <linux/gfp.h>
@@ -270,11 +271,8 @@ qla2x00_mailbox_command(scsi_qla_host_t *vha, mbx_cmd_t *mcp)
                        ictrl = RD_REG_WORD(&reg->isp.ictrl);
                }
                ql_dbg(ql_dbg_mbx + ql_dbg_buffer, vha, 0x1119,
-                   "MBX Command timeout for cmd %x.\n", command);
-               ql_dbg(ql_dbg_mbx + ql_dbg_buffer, vha, 0x111a,
-                   "iocontrol=%x jiffies=%lx.\n", ictrl, jiffies);
-               ql_dbg(ql_dbg_mbx + ql_dbg_buffer, vha, 0x111b,
-                   "mb[0] = 0x%x.\n", mb0);
+                   "MBX Command timeout for cmd %x, iocontrol=%x jiffies=%lx "
+                   "mb[0]=0x%x\n", command, ictrl, jiffies, mb0);
                ql_dump_regs(ql_dbg_mbx + ql_dbg_buffer, vha, 0x1019);
 
                /*
@@ -320,7 +318,7 @@ qla2x00_mailbox_command(scsi_qla_host_t *vha, mbx_cmd_t *mcp)
                                            CRB_NIU_XG_PAUSE_CTL_P1);
                                }
                                ql_log(ql_log_info, base_vha, 0x101c,
-                                   "Mailbox cmd timeout occured, cmd=0x%x, "
+                                   "Mailbox cmd timeout occurred, cmd=0x%x, "
                                    "mb[0]=0x%x, eeh_busy=0x%x. Scheduling ISP "
                                    "abort.\n", command, mcp->mb[0],
                                    ha->flags.eeh_busy);
@@ -345,7 +343,7 @@ qla2x00_mailbox_command(scsi_qla_host_t *vha, mbx_cmd_t *mcp)
                                            CRB_NIU_XG_PAUSE_CTL_P1);
                                }
                                ql_log(ql_log_info, base_vha, 0x101e,
-                                   "Mailbox cmd timeout occured, cmd=0x%x, "
+                                   "Mailbox cmd timeout occurred, cmd=0x%x, "
                                    "mb[0]=0x%x. Scheduling ISP abort ",
                                    command, mcp->mb[0]);
                                set_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags);
@@ -390,7 +388,8 @@ qla2x00_load_ram(scsi_qla_host_t *vha, dma_addr_t req_dma, uint32_t risc_addr,
        mbx_cmd_t mc;
        mbx_cmd_t *mcp = &mc;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x1022, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1022,
+           "Entered %s.\n", __func__);
 
        if (MSW(risc_addr) || IS_FWI2_CAPABLE(ha)) {
                mcp->mb[0] = MBC_LOAD_RISC_RAM_EXTENDED;
@@ -424,7 +423,8 @@ qla2x00_load_ram(scsi_qla_host_t *vha, dma_addr_t req_dma, uint32_t risc_addr,
                ql_dbg(ql_dbg_mbx, vha, 0x1023,
                    "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x1024, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1024,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -454,7 +454,8 @@ qla2x00_execute_fw(scsi_qla_host_t *vha, uint32_t risc_addr)
        mbx_cmd_t mc;
        mbx_cmd_t *mcp = &mc;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x1025, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1025,
+           "Entered %s.\n", __func__);
 
        mcp->mb[0] = MBC_EXECUTE_FIRMWARE;
        mcp->out_mb = MBX_0;
@@ -489,10 +490,11 @@ qla2x00_execute_fw(scsi_qla_host_t *vha, uint32_t risc_addr)
                    "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
        } else {
                if (IS_FWI2_CAPABLE(ha)) {
-                       ql_dbg(ql_dbg_mbx, vha, 0x1027,
+                       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1027,
                            "Done exchanges=%x.\n", mcp->mb[1]);
                } else {
-                       ql_dbg(ql_dbg_mbx, vha, 0x1028, "Done %s.\n", __func__);
+                       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1028,
+                           "Done %s.\n", __func__);
                }
        }
 
@@ -523,7 +525,8 @@ qla2x00_get_fw_version(scsi_qla_host_t *vha)
        mbx_cmd_t       *mcp = &mc;
        struct qla_hw_data *ha = vha->hw;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x1029, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1029,
+           "Entered %s.\n", __func__);
 
        mcp->mb[0] = MBC_GET_FIRMWARE_VERSION;
        mcp->out_mb = MBX_0;
@@ -561,11 +564,11 @@ qla2x00_get_fw_version(scsi_qla_host_t *vha)
                        ha->fw_attributes_h = mcp->mb[15];
                        ha->fw_attributes_ext[0] = mcp->mb[16];
                        ha->fw_attributes_ext[1] = mcp->mb[17];
-                       ql_dbg(ql_dbg_mbx, vha, 0x1139,
+                       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1139,
                            "%s: FW_attributes Upper: 0x%x, Lower: 0x%x.\n",
                            __func__, mcp->mb[15], mcp->mb[6]);
                } else
-                       ql_dbg(ql_dbg_mbx, vha, 0x112f,
+                       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x112f,
                            "%s: FwAttributes [Upper]  invalid, MB6:%04x\n",
                            __func__, mcp->mb[6]);
        }
@@ -576,7 +579,8 @@ failed:
                ql_dbg(ql_dbg_mbx, vha, 0x102a, "Failed=%x.\n", rval);
        } else {
                /*EMPTY*/
-               ql_dbg(ql_dbg_mbx, vha, 0x102b, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x102b,
+                   "Done %s.\n", __func__);
        }
        return rval;
 }
@@ -602,7 +606,8 @@ qla2x00_get_fw_options(scsi_qla_host_t *vha, uint16_t *fwopts)
        mbx_cmd_t mc;
        mbx_cmd_t *mcp = &mc;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x102c, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x102c,
+           "Entered %s.\n", __func__);
 
        mcp->mb[0] = MBC_GET_FIRMWARE_OPTION;
        mcp->out_mb = MBX_0;
@@ -620,7 +625,8 @@ qla2x00_get_fw_options(scsi_qla_host_t *vha, uint16_t *fwopts)
                fwopts[2] = mcp->mb[2];
                fwopts[3] = mcp->mb[3];
 
-               ql_dbg(ql_dbg_mbx, vha, 0x102e, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x102e,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -648,7 +654,8 @@ qla2x00_set_fw_options(scsi_qla_host_t *vha, uint16_t *fwopts)
        mbx_cmd_t mc;
        mbx_cmd_t *mcp = &mc;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x102f, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x102f,
+           "Entered %s.\n", __func__);
 
        mcp->mb[0] = MBC_SET_FIRMWARE_OPTION;
        mcp->mb[1] = fwopts[1];
@@ -676,7 +683,8 @@ qla2x00_set_fw_options(scsi_qla_host_t *vha, uint16_t *fwopts)
                    "Failed=%x (%x/%x).\n", rval, mcp->mb[0], mcp->mb[1]);
        } else {
                /*EMPTY*/
-               ql_dbg(ql_dbg_mbx, vha, 0x1031, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1031,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -704,7 +712,8 @@ qla2x00_mbx_reg_test(scsi_qla_host_t *vha)
        mbx_cmd_t mc;
        mbx_cmd_t *mcp = &mc;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x1032, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1032,
+           "Entered %s.\n", __func__);
 
        mcp->mb[0] = MBC_MAILBOX_REGISTER_TEST;
        mcp->mb[1] = 0xAAAA;
@@ -734,7 +743,8 @@ qla2x00_mbx_reg_test(scsi_qla_host_t *vha)
                ql_dbg(ql_dbg_mbx, vha, 0x1033, "Failed=%x.\n", rval);
        } else {
                /*EMPTY*/
-               ql_dbg(ql_dbg_mbx, vha, 0x1034, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1034,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -762,7 +772,8 @@ qla2x00_verify_checksum(scsi_qla_host_t *vha, uint32_t risc_addr)
        mbx_cmd_t mc;
        mbx_cmd_t *mcp = &mc;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x1035, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1035,
+           "Entered %s.\n", __func__);
 
        mcp->mb[0] = MBC_VERIFY_CHECKSUM;
        mcp->out_mb = MBX_0;
@@ -787,7 +798,8 @@ qla2x00_verify_checksum(scsi_qla_host_t *vha, uint32_t risc_addr)
                    "Failed=%x chm sum=%x.\n", rval, IS_FWI2_CAPABLE(vha->hw) ?
                    (mcp->mb[2] << 16) | mcp->mb[1] : mcp->mb[1]);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x1037, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1037,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -819,7 +831,8 @@ qla2x00_issue_iocb_timeout(scsi_qla_host_t *vha, void *buffer,
        mbx_cmd_t       mc;
        mbx_cmd_t       *mcp = &mc;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x1038, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1038,
+           "Entered %s.\n", __func__);
 
        mcp->mb[0] = MBC_IOCB_COMMAND_A64;
        mcp->mb[1] = 0;
@@ -842,7 +855,8 @@ qla2x00_issue_iocb_timeout(scsi_qla_host_t *vha, void *buffer,
                /* Mask reserved bits. */
                sts_entry->entry_status &=
                    IS_FWI2_CAPABLE(vha->hw) ? RF_MASK_24XX : RF_MASK;
-               ql_dbg(ql_dbg_mbx, vha, 0x103a, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x103a,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -884,7 +898,8 @@ qla2x00_abort_command(srb_t *sp)
        struct req_que *req = vha->req;
        struct scsi_cmnd *cmd = GET_CMD_SP(sp);
 
-       ql_dbg(ql_dbg_mbx, vha, 0x103b, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x103b,
+           "Entered %s.\n", __func__);
 
        spin_lock_irqsave(&ha->hardware_lock, flags);
        for (handle = 1; handle < MAX_OUTSTANDING_COMMANDS; handle++) {
@@ -915,7 +930,8 @@ qla2x00_abort_command(srb_t *sp)
        if (rval != QLA_SUCCESS) {
                ql_dbg(ql_dbg_mbx, vha, 0x103c, "Failed=%x.\n", rval);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x103d, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x103d,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -934,7 +950,8 @@ qla2x00_abort_target(struct fc_port *fcport, unsigned int l, int tag)
        l = l;
        vha = fcport->vha;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x103e, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x103e,
+           "Entered %s.\n", __func__);
 
        req = vha->hw->req_q_map[0];
        rsp = req->rsp;
@@ -955,7 +972,8 @@ qla2x00_abort_target(struct fc_port *fcport, unsigned int l, int tag)
        mcp->flags = 0;
        rval = qla2x00_mailbox_command(vha, mcp);
        if (rval != QLA_SUCCESS) {
-               ql_dbg(ql_dbg_mbx, vha, 0x103f, "Failed=%x.\n", rval);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x103f,
+                   "Failed=%x.\n", rval);
        }
 
        /* Issue marker IOCB. */
@@ -965,7 +983,8 @@ qla2x00_abort_target(struct fc_port *fcport, unsigned int l, int tag)
                ql_dbg(ql_dbg_mbx, vha, 0x1040,
                    "Failed to issue marker IOCB (%x).\n", rval2);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x1041, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1041,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -983,7 +1002,8 @@ qla2x00_lun_reset(struct fc_port *fcport, unsigned int l, int tag)
 
        vha = fcport->vha;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x1042, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1042,
+           "Entered %s.\n", __func__);
 
        req = vha->hw->req_q_map[0];
        rsp = req->rsp;
@@ -1012,7 +1032,8 @@ qla2x00_lun_reset(struct fc_port *fcport, unsigned int l, int tag)
                ql_dbg(ql_dbg_mbx, vha, 0x1044,
                    "Failed to issue marker IOCB (%x).\n", rval2);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x1045, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1045,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -1046,7 +1067,8 @@ qla2x00_get_adapter_id(scsi_qla_host_t *vha, uint16_t *id, uint8_t *al_pa,
        mbx_cmd_t mc;
        mbx_cmd_t *mcp = &mc;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x1046, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1046,
+           "Entered %s.\n", __func__);
 
        mcp->mb[0] = MBC_GET_ADAPTER_LOOP_ID;
        mcp->mb[9] = vha->vp_idx;
@@ -1074,7 +1096,8 @@ qla2x00_get_adapter_id(scsi_qla_host_t *vha, uint16_t *id, uint8_t *al_pa,
                /*EMPTY*/
                ql_dbg(ql_dbg_mbx, vha, 0x1047, "Failed=%x.\n", rval);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x1048, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1048,
+                   "Done %s.\n", __func__);
 
                if (IS_CNA_CAPABLE(vha->hw)) {
                        vha->fcoe_vlan_id = mcp->mb[9] & 0xfff;
@@ -1115,7 +1138,8 @@ qla2x00_get_retry_cnt(scsi_qla_host_t *vha, uint8_t *retry_cnt, uint8_t *tov,
        mbx_cmd_t mc;
        mbx_cmd_t *mcp = &mc;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x1049, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1049,
+           "Entered %s.\n", __func__);
 
        mcp->mb[0] = MBC_GET_RETRY_COUNT;
        mcp->out_mb = MBX_0;
@@ -1138,7 +1162,7 @@ qla2x00_get_retry_cnt(scsi_qla_host_t *vha, uint8_t *retry_cnt, uint8_t *tov,
                        *tov = ratov;
                }
 
-               ql_dbg(ql_dbg_mbx, vha, 0x104b,
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x104b,
                    "Done %s mb3=%d ratov=%d.\n", __func__, mcp->mb[3], ratov);
        }
 
@@ -1170,7 +1194,8 @@ qla2x00_init_firmware(scsi_qla_host_t *vha, uint16_t size)
        mbx_cmd_t *mcp = &mc;
        struct qla_hw_data *ha = vha->hw;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x104c, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x104c,
+           "Entered %s.\n", __func__);
 
        if (IS_QLA82XX(ha) && ql2xdbwr)
                qla82xx_wr_32(ha, ha->nxdb_wr_ptr,
@@ -1213,9 +1238,100 @@ qla2x00_init_firmware(scsi_qla_host_t *vha, uint16_t size)
                    rval, mcp->mb[0], mcp->mb[1], mcp->mb[2], mcp->mb[3]);
        } else {
                /*EMPTY*/
-               ql_dbg(ql_dbg_mbx, vha, 0x104e, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x104e,
+                   "Done %s.\n", __func__);
+       }
+
+       return rval;
+}
+
+/*
+ * qla2x00_get_node_name_list
+ *      Issue get node name list mailbox command, kmalloc()
+ *      and return the resulting list. Caller must kfree() it!
+ *
+ * Input:
+ *      ha = adapter state pointer.
+ *      out_data = resulting list
+ *      out_len = length of the resulting list
+ *
+ * Returns:
+ *      qla2x00 local function return status code.
+ *
+ * Context:
+ *      Kernel context.
+ */
+int
+qla2x00_get_node_name_list(scsi_qla_host_t *vha, void **out_data, int *out_len)
+{
+       struct qla_hw_data *ha = vha->hw;
+       struct qla_port_24xx_data *list = NULL;
+       void *pmap;
+       mbx_cmd_t mc;
+       dma_addr_t pmap_dma;
+       ulong dma_size;
+       int rval, left;
+
+       left = 1;
+       while (left > 0) {
+               dma_size = left * sizeof(*list);
+               pmap = dma_alloc_coherent(&ha->pdev->dev, dma_size,
+                                        &pmap_dma, GFP_KERNEL);
+               if (!pmap) {
+                       ql_log(ql_log_warn, vha, 0x113f,
+                           "%s(%ld): DMA Alloc failed of %ld\n",
+                           __func__, vha->host_no, dma_size);
+                       rval = QLA_MEMORY_ALLOC_FAILED;
+                       goto out;
+               }
+
+               mc.mb[0] = MBC_PORT_NODE_NAME_LIST;
+               mc.mb[1] = BIT_1 | BIT_3;
+               mc.mb[2] = MSW(pmap_dma);
+               mc.mb[3] = LSW(pmap_dma);
+               mc.mb[6] = MSW(MSD(pmap_dma));
+               mc.mb[7] = LSW(MSD(pmap_dma));
+               mc.mb[8] = dma_size;
+               mc.out_mb = MBX_0|MBX_1|MBX_2|MBX_3|MBX_6|MBX_7|MBX_8;
+               mc.in_mb = MBX_0|MBX_1;
+               mc.tov = 30;
+               mc.flags = MBX_DMA_IN;
+
+               rval = qla2x00_mailbox_command(vha, &mc);
+               if (rval != QLA_SUCCESS) {
+                       if ((mc.mb[0] == MBS_COMMAND_ERROR) &&
+                           (mc.mb[1] == 0xA)) {
+                               left += le16_to_cpu(mc.mb[2]) /
+                                   sizeof(struct qla_port_24xx_data);
+                               goto restart;
+                       }
+                       goto out_free;
+               }
+
+               left = 0;
+
+               list = kzalloc(dma_size, GFP_KERNEL);
+               if (!list) {
+                       ql_log(ql_log_warn, vha, 0x1140,
+                           "%s(%ld): failed to allocate node names list "
+                           "structure.\n", __func__, vha->host_no);
+                       rval = QLA_MEMORY_ALLOC_FAILED;
+                       goto out_free;
+               }
+
+               memcpy(list, pmap, dma_size);
+restart:
+               dma_free_coherent(&ha->pdev->dev, dma_size, pmap, pmap_dma);
        }
 
+       *out_data = list;
+       *out_len = dma_size;
+
+out:
+       return rval;
+
+out_free:
+       dma_free_coherent(&ha->pdev->dev, dma_size, pmap, pmap_dma);
        return rval;
 }
 
@@ -1246,7 +1362,8 @@ qla2x00_get_port_database(scsi_qla_host_t *vha, fc_port_t *fcport, uint8_t opt)
        dma_addr_t pd_dma;
        struct qla_hw_data *ha = vha->hw;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x104f, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x104f,
+           "Entered %s.\n", __func__);
 
        pd24 = NULL;
        pd = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &pd_dma);
@@ -1326,6 +1443,13 @@ qla2x00_get_port_database(scsi_qla_host_t *vha, fc_port_t *fcport, uint8_t opt)
                        fcport->port_type = FCT_INITIATOR;
                else
                        fcport->port_type = FCT_TARGET;
+
+               /* Passback COS information. */
+               fcport->supported_classes = (pd24->flags & PDF_CLASS_2) ?
+                               FC_COS_CLASS2 : FC_COS_CLASS3;
+
+               if (pd24->prli_svc_param_word_3[0] & BIT_7)
+                       fcport->flags |= FCF_CONF_COMP_SUPPORTED;
        } else {
                uint64_t zero = 0;
 
@@ -1378,7 +1502,8 @@ gpd_error_out:
                    "Failed=%x mb[0]=%x mb[1]=%x.\n", rval,
                    mcp->mb[0], mcp->mb[1]);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x1053, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1053,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -1407,7 +1532,8 @@ qla2x00_get_firmware_state(scsi_qla_host_t *vha, uint16_t *states)
        mbx_cmd_t mc;
        mbx_cmd_t *mcp = &mc;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x1054, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1054,
+           "Entered %s.\n", __func__);
 
        mcp->mb[0] = MBC_GET_FIRMWARE_STATE;
        mcp->out_mb = MBX_0;
@@ -1433,7 +1559,8 @@ qla2x00_get_firmware_state(scsi_qla_host_t *vha, uint16_t *states)
                ql_dbg(ql_dbg_mbx, vha, 0x1055, "Failed=%x.\n", rval);
        } else {
                /*EMPTY*/
-               ql_dbg(ql_dbg_mbx, vha, 0x1056, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1056,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -1465,7 +1592,8 @@ qla2x00_get_port_name(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t *name,
        mbx_cmd_t mc;
        mbx_cmd_t *mcp = &mc;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x1057, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1057,
+           "Entered %s.\n", __func__);
 
        mcp->mb[0] = MBC_GET_PORT_NAME;
        mcp->mb[9] = vha->vp_idx;
@@ -1499,7 +1627,8 @@ qla2x00_get_port_name(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t *name,
                        name[7] = LSB(mcp->mb[7]);
                }
 
-               ql_dbg(ql_dbg_mbx, vha, 0x1059, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1059,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -1527,7 +1656,8 @@ qla2x00_lip_reset(scsi_qla_host_t *vha)
        mbx_cmd_t mc;
        mbx_cmd_t *mcp = &mc;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x105a, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x105a,
+           "Entered %s.\n", __func__);
 
        if (IS_CNA_CAPABLE(vha->hw)) {
                /* Logout across all FCFs. */
@@ -1564,7 +1694,8 @@ qla2x00_lip_reset(scsi_qla_host_t *vha)
                ql_dbg(ql_dbg_mbx, vha, 0x105b, "Failed=%x.\n", rval);
        } else {
                /*EMPTY*/
-               ql_dbg(ql_dbg_mbx, vha, 0x105c, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x105c,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -1596,9 +1727,10 @@ qla2x00_send_sns(scsi_qla_host_t *vha, dma_addr_t sns_phys_address,
        mbx_cmd_t mc;
        mbx_cmd_t *mcp = &mc;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x105d, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x105d,
+           "Entered %s.\n", __func__);
 
-       ql_dbg(ql_dbg_mbx, vha, 0x105e,
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x105e,
            "Retry cnt=%d ratov=%d total tov=%d.\n",
            vha->hw->retry_count, vha->hw->login_timeout, mcp->tov);
 
@@ -1622,7 +1754,8 @@ qla2x00_send_sns(scsi_qla_host_t *vha, dma_addr_t sns_phys_address,
                    rval, mcp->mb[0], mcp->mb[1]);
        } else {
                /*EMPTY*/
-               ql_dbg(ql_dbg_mbx, vha, 0x1060, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1060,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -1641,7 +1774,8 @@ qla24xx_login_fabric(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain,
        struct req_que *req;
        struct rsp_que *rsp;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x1061, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1061,
+           "Entered %s.\n", __func__);
 
        if (ha->flags.cpu_affinity_enabled)
                req = ha->req_q_map[0];
@@ -1715,7 +1849,8 @@ qla24xx_login_fabric(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain,
                        break;
                }
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x1066, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1066,
+                   "Done %s.\n", __func__);
 
                iop[0] = le32_to_cpu(lg->io_parameter[0]);
 
@@ -1733,6 +1868,10 @@ qla24xx_login_fabric(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain,
                        mb[10] |= BIT_0;        /* Class 2. */
                if (lg->io_parameter[9] || lg->io_parameter[10])
                        mb[10] |= BIT_1;        /* Class 3. */
+               if (lg->io_parameter[0] & __constant_cpu_to_le32(BIT_7))
+                       mb[10] |= BIT_7;        /* Confirmed Completion
+                                                * Allowed
+                                                */
        }
 
        dma_pool_free(ha->s_dma_pool, lg, lg_dma);
@@ -1770,7 +1909,8 @@ qla2x00_login_fabric(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain,
        mbx_cmd_t *mcp = &mc;
        struct qla_hw_data *ha = vha->hw;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x1067, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1067,
+           "Entered %s.\n", __func__);
 
        mcp->mb[0] = MBC_LOGIN_FABRIC_PORT;
        mcp->out_mb = MBX_3|MBX_2|MBX_1|MBX_0;
@@ -1818,7 +1958,8 @@ qla2x00_login_fabric(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain,
                    rval, mcp->mb[0], mcp->mb[1], mcp->mb[2]);
        } else {
                /*EMPTY*/
-               ql_dbg(ql_dbg_mbx, vha, 0x1069, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1069,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -1849,7 +1990,8 @@ qla2x00_login_local_device(scsi_qla_host_t *vha, fc_port_t *fcport,
        mbx_cmd_t *mcp = &mc;
        struct qla_hw_data *ha = vha->hw;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x106a, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x106a,
+           "Entered %s.\n", __func__);
 
        if (IS_FWI2_CAPABLE(ha))
                return qla24xx_login_fabric(vha, fcport->loop_id,
@@ -1891,7 +2033,8 @@ qla2x00_login_local_device(scsi_qla_host_t *vha, fc_port_t *fcport,
                    rval, mcp->mb[0], mcp->mb[1], mcp->mb[6], mcp->mb[7]);
        } else {
                /*EMPTY*/
-               ql_dbg(ql_dbg_mbx, vha, 0x106c, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x106c,
+                   "Done %s.\n", __func__);
        }
 
        return (rval);
@@ -1908,7 +2051,8 @@ qla24xx_fabric_logout(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain,
        struct req_que *req;
        struct rsp_que *rsp;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x106d, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x106d,
+           "Entered %s.\n", __func__);
 
        lg = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &lg_dma);
        if (lg == NULL) {
@@ -1952,7 +2096,8 @@ qla24xx_fabric_logout(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain,
                    le32_to_cpu(lg->io_parameter[1]));
        } else {
                /*EMPTY*/
-               ql_dbg(ql_dbg_mbx, vha, 0x1072, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1072,
+                   "Done %s.\n", __func__);
        }
 
        dma_pool_free(ha->s_dma_pool, lg, lg_dma);
@@ -1984,7 +2129,8 @@ qla2x00_fabric_logout(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain,
        mbx_cmd_t mc;
        mbx_cmd_t *mcp = &mc;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x1073, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1073,
+           "Entered %s.\n", __func__);
 
        mcp->mb[0] = MBC_LOGOUT_FABRIC_PORT;
        mcp->out_mb = MBX_1|MBX_0;
@@ -2007,7 +2153,8 @@ qla2x00_fabric_logout(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain,
                    "Failed=%x mb[1]=%x.\n", rval, mcp->mb[1]);
        } else {
                /*EMPTY*/
-               ql_dbg(ql_dbg_mbx, vha, 0x1075, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1075,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -2035,7 +2182,8 @@ qla2x00_full_login_lip(scsi_qla_host_t *vha)
        mbx_cmd_t mc;
        mbx_cmd_t *mcp = &mc;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x1076, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1076,
+           "Entered %s.\n", __func__);
 
        mcp->mb[0] = MBC_LIP_FULL_LOGIN;
        mcp->mb[1] = IS_FWI2_CAPABLE(vha->hw) ? BIT_3 : 0;
@@ -2052,7 +2200,8 @@ qla2x00_full_login_lip(scsi_qla_host_t *vha)
                ql_dbg(ql_dbg_mbx, vha, 0x1077, "Failed=%x.\n", rval);
        } else {
                /*EMPTY*/
-               ql_dbg(ql_dbg_mbx, vha, 0x1078, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1078,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -2078,7 +2227,8 @@ qla2x00_get_id_list(scsi_qla_host_t *vha, void *id_list, dma_addr_t id_list_dma,
        mbx_cmd_t mc;
        mbx_cmd_t *mcp = &mc;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x1079, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1079,
+           "Entered %s.\n", __func__);
 
        if (id_list == NULL)
                return QLA_FUNCTION_FAILED;
@@ -2110,7 +2260,8 @@ qla2x00_get_id_list(scsi_qla_host_t *vha, void *id_list, dma_addr_t id_list_dma,
                ql_dbg(ql_dbg_mbx, vha, 0x107a, "Failed=%x.\n", rval);
        } else {
                *entries = mcp->mb[1];
-               ql_dbg(ql_dbg_mbx, vha, 0x107b, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x107b,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -2138,7 +2289,8 @@ qla2x00_get_resource_cnts(scsi_qla_host_t *vha, uint16_t *cur_xchg_cnt,
        mbx_cmd_t mc;
        mbx_cmd_t *mcp = &mc;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x107c, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x107c,
+           "Entered %s.\n", __func__);
 
        mcp->mb[0] = MBC_GET_RESOURCE_COUNTS;
        mcp->out_mb = MBX_0;
@@ -2154,7 +2306,7 @@ qla2x00_get_resource_cnts(scsi_qla_host_t *vha, uint16_t *cur_xchg_cnt,
                ql_dbg(ql_dbg_mbx, vha, 0x107d,
                    "Failed mb[0]=%x.\n", mcp->mb[0]);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x107e,
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x107e,
                    "Done %s mb1=%x mb2=%x mb3=%x mb6=%x mb7=%x mb10=%x "
                    "mb11=%x mb12=%x.\n", __func__, mcp->mb[1], mcp->mb[2],
                    mcp->mb[3], mcp->mb[6], mcp->mb[7], mcp->mb[10],
@@ -2201,7 +2353,8 @@ qla2x00_get_fcal_position_map(scsi_qla_host_t *vha, char *pos_map)
        dma_addr_t pmap_dma;
        struct qla_hw_data *ha = vha->hw;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x107f, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x107f,
+           "Entered %s.\n", __func__);
 
        pmap = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &pmap_dma);
        if (pmap  == NULL) {
@@ -2224,7 +2377,7 @@ qla2x00_get_fcal_position_map(scsi_qla_host_t *vha, char *pos_map)
        rval = qla2x00_mailbox_command(vha, mcp);
 
        if (rval == QLA_SUCCESS) {
-               ql_dbg(ql_dbg_mbx, vha, 0x1081,
+               ql_dbg(ql_dbg_mbx + ql_dbg_buffer, vha, 0x1081,
                    "mb0/mb1=%x/%X FC/AL position map size (%x).\n",
                    mcp->mb[0], mcp->mb[1], (unsigned)pmap[0]);
                ql_dump_buffer(ql_dbg_mbx + ql_dbg_buffer, vha, 0x111d,
@@ -2238,7 +2391,8 @@ qla2x00_get_fcal_position_map(scsi_qla_host_t *vha, char *pos_map)
        if (rval != QLA_SUCCESS) {
                ql_dbg(ql_dbg_mbx, vha, 0x1082, "Failed=%x.\n", rval);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x1083, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1083,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -2267,7 +2421,8 @@ qla2x00_get_link_status(scsi_qla_host_t *vha, uint16_t loop_id,
        uint32_t *siter, *diter, dwords;
        struct qla_hw_data *ha = vha->hw;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x1084, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1084,
+           "Entered %s.\n", __func__);
 
        mcp->mb[0] = MBC_GET_LINK_STATUS;
        mcp->mb[2] = MSW(stats_dma);
@@ -2301,7 +2456,8 @@ qla2x00_get_link_status(scsi_qla_host_t *vha, uint16_t loop_id,
                        rval = QLA_FUNCTION_FAILED;
                } else {
                        /* Copy over data -- firmware data is LE. */
-                       ql_dbg(ql_dbg_mbx, vha, 0x1086, "Done %s.\n", __func__);
+                       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1086,
+                           "Done %s.\n", __func__);
                        dwords = offsetof(struct link_statistics, unused1) / 4;
                        siter = diter = &stats->link_fail_cnt;
                        while (dwords--)
@@ -2324,7 +2480,8 @@ qla24xx_get_isp_stats(scsi_qla_host_t *vha, struct link_statistics *stats,
        mbx_cmd_t *mcp = &mc;
        uint32_t *siter, *diter, dwords;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x1088, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1088,
+           "Entered %s.\n", __func__);
 
        mcp->mb[0] = MBC_GET_LINK_PRIV_STATS;
        mcp->mb[2] = MSW(stats_dma);
@@ -2346,7 +2503,8 @@ qla24xx_get_isp_stats(scsi_qla_host_t *vha, struct link_statistics *stats,
                            "Failed mb[0]=%x.\n", mcp->mb[0]);
                        rval = QLA_FUNCTION_FAILED;
                } else {
-                       ql_dbg(ql_dbg_mbx, vha, 0x108a, "Done %s.\n", __func__);
+                       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x108a,
+                           "Done %s.\n", __func__);
                        /* Copy over data -- firmware data is LE. */
                        dwords = sizeof(struct link_statistics) / 4;
                        siter = diter = &stats->link_fail_cnt;
@@ -2375,7 +2533,8 @@ qla24xx_abort_command(srb_t *sp)
        struct qla_hw_data *ha = vha->hw;
        struct req_que *req = vha->req;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x108c, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x108c,
+           "Entered %s.\n", __func__);
 
        spin_lock_irqsave(&ha->hardware_lock, flags);
        for (handle = 1; handle < MAX_OUTSTANDING_COMMANDS; handle++) {
@@ -2404,7 +2563,7 @@ qla24xx_abort_command(srb_t *sp)
        abt->port_id[0] = fcport->d_id.b.al_pa;
        abt->port_id[1] = fcport->d_id.b.area;
        abt->port_id[2] = fcport->d_id.b.domain;
-       abt->vp_index = fcport->vp_idx;
+       abt->vp_index = fcport->vha->vp_idx;
 
        abt->req_que_no = cpu_to_le16(req->id);
 
@@ -2423,7 +2582,8 @@ qla24xx_abort_command(srb_t *sp)
                    le16_to_cpu(abt->nport_handle));
                rval = QLA_FUNCTION_FAILED;
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x1091, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1091,
+                   "Done %s.\n", __func__);
        }
 
        dma_pool_free(ha->s_dma_pool, abt, abt_dma);
@@ -2455,7 +2615,8 @@ __qla24xx_issue_tmf(char *name, uint32_t type, struct fc_port *fcport,
        ha = vha->hw;
        req = vha->req;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x1092, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1092,
+           "Entered %s.\n", __func__);
 
        if (ha->flags.cpu_affinity_enabled)
                rsp = ha->rsp_q_map[tag + 1];
@@ -2478,7 +2639,7 @@ __qla24xx_issue_tmf(char *name, uint32_t type, struct fc_port *fcport,
        tsk->p.tsk.port_id[0] = fcport->d_id.b.al_pa;
        tsk->p.tsk.port_id[1] = fcport->d_id.b.area;
        tsk->p.tsk.port_id[2] = fcport->d_id.b.domain;
-       tsk->p.tsk.vp_index = fcport->vp_idx;
+       tsk->p.tsk.vp_index = fcport->vha->vp_idx;
        if (type == TCF_LUN_RESET) {
                int_to_scsilun(l, &tsk->p.tsk.lun);
                host_to_fcp_swap((uint8_t *)&tsk->p.tsk.lun,
@@ -2504,7 +2665,7 @@ __qla24xx_issue_tmf(char *name, uint32_t type, struct fc_port *fcport,
        } else if (le16_to_cpu(sts->scsi_status) &
            SS_RESPONSE_INFO_LEN_VALID) {
                if (le32_to_cpu(sts->rsp_data_len) < 4) {
-                       ql_dbg(ql_dbg_mbx, vha, 0x1097,
+                       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1097,
                            "Ignoring inconsistent data length -- not enough "
                            "response info (%d).\n",
                            le32_to_cpu(sts->rsp_data_len));
@@ -2523,7 +2684,8 @@ __qla24xx_issue_tmf(char *name, uint32_t type, struct fc_port *fcport,
                ql_dbg(ql_dbg_mbx, vha, 0x1099,
                    "Failed to issue marker IOCB (%x).\n", rval2);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x109a, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x109a,
+                   "Done %s.\n", __func__);
        }
 
        dma_pool_free(ha->s_dma_pool, tsk, tsk_dma);
@@ -2564,7 +2726,8 @@ qla2x00_system_error(scsi_qla_host_t *vha)
        if (!IS_QLA23XX(ha) && !IS_FWI2_CAPABLE(ha))
                return QLA_FUNCTION_FAILED;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x109b, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x109b,
+           "Entered %s.\n", __func__);
 
        mcp->mb[0] = MBC_GEN_SYSTEM_ERROR;
        mcp->out_mb = MBX_0;
@@ -2576,7 +2739,8 @@ qla2x00_system_error(scsi_qla_host_t *vha)
        if (rval != QLA_SUCCESS) {
                ql_dbg(ql_dbg_mbx, vha, 0x109c, "Failed=%x.\n", rval);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x109d, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x109d,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -2596,7 +2760,8 @@ qla2x00_set_serdes_params(scsi_qla_host_t *vha, uint16_t sw_em_1g,
        mbx_cmd_t mc;
        mbx_cmd_t *mcp = &mc;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x109e, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x109e,
+           "Entered %s.\n", __func__);
 
        mcp->mb[0] = MBC_SERDES_PARAMS;
        mcp->mb[1] = BIT_0;
@@ -2615,7 +2780,8 @@ qla2x00_set_serdes_params(scsi_qla_host_t *vha, uint16_t sw_em_1g,
                    "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
        } else {
                /*EMPTY*/
-               ql_dbg(ql_dbg_mbx, vha, 0x10a0, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10a0,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -2631,7 +2797,8 @@ qla2x00_stop_firmware(scsi_qla_host_t *vha)
        if (!IS_FWI2_CAPABLE(vha->hw))
                return QLA_FUNCTION_FAILED;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x10a1, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10a1,
+           "Entered %s.\n", __func__);
 
        mcp->mb[0] = MBC_STOP_FIRMWARE;
        mcp->mb[1] = 0;
@@ -2646,7 +2813,8 @@ qla2x00_stop_firmware(scsi_qla_host_t *vha)
                if (mcp->mb[0] == MBS_INVALID_COMMAND)
                        rval = QLA_INVALID_COMMAND;
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x10a3, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10a3,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -2660,7 +2828,8 @@ qla2x00_enable_eft_trace(scsi_qla_host_t *vha, dma_addr_t eft_dma,
        mbx_cmd_t mc;
        mbx_cmd_t *mcp = &mc;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x10a4, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10a4,
+           "Entered %s.\n", __func__);
 
        if (!IS_FWI2_CAPABLE(vha->hw))
                return QLA_FUNCTION_FAILED;
@@ -2686,7 +2855,8 @@ qla2x00_enable_eft_trace(scsi_qla_host_t *vha, dma_addr_t eft_dma,
                    "Failed=%x mb[0]=%x mb[1]=%x.\n",
                    rval, mcp->mb[0], mcp->mb[1]);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x10a6, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10a6,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -2699,7 +2869,8 @@ qla2x00_disable_eft_trace(scsi_qla_host_t *vha)
        mbx_cmd_t mc;
        mbx_cmd_t *mcp = &mc;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x10a7, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10a7,
+           "Entered %s.\n", __func__);
 
        if (!IS_FWI2_CAPABLE(vha->hw))
                return QLA_FUNCTION_FAILED;
@@ -2719,7 +2890,8 @@ qla2x00_disable_eft_trace(scsi_qla_host_t *vha)
                    "Failed=%x mb[0]=%x mb[1]=%x.\n",
                    rval, mcp->mb[0], mcp->mb[1]);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x10a9, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10a9,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -2733,7 +2905,8 @@ qla2x00_enable_fce_trace(scsi_qla_host_t *vha, dma_addr_t fce_dma,
        mbx_cmd_t mc;
        mbx_cmd_t *mcp = &mc;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x10aa, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10aa,
+           "Entered %s.\n", __func__);
 
        if (!IS_QLA25XX(vha->hw) && !IS_QLA81XX(vha->hw) &&
            !IS_QLA83XX(vha->hw))
@@ -2764,7 +2937,8 @@ qla2x00_enable_fce_trace(scsi_qla_host_t *vha, dma_addr_t fce_dma,
                    "Failed=%x mb[0]=%x mb[1]=%x.\n",
                    rval, mcp->mb[0], mcp->mb[1]);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x10ac, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10ac,
+                   "Done %s.\n", __func__);
 
                if (mb)
                        memcpy(mb, mcp->mb, 8 * sizeof(*mb));
@@ -2782,7 +2956,8 @@ qla2x00_disable_fce_trace(scsi_qla_host_t *vha, uint64_t *wr, uint64_t *rd)
        mbx_cmd_t mc;
        mbx_cmd_t *mcp = &mc;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x10ad, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10ad,
+           "Entered %s.\n", __func__);
 
        if (!IS_FWI2_CAPABLE(vha->hw))
                return QLA_FUNCTION_FAILED;
@@ -2804,7 +2979,8 @@ qla2x00_disable_fce_trace(scsi_qla_host_t *vha, uint64_t *wr, uint64_t *rd)
                    "Failed=%x mb[0]=%x mb[1]=%x.\n",
                    rval, mcp->mb[0], mcp->mb[1]);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x10af, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10af,
+                   "Done %s.\n", __func__);
 
                if (wr)
                        *wr = (uint64_t) mcp->mb[5] << 48 |
@@ -2829,7 +3005,8 @@ qla2x00_get_idma_speed(scsi_qla_host_t *vha, uint16_t loop_id,
        mbx_cmd_t mc;
        mbx_cmd_t *mcp = &mc;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x10b0, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10b0,
+           "Entered %s.\n", __func__);
 
        if (!IS_IIDMA_CAPABLE(vha->hw))
                return QLA_FUNCTION_FAILED;
@@ -2854,7 +3031,8 @@ qla2x00_get_idma_speed(scsi_qla_host_t *vha, uint16_t loop_id,
        if (rval != QLA_SUCCESS) {
                ql_dbg(ql_dbg_mbx, vha, 0x10b1, "Failed=%x.\n", rval);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x10b2, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10b2,
+                   "Done %s.\n", __func__);
                if (port_speed)
                        *port_speed = mcp->mb[3];
        }
@@ -2870,7 +3048,8 @@ qla2x00_set_idma_speed(scsi_qla_host_t *vha, uint16_t loop_id,
        mbx_cmd_t mc;
        mbx_cmd_t *mcp = &mc;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x10b3, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10b3,
+           "Entered %s.\n", __func__);
 
        if (!IS_IIDMA_CAPABLE(vha->hw))
                return QLA_FUNCTION_FAILED;
@@ -2897,9 +3076,11 @@ qla2x00_set_idma_speed(scsi_qla_host_t *vha, uint16_t loop_id,
        }
 
        if (rval != QLA_SUCCESS) {
-               ql_dbg(ql_dbg_mbx, vha, 0x10b4, "Failed=%x.\n", rval);
+               ql_dbg(ql_dbg_mbx, vha, 0x10b4,
+                   "Failed=%x.\n", rval);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x10b5, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10b5,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -2915,24 +3096,25 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha,
        scsi_qla_host_t *vp;
        unsigned long   flags;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x10b6, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10b6,
+           "Entered %s.\n", __func__);
 
        if (rptid_entry->entry_status != 0)
                return;
 
        if (rptid_entry->format == 0) {
-               ql_dbg(ql_dbg_mbx, vha, 0x10b7,
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10b7,
                    "Format 0 : Number of VPs setup %d, number of "
                    "VPs acquired %d.\n",
                    MSB(le16_to_cpu(rptid_entry->vp_count)),
                    LSB(le16_to_cpu(rptid_entry->vp_count)));
-               ql_dbg(ql_dbg_mbx, vha, 0x10b8,
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10b8,
                    "Primary port id %02x%02x%02x.\n",
                    rptid_entry->port_id[2], rptid_entry->port_id[1],
                    rptid_entry->port_id[0]);
        } else if (rptid_entry->format == 1) {
                vp_idx = LSB(stat);
-               ql_dbg(ql_dbg_mbx, vha, 0x10b9,
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10b9,
                    "Format 1: VP[%d] enabled - status %d - with "
                    "port id %02x%02x%02x.\n", vp_idx, MSB(stat),
                    rptid_entry->port_id[2], rptid_entry->port_id[1],
@@ -2999,7 +3181,8 @@ qla24xx_modify_vp_config(scsi_qla_host_t *vha)
 
        /* This can be called by the parent */
 
-       ql_dbg(ql_dbg_mbx, vha, 0x10bb, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10bb,
+           "Entered %s.\n", __func__);
 
        vpmod = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &vpmod_dma);
        if (!vpmod) {
@@ -3015,6 +3198,9 @@ qla24xx_modify_vp_config(scsi_qla_host_t *vha)
        vpmod->vp_count = 1;
        vpmod->vp_index1 = vha->vp_idx;
        vpmod->options_idx1 = BIT_3|BIT_4|BIT_5;
+
+       qlt_modify_vp_config(vha, vpmod);
+
        memcpy(vpmod->node_name_idx1, vha->node_name, WWN_SIZE);
        memcpy(vpmod->port_name_idx1, vha->port_name, WWN_SIZE);
        vpmod->entry_count = 1;
@@ -3035,7 +3221,8 @@ qla24xx_modify_vp_config(scsi_qla_host_t *vha)
                rval = QLA_FUNCTION_FAILED;
        } else {
                /* EMPTY */
-               ql_dbg(ql_dbg_mbx, vha, 0x10c0, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10c0,
+                   "Done %s.\n", __func__);
                fc_vport_set_state(vha->fc_vport, FC_VPORT_INITIALIZING);
        }
        dma_pool_free(ha->s_dma_pool, vpmod, vpmod_dma);
@@ -3069,7 +3256,7 @@ qla24xx_control_vp(scsi_qla_host_t *vha, int cmd)
        int     vp_index = vha->vp_idx;
        struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev);
 
-       ql_dbg(ql_dbg_mbx, vha, 0x10c1,
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10c1,
            "Entered %s enabling index %d.\n", __func__, vp_index);
 
        if (vp_index == 0 || vp_index >= ha->max_npiv_vports)
@@ -3112,7 +3299,8 @@ qla24xx_control_vp(scsi_qla_host_t *vha, int cmd)
                    le16_to_cpu(vce->comp_status));
                rval = QLA_FUNCTION_FAILED;
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x10c6, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10c6,
+                   "Done %s.\n", __func__);
        }
 
        dma_pool_free(ha->s_dma_pool, vce, vce_dma);
@@ -3149,14 +3337,8 @@ qla2x00_send_change_request(scsi_qla_host_t *vha, uint16_t format,
        mbx_cmd_t mc;
        mbx_cmd_t *mcp = &mc;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x10c7, "Entered %s.\n", __func__);
-
-       /*
-        * This command is implicitly executed by firmware during login for the
-        * physical hosts
-        */
-       if (vp_idx == 0)
-               return QLA_FUNCTION_FAILED;
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10c7,
+           "Entered %s.\n", __func__);
 
        mcp->mb[0] = MBC_SEND_CHANGE_REQUEST;
        mcp->mb[1] = format;
@@ -3185,7 +3367,8 @@ qla2x00_dump_ram(scsi_qla_host_t *vha, dma_addr_t req_dma, uint32_t addr,
        mbx_cmd_t mc;
        mbx_cmd_t *mcp = &mc;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x1009, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1009,
+           "Entered %s.\n", __func__);
 
        if (MSW(addr) || IS_FWI2_CAPABLE(vha->hw)) {
                mcp->mb[0] = MBC_DUMP_RISC_RAM_EXTENDED;
@@ -3219,7 +3402,8 @@ qla2x00_dump_ram(scsi_qla_host_t *vha, dma_addr_t req_dma, uint32_t addr,
                ql_dbg(ql_dbg_mbx, vha, 0x1008,
                    "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x1007, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1007,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -3244,7 +3428,8 @@ qla84xx_verify_chip(struct scsi_qla_host *vha, uint16_t *status)
        unsigned long flags;
        struct qla_hw_data *ha = vha->hw;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x10c8, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10c8,
+           "Entered %s.\n", __func__);
 
        mn = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &mn_dma);
        if (mn == NULL) {
@@ -3285,7 +3470,7 @@ qla84xx_verify_chip(struct scsi_qla_host *vha, uint16_t *status)
                status[0] = le16_to_cpu(mn->p.rsp.comp_status);
                status[1] = status[0] == CS_VCS_CHIP_FAILURE ?
                    le16_to_cpu(mn->p.rsp.failure_code) : 0;
-               ql_dbg(ql_dbg_mbx, vha, 0x10ce,
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10ce,
                    "cs=%x fc=%x.\n", status[0], status[1]);
 
                if (status[0] != CS_COMPLETE) {
@@ -3299,7 +3484,7 @@ qla84xx_verify_chip(struct scsi_qla_host *vha, uint16_t *status)
                                retry = 1;
                        }
                } else {
-                       ql_dbg(ql_dbg_mbx, vha, 0x10d0,
+                       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10d0,
                            "Firmware updated to %x.\n",
                            le32_to_cpu(mn->p.rsp.fw_ver));
 
@@ -3316,9 +3501,11 @@ verify_done:
        dma_pool_free(ha->s_dma_pool, mn, mn_dma);
 
        if (rval != QLA_SUCCESS) {
-               ql_dbg(ql_dbg_mbx, vha, 0x10d1, "Failed=%x.\n", rval);
+               ql_dbg(ql_dbg_mbx, vha, 0x10d1,
+                   "Failed=%x.\n", rval);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x10d2, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10d2,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -3334,7 +3521,8 @@ qla25xx_init_req_que(struct scsi_qla_host *vha, struct req_que *req)
        struct device_reg_25xxmq __iomem *reg;
        struct qla_hw_data *ha = vha->hw;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x10d3, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10d3,
+           "Entered %s.\n", __func__);
 
        mcp->mb[0] = MBC_INITIALIZE_MULTIQ;
        mcp->mb[1] = req->options;
@@ -3388,7 +3576,8 @@ qla25xx_init_req_que(struct scsi_qla_host *vha, struct req_que *req)
                ql_dbg(ql_dbg_mbx, vha, 0x10d4,
                    "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x10d5, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10d5,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -3404,7 +3593,8 @@ qla25xx_init_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp)
        struct device_reg_25xxmq __iomem *reg;
        struct qla_hw_data *ha = vha->hw;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x10d6, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10d6,
+           "Entered %s.\n", __func__);
 
        mcp->mb[0] = MBC_INITIALIZE_MULTIQ;
        mcp->mb[1] = rsp->options;
@@ -3456,7 +3646,8 @@ qla25xx_init_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp)
                ql_dbg(ql_dbg_mbx, vha, 0x10d7,
                    "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x10d8, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10d8,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -3469,7 +3660,8 @@ qla81xx_idc_ack(scsi_qla_host_t *vha, uint16_t *mb)
        mbx_cmd_t mc;
        mbx_cmd_t *mcp = &mc;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x10d9, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10d9,
+           "Entered %s.\n", __func__);
 
        mcp->mb[0] = MBC_IDC_ACK;
        memcpy(&mcp->mb[1], mb, QLA_IDC_ACK_REGS * sizeof(uint16_t));
@@ -3483,7 +3675,8 @@ qla81xx_idc_ack(scsi_qla_host_t *vha, uint16_t *mb)
                ql_dbg(ql_dbg_mbx, vha, 0x10da,
                    "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x10db, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10db,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -3496,7 +3689,8 @@ qla81xx_fac_get_sector_size(scsi_qla_host_t *vha, uint32_t *sector_size)
        mbx_cmd_t mc;
        mbx_cmd_t *mcp = &mc;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x10dc, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10dc,
+           "Entered %s.\n", __func__);
 
        if (!IS_QLA81XX(vha->hw) && !IS_QLA83XX(vha->hw))
                return QLA_FUNCTION_FAILED;
@@ -3514,7 +3708,8 @@ qla81xx_fac_get_sector_size(scsi_qla_host_t *vha, uint32_t *sector_size)
                    "Failed=%x mb[0]=%x mb[1]=%x.\n",
                    rval, mcp->mb[0], mcp->mb[1]);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x10de, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10de,
+                   "Done %s.\n", __func__);
                *sector_size = mcp->mb[1];
        }
 
@@ -3531,7 +3726,8 @@ qla81xx_fac_do_write_enable(scsi_qla_host_t *vha, int enable)
        if (!IS_QLA81XX(vha->hw) && !IS_QLA83XX(vha->hw))
                return QLA_FUNCTION_FAILED;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x10df, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10df,
+           "Entered %s.\n", __func__);
 
        mcp->mb[0] = MBC_FLASH_ACCESS_CTRL;
        mcp->mb[1] = enable ? FAC_OPT_CMD_WRITE_ENABLE :
@@ -3547,7 +3743,8 @@ qla81xx_fac_do_write_enable(scsi_qla_host_t *vha, int enable)
                    "Failed=%x mb[0]=%x mb[1]=%x.\n",
                    rval, mcp->mb[0], mcp->mb[1]);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x10e1, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10e1,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -3563,7 +3760,8 @@ qla81xx_fac_erase_sector(scsi_qla_host_t *vha, uint32_t start, uint32_t finish)
        if (!IS_QLA81XX(vha->hw) && !IS_QLA83XX(vha->hw))
                return QLA_FUNCTION_FAILED;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x10e2, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10e2,
+           "Entered %s.\n", __func__);
 
        mcp->mb[0] = MBC_FLASH_ACCESS_CTRL;
        mcp->mb[1] = FAC_OPT_CMD_ERASE_SECTOR;
@@ -3582,7 +3780,8 @@ qla81xx_fac_erase_sector(scsi_qla_host_t *vha, uint32_t start, uint32_t finish)
                    "Failed=%x mb[0]=%x mb[1]=%x mb[2]=%x.\n",
                    rval, mcp->mb[0], mcp->mb[1], mcp->mb[2]);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x10e4, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10e4,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -3595,7 +3794,8 @@ qla81xx_restart_mpi_firmware(scsi_qla_host_t *vha)
        mbx_cmd_t mc;
        mbx_cmd_t *mcp = &mc;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x10e5, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10e5,
+           "Entered %s.\n", __func__);
 
        mcp->mb[0] = MBC_RESTART_MPI_FW;
        mcp->out_mb = MBX_0;
@@ -3609,7 +3809,8 @@ qla81xx_restart_mpi_firmware(scsi_qla_host_t *vha)
                    "Failed=%x mb[0]=%x mb[1]=%x.\n",
                    rval, mcp->mb[0], mcp->mb[1]);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x10e7, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10e7,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -3624,7 +3825,8 @@ qla2x00_read_sfp(scsi_qla_host_t *vha, dma_addr_t sfp_dma, uint8_t *sfp,
        mbx_cmd_t *mcp = &mc;
        struct qla_hw_data *ha = vha->hw;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x10e8, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10e8,
+           "Entered %s.\n", __func__);
 
        if (!IS_FWI2_CAPABLE(ha))
                return QLA_FUNCTION_FAILED;
@@ -3654,7 +3856,8 @@ qla2x00_read_sfp(scsi_qla_host_t *vha, dma_addr_t sfp_dma, uint8_t *sfp,
                ql_dbg(ql_dbg_mbx, vha, 0x10e9,
                    "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x10ea, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10ea,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -3669,7 +3872,8 @@ qla2x00_write_sfp(scsi_qla_host_t *vha, dma_addr_t sfp_dma, uint8_t *sfp,
        mbx_cmd_t *mcp = &mc;
        struct qla_hw_data *ha = vha->hw;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x10eb, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10eb,
+           "Entered %s.\n", __func__);
 
        if (!IS_FWI2_CAPABLE(ha))
                return QLA_FUNCTION_FAILED;
@@ -3699,7 +3903,8 @@ qla2x00_write_sfp(scsi_qla_host_t *vha, dma_addr_t sfp_dma, uint8_t *sfp,
                ql_dbg(ql_dbg_mbx, vha, 0x10ec,
                    "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x10ed, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10ed,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -3713,7 +3918,8 @@ qla2x00_get_xgmac_stats(scsi_qla_host_t *vha, dma_addr_t stats_dma,
        mbx_cmd_t mc;
        mbx_cmd_t *mcp = &mc;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x10ee, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10ee,
+           "Entered %s.\n", __func__);
 
        if (!IS_CNA_CAPABLE(vha->hw))
                return QLA_FUNCTION_FAILED;
@@ -3735,7 +3941,8 @@ qla2x00_get_xgmac_stats(scsi_qla_host_t *vha, dma_addr_t stats_dma,
                    "Failed=%x mb[0]=%x mb[1]=%x mb[2]=%x.\n",
                    rval, mcp->mb[0], mcp->mb[1], mcp->mb[2]);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x10f0, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10f0,
+                   "Done %s.\n", __func__);
 
 
                *actual_size = mcp->mb[2] << 2;
@@ -3752,7 +3959,8 @@ qla2x00_get_dcbx_params(scsi_qla_host_t *vha, dma_addr_t tlv_dma,
        mbx_cmd_t mc;
        mbx_cmd_t *mcp = &mc;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x10f1, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10f1,
+           "Entered %s.\n", __func__);
 
        if (!IS_CNA_CAPABLE(vha->hw))
                return QLA_FUNCTION_FAILED;
@@ -3775,7 +3983,8 @@ qla2x00_get_dcbx_params(scsi_qla_host_t *vha, dma_addr_t tlv_dma,
                    "Failed=%x mb[0]=%x mb[1]=%x mb[2]=%x.\n",
                    rval, mcp->mb[0], mcp->mb[1], mcp->mb[2]);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x10f3, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10f3,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -3788,7 +3997,8 @@ qla2x00_read_ram_word(scsi_qla_host_t *vha, uint32_t risc_addr, uint32_t *data)
        mbx_cmd_t mc;
        mbx_cmd_t *mcp = &mc;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x10f4, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10f4,
+           "Entered %s.\n", __func__);
 
        if (!IS_FWI2_CAPABLE(vha->hw))
                return QLA_FUNCTION_FAILED;
@@ -3805,7 +4015,8 @@ qla2x00_read_ram_word(scsi_qla_host_t *vha, uint32_t risc_addr, uint32_t *data)
                ql_dbg(ql_dbg_mbx, vha, 0x10f5,
                    "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x10f6, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10f6,
+                   "Done %s.\n", __func__);
                *data = mcp->mb[3] << 16 | mcp->mb[2];
        }
 
@@ -3821,7 +4032,8 @@ qla2x00_loopback_test(scsi_qla_host_t *vha, struct msg_echo_lb *mreq,
        mbx_cmd_t *mcp = &mc;
        uint32_t iter_cnt = 0x1;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x10f7, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10f7,
+           "Entered %s.\n", __func__);
 
        memset(mcp->mb, 0 , sizeof(mcp->mb));
        mcp->mb[0] = MBC_DIAGNOSTIC_LOOP_BACK;
@@ -3865,7 +4077,8 @@ qla2x00_loopback_test(scsi_qla_host_t *vha, struct msg_echo_lb *mreq,
                    "mb[19]=%x.\n", rval, mcp->mb[0], mcp->mb[1], mcp->mb[2],
                    mcp->mb[3], mcp->mb[18], mcp->mb[19]);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x10f9, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10f9,
+                   "Done %s.\n", __func__);
        }
 
        /* Copy mailbox information */
@@ -3882,7 +4095,8 @@ qla2x00_echo_test(scsi_qla_host_t *vha, struct msg_echo_lb *mreq,
        mbx_cmd_t *mcp = &mc;
        struct qla_hw_data *ha = vha->hw;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x10fa, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10fa,
+           "Entered %s.\n", __func__);
 
        memset(mcp->mb, 0 , sizeof(mcp->mb));
        mcp->mb[0] = MBC_DIAGNOSTIC_ECHO;
@@ -3926,7 +4140,8 @@ qla2x00_echo_test(scsi_qla_host_t *vha, struct msg_echo_lb *mreq,
                    "Failed=%x mb[0]=%x mb[1]=%x.\n",
                    rval, mcp->mb[0], mcp->mb[1]);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x10fc, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10fc,
+                   "Done %s.\n", __func__);
        }
 
        /* Copy mailbox information */
@@ -3941,7 +4156,7 @@ qla84xx_reset_chip(scsi_qla_host_t *vha, uint16_t enable_diagnostic)
        mbx_cmd_t mc;
        mbx_cmd_t *mcp = &mc;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x10fd,
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10fd,
            "Entered %s enable_diag=%d.\n", __func__, enable_diagnostic);
 
        mcp->mb[0] = MBC_ISP84XX_RESET;
@@ -3955,7 +4170,8 @@ qla84xx_reset_chip(scsi_qla_host_t *vha, uint16_t enable_diagnostic)
        if (rval != QLA_SUCCESS)
                ql_dbg(ql_dbg_mbx, vha, 0x10fe, "Failed=%x.\n", rval);
        else
-               ql_dbg(ql_dbg_mbx, vha, 0x10ff, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10ff,
+                   "Done %s.\n", __func__);
 
        return rval;
 }
@@ -3967,7 +4183,8 @@ qla2x00_write_ram_word(scsi_qla_host_t *vha, uint32_t risc_addr, uint32_t data)
        mbx_cmd_t mc;
        mbx_cmd_t *mcp = &mc;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x1100, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1100,
+           "Entered %s.\n", __func__);
 
        if (!IS_FWI2_CAPABLE(vha->hw))
                return QLA_FUNCTION_FAILED;
@@ -3986,7 +4203,8 @@ qla2x00_write_ram_word(scsi_qla_host_t *vha, uint32_t risc_addr, uint32_t data)
                ql_dbg(ql_dbg_mbx, vha, 0x1101,
                    "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x1102, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1102,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -4003,7 +4221,8 @@ qla81xx_write_mpi_register(scsi_qla_host_t *vha, uint16_t *mb)
 
        rval = QLA_SUCCESS;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x1103, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1103,
+           "Entered %s.\n", __func__);
 
        clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags);
 
@@ -4046,7 +4265,8 @@ qla81xx_write_mpi_register(scsi_qla_host_t *vha, uint16_t *mb)
                ql_dbg(ql_dbg_mbx, vha, 0x1104,
                    "Failed=%x mb[0]=%x.\n", rval, mb[0]);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x1105, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1105,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -4060,7 +4280,8 @@ qla2x00_get_data_rate(scsi_qla_host_t *vha)
        mbx_cmd_t *mcp = &mc;
        struct qla_hw_data *ha = vha->hw;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x1106, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1106,
+           "Entered %s.\n", __func__);
 
        if (!IS_FWI2_CAPABLE(ha))
                return QLA_FUNCTION_FAILED;
@@ -4078,7 +4299,8 @@ qla2x00_get_data_rate(scsi_qla_host_t *vha)
                ql_dbg(ql_dbg_mbx, vha, 0x1107,
                    "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x1108, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1108,
+                   "Done %s.\n", __func__);
                if (mcp->mb[1] != 0x7)
                        ha->link_data_rate = mcp->mb[1];
        }
@@ -4094,7 +4316,8 @@ qla81xx_get_port_config(scsi_qla_host_t *vha, uint16_t *mb)
        mbx_cmd_t *mcp = &mc;
        struct qla_hw_data *ha = vha->hw;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x1109, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1109,
+           "Entered %s.\n", __func__);
 
        if (!IS_QLA81XX(ha) && !IS_QLA83XX(ha))
                return QLA_FUNCTION_FAILED;
@@ -4113,7 +4336,8 @@ qla81xx_get_port_config(scsi_qla_host_t *vha, uint16_t *mb)
                /* Copy all bits to preserve original value */
                memcpy(mb, &mcp->mb[1], sizeof(uint16_t) * 4);
 
-               ql_dbg(ql_dbg_mbx, vha, 0x110b, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x110b,
+                   "Done %s.\n", __func__);
        }
        return rval;
 }
@@ -4125,7 +4349,8 @@ qla81xx_set_port_config(scsi_qla_host_t *vha, uint16_t *mb)
        mbx_cmd_t mc;
        mbx_cmd_t *mcp = &mc;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x110c, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x110c,
+           "Entered %s.\n", __func__);
 
        mcp->mb[0] = MBC_SET_PORT_CONFIG;
        /* Copy all bits to preserve original setting */
@@ -4140,7 +4365,8 @@ qla81xx_set_port_config(scsi_qla_host_t *vha, uint16_t *mb)
                ql_dbg(ql_dbg_mbx, vha, 0x110d,
                    "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
        } else
-               ql_dbg(ql_dbg_mbx, vha, 0x110e, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x110e,
+                   "Done %s.\n", __func__);
 
        return rval;
 }
@@ -4155,7 +4381,8 @@ qla24xx_set_fcp_prio(scsi_qla_host_t *vha, uint16_t loop_id, uint16_t priority,
        mbx_cmd_t *mcp = &mc;
        struct qla_hw_data *ha = vha->hw;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x110f, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x110f,
+           "Entered %s.\n", __func__);
 
        if (!IS_QLA24XX_TYPE(ha) && !IS_QLA25XX(ha))
                return QLA_FUNCTION_FAILED;
@@ -4183,7 +4410,8 @@ qla24xx_set_fcp_prio(scsi_qla_host_t *vha, uint16_t loop_id, uint16_t priority,
        if (rval != QLA_SUCCESS) {
                ql_dbg(ql_dbg_mbx, vha, 0x10cd, "Failed=%x.\n", rval);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x10cc, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10cc,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -4196,7 +4424,8 @@ qla2x00_get_thermal_temp(scsi_qla_host_t *vha, uint16_t *temp, uint16_t *frac)
        uint8_t byte;
        struct qla_hw_data *ha = vha->hw;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x10ca, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10ca,
+           "Entered %s.\n", __func__);
 
        /* Integer part */
        rval = qla2x00_read_sfp(vha, 0, &byte, 0x98, 0x01, 1, BIT_13|BIT_0);
@@ -4216,7 +4445,8 @@ qla2x00_get_thermal_temp(scsi_qla_host_t *vha, uint16_t *temp, uint16_t *frac)
        }
        *frac = (byte >> 6) * 25;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x1018, "Done %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1018,
+           "Done %s.\n", __func__);
 fail:
        return rval;
 }
@@ -4229,7 +4459,8 @@ qla82xx_mbx_intr_enable(scsi_qla_host_t *vha)
        mbx_cmd_t mc;
        mbx_cmd_t *mcp = &mc;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x1017, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1017,
+           "Entered %s.\n", __func__);
 
        if (!IS_FWI2_CAPABLE(ha))
                return QLA_FUNCTION_FAILED;
@@ -4248,7 +4479,8 @@ qla82xx_mbx_intr_enable(scsi_qla_host_t *vha)
                ql_dbg(ql_dbg_mbx, vha, 0x1016,
                    "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x100e, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x100e,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -4262,7 +4494,8 @@ qla82xx_mbx_intr_disable(scsi_qla_host_t *vha)
        mbx_cmd_t mc;
        mbx_cmd_t *mcp = &mc;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x100d, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x100d,
+           "Entered %s.\n", __func__);
 
        if (!IS_QLA82XX(ha))
                return QLA_FUNCTION_FAILED;
@@ -4281,7 +4514,8 @@ qla82xx_mbx_intr_disable(scsi_qla_host_t *vha)
                ql_dbg(ql_dbg_mbx, vha, 0x100c,
                    "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x100b, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x100b,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -4295,7 +4529,8 @@ qla82xx_md_get_template_size(scsi_qla_host_t *vha)
        mbx_cmd_t *mcp = &mc;
        int rval = QLA_FUNCTION_FAILED;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x111f, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x111f,
+           "Entered %s.\n", __func__);
 
        memset(mcp->mb, 0 , sizeof(mcp->mb));
        mcp->mb[0] = LSW(MBC_DIAGNOSTIC_MINIDUMP_TEMPLATE);
@@ -4318,7 +4553,8 @@ qla82xx_md_get_template_size(scsi_qla_host_t *vha)
                    (mcp->mb[1] << 16) | mcp->mb[0],
                    (mcp->mb[3] << 16) | mcp->mb[2]);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x1121, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1121,
+                   "Done %s.\n", __func__);
                ha->md_template_size = ((mcp->mb[3] << 16) | mcp->mb[2]);
                if (!ha->md_template_size) {
                        ql_dbg(ql_dbg_mbx, vha, 0x1122,
@@ -4337,7 +4573,8 @@ qla82xx_md_get_template(scsi_qla_host_t *vha)
        mbx_cmd_t *mcp = &mc;
        int rval = QLA_FUNCTION_FAILED;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x1123, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1123,
+           "Entered %s.\n", __func__);
 
        ha->md_tmplt_hdr = dma_alloc_coherent(&ha->pdev->dev,
           ha->md_template_size, &ha->md_tmplt_hdr_dma, GFP_KERNEL);
@@ -4372,7 +4609,8 @@ qla82xx_md_get_template(scsi_qla_host_t *vha)
                    ((mcp->mb[1] << 16) | mcp->mb[0]),
                    ((mcp->mb[3] << 16) | mcp->mb[2]));
        } else
-               ql_dbg(ql_dbg_mbx, vha, 0x1126, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1126,
+                   "Done %s.\n", __func__);
        return rval;
 }
 
@@ -4387,7 +4625,8 @@ qla81xx_set_led_config(scsi_qla_host_t *vha, uint16_t *led_cfg)
        if (!IS_QLA81XX(ha) && !IS_QLA8031(ha))
                return QLA_FUNCTION_FAILED;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x1133, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1133,
+           "Entered %s.\n", __func__);
 
        memset(mcp, 0, sizeof(mbx_cmd_t));
        mcp->mb[0] = MBC_SET_LED_CONFIG;
@@ -4412,7 +4651,8 @@ qla81xx_set_led_config(scsi_qla_host_t *vha, uint16_t *led_cfg)
                ql_dbg(ql_dbg_mbx, vha, 0x1134,
                    "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x1135, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1135,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -4429,7 +4669,8 @@ qla81xx_get_led_config(scsi_qla_host_t *vha, uint16_t *led_cfg)
        if (!IS_QLA81XX(ha) && !IS_QLA8031(ha))
                return QLA_FUNCTION_FAILED;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x1136, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1136,
+           "Entered %s.\n", __func__);
 
        memset(mcp, 0, sizeof(mbx_cmd_t));
        mcp->mb[0] = MBC_GET_LED_CONFIG;
@@ -4454,7 +4695,8 @@ qla81xx_get_led_config(scsi_qla_host_t *vha, uint16_t *led_cfg)
                        led_cfg[4] = mcp->mb[5];
                        led_cfg[5] = mcp->mb[6];
                }
-               ql_dbg(ql_dbg_mbx, vha, 0x1138, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1138,
+                   "Done %s.\n", __func__);
        }
 
        return rval;
@@ -4471,7 +4713,7 @@ qla82xx_mbx_beacon_ctl(scsi_qla_host_t *vha, int enable)
        if (!IS_QLA82XX(ha))
                return QLA_FUNCTION_FAILED;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x1127,
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1127,
                "Entered %s.\n", __func__);
 
        memset(mcp, 0, sizeof(mbx_cmd_t));
@@ -4491,7 +4733,7 @@ qla82xx_mbx_beacon_ctl(scsi_qla_host_t *vha, int enable)
                ql_dbg(ql_dbg_mbx, vha, 0x1128,
                    "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x1129,
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1129,
                    "Done %s.\n", __func__);
        }
 
@@ -4509,7 +4751,8 @@ qla83xx_write_remote_reg(scsi_qla_host_t *vha, uint32_t reg, uint32_t data)
        if (!IS_QLA83XX(ha))
                return QLA_FUNCTION_FAILED;
 
-       ql_dbg(ql_dbg_mbx, vha, 0x1130, "Entered %s.\n", __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1130,
+           "Entered %s.\n", __func__);
 
        mcp->mb[0] = MBC_WRITE_REMOTE_REG;
        mcp->mb[1] = LSW(reg);
@@ -4527,7 +4770,7 @@ qla83xx_write_remote_reg(scsi_qla_host_t *vha, uint32_t reg, uint32_t data)
                ql_dbg(ql_dbg_mbx, vha, 0x1131,
                    "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
        } else {
-               ql_dbg(ql_dbg_mbx, vha, 0x1132,
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1132,
                    "Done %s.\n", __func__);
        }
 
@@ -4543,13 +4786,14 @@ qla2x00_port_logout(scsi_qla_host_t *vha, struct fc_port *fcport)
        mbx_cmd_t *mcp = &mc;
 
        if (IS_QLA2100(ha) || IS_QLA2200(ha)) {
-               ql_dbg(ql_dbg_mbx, vha, 0x113b,
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x113b,
                    "Implicit LOGO Unsupported.\n");
                return QLA_FUNCTION_FAILED;
        }
 
 
-       ql_dbg(ql_dbg_mbx, vha, 0x113c, "Done %s.\n",  __func__);
+       ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x113c,
+           "Entering %s.\n",  __func__);
 
        /* Perform Implicit LOGO. */
        mcp->mb[0] = MBC_PORT_LOGOUT;
@@ -4564,7 +4808,8 @@ qla2x00_port_logout(scsi_qla_host_t *vha, struct fc_port *fcport)
                ql_dbg(ql_dbg_mbx, vha, 0x113d,
                    "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
        else
-               ql_dbg(ql_dbg_mbx, vha, 0x113e, "Done %s.\n", __func__);
+               ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x113e,
+                   "Done %s.\n", __func__);
 
        return rval;
 }
index aa062a1b0ca496f4a1bc4f10e85f795d0d5d17b3..3e8b32419e68959440c8f85716867e6dfc897435 100644 (file)
@@ -6,6 +6,7 @@
  */
 #include "qla_def.h"
 #include "qla_gbl.h"
+#include "qla_target.h"
 
 #include <linux/moduleparam.h>
 #include <linux/vmalloc.h>
@@ -49,6 +50,9 @@ qla24xx_allocate_vp_id(scsi_qla_host_t *vha)
 
        spin_lock_irqsave(&ha->vport_slock, flags);
        list_add_tail(&vha->list, &ha->vp_list);
+
+       qlt_update_vp_map(vha, SET_VP_IDX);
+
        spin_unlock_irqrestore(&ha->vport_slock, flags);
 
        mutex_unlock(&ha->vport_lock);
@@ -79,6 +83,7 @@ qla24xx_deallocate_vp_id(scsi_qla_host_t *vha)
                spin_lock_irqsave(&ha->vport_slock, flags);
        }
        list_del(&vha->list);
+       qlt_update_vp_map(vha, RESET_VP_IDX);
        spin_unlock_irqrestore(&ha->vport_slock, flags);
 
        vp_id = vha->vp_idx;
@@ -134,7 +139,7 @@ qla2x00_mark_vp_devices_dead(scsi_qla_host_t *vha)
        list_for_each_entry(fcport, &vha->vp_fcports, list) {
                ql_dbg(ql_dbg_vport, vha, 0xa001,
                    "Marking port dead, loop_id=0x%04x : %x.\n",
-                   fcport->loop_id, fcport->vp_idx);
+                   fcport->loop_id, fcport->vha->vp_idx);
 
                qla2x00_mark_device_lost(vha, fcport, 0, 0);
                qla2x00_set_fcport_state(fcport, FCS_UNCONFIGURED);
@@ -150,6 +155,9 @@ qla24xx_disable_vp(scsi_qla_host_t *vha)
        atomic_set(&vha->loop_state, LOOP_DOWN);
        atomic_set(&vha->loop_down_timer, LOOP_DOWN_TIME);
 
+       /* Remove port id from vp target map */
+       qlt_update_vp_map(vha, RESET_AL_PA);
+
        qla2x00_mark_vp_devices_dead(vha);
        atomic_set(&vha->vp_state, VP_FAILED);
        vha->flags.management_server_logged_in = 0;
@@ -295,10 +303,8 @@ qla2x00_vp_abort_isp(scsi_qla_host_t *vha)
 static int
 qla2x00_do_dpc_vp(scsi_qla_host_t *vha)
 {
-       ql_dbg(ql_dbg_dpc, vha, 0x4012,
-           "Entering %s.\n", __func__);
-       ql_dbg(ql_dbg_dpc, vha, 0x4013,
-           "vp_flags: 0x%lx.\n", vha->vp_flags);
+       ql_dbg(ql_dbg_dpc + ql_dbg_verbose, vha, 0x4012,
+           "Entering %s vp_flags: 0x%lx.\n", __func__, vha->vp_flags);
 
        qla2x00_do_work(vha);
 
@@ -348,7 +354,7 @@ qla2x00_do_dpc_vp(scsi_qla_host_t *vha)
                }
        }
 
-       ql_dbg(ql_dbg_dpc, vha, 0x401c,
+       ql_dbg(ql_dbg_dpc + ql_dbg_verbose, vha, 0x401c,
            "Exiting %s.\n", __func__);
        return 0;
 }
index de722a933438dea5de3b60ce03b212b1170d734d..caf627ba7fa8b3e11a493355bf7a08f120770bcd 100644 (file)
@@ -1190,12 +1190,12 @@ qla82xx_pinit_from_rom(scsi_qla_host_t *vha)
        }
 
        /* Offset in flash = lower 16 bits
-        * Number of enteries = upper 16 bits
+        * Number of entries = upper 16 bits
         */
        offset = n & 0xffffU;
        n = (n >> 16) & 0xffffU;
 
-       /* number of addr/value pair should not exceed 1024 enteries */
+       /* number of addr/value pair should not exceed 1024 entries */
        if (n  >= 1024) {
                ql_log(ql_log_fatal, vha, 0x0071,
                    "Card flash not initialized:n=0x%x.\n", n);
@@ -2050,7 +2050,7 @@ qla82xx_intr_handler(int irq, void *dev_id)
 
        rsp = (struct rsp_que *) dev_id;
        if (!rsp) {
-               ql_log(ql_log_info, NULL, 0xb054,
+               ql_log(ql_log_info, NULL, 0xb053,
                    "%s: NULL response queue pointer.\n", __func__);
                return IRQ_NONE;
        }
@@ -2446,7 +2446,7 @@ qla82xx_load_fw(scsi_qla_host_t *vha)
 
        if (qla82xx_fw_load_from_flash(ha) == QLA_SUCCESS) {
                ql_log(ql_log_info, vha, 0x00a1,
-                   "Firmware loaded successully from flash.\n");
+                   "Firmware loaded successfully from flash.\n");
                return QLA_SUCCESS;
        } else {
                ql_log(ql_log_warn, vha, 0x0108,
@@ -2461,7 +2461,7 @@ try_blob_fw:
        blob = ha->hablob = qla2x00_request_firmware(vha);
        if (!blob) {
                ql_log(ql_log_fatal, vha, 0x00a3,
-                   "Firmware image not preset.\n");
+                   "Firmware image not present.\n");
                goto fw_load_failed;
        }
 
@@ -2689,7 +2689,7 @@ qla82xx_write_flash_data(struct scsi_qla_host *vha, uint32_t *dwptr,
                if (!optrom) {
                        ql_log(ql_log_warn, vha, 0xb01b,
                            "Unable to allocate memory "
-                           "for optron burst write (%x KB).\n",
+                           "for optrom burst write (%x KB).\n",
                            OPTROM_BURST_SIZE / 1024);
                }
        }
@@ -2960,9 +2960,8 @@ qla82xx_need_qsnt_handler(scsi_qla_host_t *vha)
                         * changing the state to DEV_READY
                         */
                        ql_log(ql_log_info, vha, 0xb023,
-                           "%s : QUIESCENT TIMEOUT.\n", QLA2XXX_DRIVER_NAME);
-                       ql_log(ql_log_info, vha, 0xb024,
-                           "DRV_ACTIVE:%d DRV_STATE:%d.\n",
+                           "%s : QUIESCENT TIMEOUT DRV_ACTIVE:%d "
+                           "DRV_STATE:%d.\n", QLA2XXX_DRIVER_NAME,
                            drv_active, drv_state);
                        qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE,
                            QLA82XX_DEV_READY);
@@ -3129,7 +3128,7 @@ qla82xx_need_reset_handler(scsi_qla_host_t *vha)
                if (ql2xmdenable) {
                        if (qla82xx_md_collect(vha))
                                ql_log(ql_log_warn, vha, 0xb02c,
-                                   "Not able to collect minidump.\n");
+                                   "Minidump not collected.\n");
                } else
                        ql_log(ql_log_warn, vha, 0xb04f,
                            "Minidump disabled.\n");
@@ -3160,11 +3159,11 @@ qla82xx_check_md_needed(scsi_qla_host_t *vha)
                                    "Firmware version differs "
                                    "Previous version: %d:%d:%d - "
                                    "New version: %d:%d:%d\n",
+                                   fw_major_version, fw_minor_version,
+                                   fw_subminor_version,
                                    ha->fw_major_version,
                                    ha->fw_minor_version,
-                                   ha->fw_subminor_version,
-                                   fw_major_version, fw_minor_version,
-                                   fw_subminor_version);
+                                   ha->fw_subminor_version);
                                /* Release MiniDump resources */
                                qla82xx_md_free(vha);
                                /* ALlocate MiniDump resources */
@@ -3325,6 +3324,30 @@ exit:
        return rval;
 }
 
+static int qla82xx_check_temp(scsi_qla_host_t *vha)
+{
+       uint32_t temp, temp_state, temp_val;
+       struct qla_hw_data *ha = vha->hw;
+
+       temp = qla82xx_rd_32(ha, CRB_TEMP_STATE);
+       temp_state = qla82xx_get_temp_state(temp);
+       temp_val = qla82xx_get_temp_val(temp);
+
+       if (temp_state == QLA82XX_TEMP_PANIC) {
+               ql_log(ql_log_warn, vha, 0x600e,
+                   "Device temperature %d degrees C exceeds "
+                   " maximum allowed. Hardware has been shut down.\n",
+                   temp_val);
+               return 1;
+       } else if (temp_state == QLA82XX_TEMP_WARN) {
+               ql_log(ql_log_warn, vha, 0x600f,
+                   "Device temperature %d degrees C exceeds "
+                   "operating range. Immediate action needed.\n",
+                   temp_val);
+       }
+       return 0;
+}
+
 void qla82xx_clear_pending_mbx(scsi_qla_host_t *vha)
 {
        struct qla_hw_data *ha = vha->hw;
@@ -3347,18 +3370,20 @@ void qla82xx_watchdog(scsi_qla_host_t *vha)
        /* don't poll if reset is going on */
        if (!ha->flags.isp82xx_reset_hdlr_active) {
                dev_state = qla82xx_rd_32(ha, QLA82XX_CRB_DEV_STATE);
-               if (dev_state == QLA82XX_DEV_NEED_RESET &&
+               if (qla82xx_check_temp(vha)) {
+                       set_bit(ISP_UNRECOVERABLE, &vha->dpc_flags);
+                       ha->flags.isp82xx_fw_hung = 1;
+                       qla82xx_clear_pending_mbx(vha);
+               } else if (dev_state == QLA82XX_DEV_NEED_RESET &&
                    !test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags)) {
                        ql_log(ql_log_warn, vha, 0x6001,
                            "Adapter reset needed.\n");
                        set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
-                       qla2xxx_wake_dpc(vha);
                } else if (dev_state == QLA82XX_DEV_NEED_QUIESCENT &&
                        !test_bit(ISP_QUIESCE_NEEDED, &vha->dpc_flags)) {
                        ql_log(ql_log_warn, vha, 0x6002,
                            "Quiescent needed.\n");
                        set_bit(ISP_QUIESCE_NEEDED, &vha->dpc_flags);
-                       qla2xxx_wake_dpc(vha);
                } else {
                        if (qla82xx_check_fw_alive(vha)) {
                                ql_dbg(ql_dbg_timer, vha, 0x6011,
@@ -3398,7 +3423,6 @@ void qla82xx_watchdog(scsi_qla_host_t *vha)
                                        set_bit(ISP_ABORT_NEEDED,
                                            &vha->dpc_flags);
                                }
-                               qla2xxx_wake_dpc(vha);
                                ha->flags.isp82xx_fw_hung = 1;
                                ql_log(ql_log_warn, vha, 0x6007, "Firmware hung.\n");
                                qla82xx_clear_pending_mbx(vha);
@@ -4113,6 +4137,14 @@ qla82xx_md_collect(scsi_qla_host_t *vha)
                goto md_failed;
        }
 
+       if (ha->flags.isp82xx_no_md_cap) {
+               ql_log(ql_log_warn, vha, 0xb054,
+                   "Forced reset from application, "
+                   "ignore minidump capture\n");
+               ha->flags.isp82xx_no_md_cap = 0;
+               goto md_failed;
+       }
+
        if (qla82xx_validate_template_chksum(vha)) {
                ql_log(ql_log_info, vha, 0xb039,
                    "Template checksum validation error\n");
index 4ac50e274661af0744ca1ffcbfecdcedf5771b53..6eb210e3cc637242aed65efde17902a0f94c2d60 100644 (file)
@@ -26,6 +26,7 @@
 #define CRB_RCVPEG_STATE               QLA82XX_REG(0x13c)
 #define BOOT_LOADER_DIMM_STATUS                QLA82XX_REG(0x54)
 #define CRB_DMA_SHIFT                  QLA82XX_REG(0xcc)
+#define CRB_TEMP_STATE                 QLA82XX_REG(0x1b4)
 #define QLA82XX_DMA_SHIFT_VALUE                0x55555555
 
 #define QLA82XX_HW_H0_CH_HUB_ADR    0x05
 #define QLA82XX_FW_VERSION_SUB         (QLA82XX_CAM_RAM(0x158))
 #define QLA82XX_PCIE_REG(reg)          (QLA82XX_CRB_PCIE + (reg))
 
-#define PCIE_CHICKEN3                  (0x120c8)
 #define PCIE_SETUP_FUNCTION            (0x12040)
 #define PCIE_SETUP_FUNCTION2           (0x12048)
 
@@ -1178,4 +1178,16 @@ static const int MD_MIU_TEST_AGT_RDDATA[] = { 0x410000A8, 0x410000AC,
 #define CRB_NIU_XG_PAUSE_CTL_P0        0x1
 #define CRB_NIU_XG_PAUSE_CTL_P1        0x8
 
+#define qla82xx_get_temp_val(x)          ((x) >> 16)
+#define qla82xx_get_temp_state(x)        ((x) & 0xffff)
+#define qla82xx_encode_temp(val, state)  (((val) << 16) | (state))
+
+/*
+ * Temperature control.
+ */
+enum {
+       QLA82XX_TEMP_NORMAL = 0x1, /* Normal operating range */
+       QLA82XX_TEMP_WARN,         /* Sound alert, temperature getting high */
+       QLA82XX_TEMP_PANIC         /* Fatal error, hardware has shut down. */
+};
 #endif
index c9c56a8427f3e1d36c2a1c14764a4d4860a782d9..6d1d873a20e2f8997ead5ecadd2e30a183d92c6c 100644 (file)
 #include <linux/mutex.h>
 #include <linux/kobject.h>
 #include <linux/slab.h>
-
 #include <scsi/scsi_tcq.h>
 #include <scsi/scsicam.h>
 #include <scsi/scsi_transport.h>
 #include <scsi/scsi_transport_fc.h>
 
+#include "qla_target.h"
+
 /*
  * Driver version
  */
@@ -40,6 +41,12 @@ static struct kmem_cache *ctx_cachep;
  */
 int ql_errlev = ql_log_all;
 
+int ql2xenableclass2;
+module_param(ql2xenableclass2, int, S_IRUGO|S_IRUSR);
+MODULE_PARM_DESC(ql2xenableclass2,
+               "Specify if Class 2 operations are supported from the very "
+               "beginning. Default is 0 - class 2 not supported.");
+
 int ql2xlogintimeout = 20;
 module_param(ql2xlogintimeout, int, S_IRUGO);
 MODULE_PARM_DESC(ql2xlogintimeout,
@@ -255,6 +262,8 @@ struct scsi_host_template qla2xxx_driver_template = {
 
        .max_sectors            = 0xFFFF,
        .shost_attrs            = qla2x00_host_attrs,
+
+       .supported_mode         = MODE_INITIATOR,
 };
 
 static struct scsi_transport_template *qla2xxx_transport_template = NULL;
@@ -306,7 +315,8 @@ static void qla2x00_free_fw_dump(struct qla_hw_data *);
 static void qla2x00_mem_free(struct qla_hw_data *);
 
 /* -------------------------------------------------------------------------- */
-static int qla2x00_alloc_queues(struct qla_hw_data *ha)
+static int qla2x00_alloc_queues(struct qla_hw_data *ha, struct req_que *req,
+                               struct rsp_que *rsp)
 {
        scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
        ha->req_q_map = kzalloc(sizeof(struct req_que *) * ha->max_req_queues,
@@ -324,6 +334,12 @@ static int qla2x00_alloc_queues(struct qla_hw_data *ha)
                    "Unable to allocate memory for response queue ptrs.\n");
                goto fail_rsp_map;
        }
+       /*
+        * Make sure we record at least the request and response queue zero in
+        * case we need to free them if part of the probe fails.
+        */
+       ha->rsp_q_map[0] = rsp;
+       ha->req_q_map[0] = req;
        set_bit(0, ha->rsp_qid_map);
        set_bit(0, ha->req_qid_map);
        return 1;
@@ -642,12 +658,12 @@ qla2xxx_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd)
 
        if (ha->flags.eeh_busy) {
                if (ha->flags.pci_channel_io_perm_failure) {
-                       ql_dbg(ql_dbg_io, vha, 0x3001,
+                       ql_dbg(ql_dbg_aer, vha, 0x9010,
                            "PCI Channel IO permanent failure, exiting "
                            "cmd=%p.\n", cmd);
                        cmd->result = DID_NO_CONNECT << 16;
                } else {
-                       ql_dbg(ql_dbg_io, vha, 0x3002,
+                       ql_dbg(ql_dbg_aer, vha, 0x9011,
                            "EEH_Busy, Requeuing the cmd=%p.\n", cmd);
                        cmd->result = DID_REQUEUE << 16;
                }
@@ -657,7 +673,7 @@ qla2xxx_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd)
        rval = fc_remote_port_chkready(rport);
        if (rval) {
                cmd->result = rval;
-               ql_dbg(ql_dbg_io, vha, 0x3003,
+               ql_dbg(ql_dbg_io + ql_dbg_verbose, vha, 0x3003,
                    "fc_remote_port_chkready failed for cmd=%p, rval=0x%x.\n",
                    cmd, rval);
                goto qc24_fail_command;
@@ -1136,7 +1152,7 @@ qla2xxx_eh_bus_reset(struct scsi_cmnd *cmd)
        ret = FAILED;
 
        ql_log(ql_log_info, vha, 0x8012,
-           "BUS RESET ISSUED nexus=%ld:%d%d.\n", vha->host_no, id, lun);
+           "BUS RESET ISSUED nexus=%ld:%d:%d.\n", vha->host_no, id, lun);
 
        if (qla2x00_wait_for_hba_online(vha) != QLA_SUCCESS) {
                ql_log(ql_log_fatal, vha, 0x8013,
@@ -2180,6 +2196,7 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
        ql_dbg_pci(ql_dbg_init, pdev, 0x000a,
            "Memory allocated for ha=%p.\n", ha);
        ha->pdev = pdev;
+       ha->tgt.enable_class_2 = ql2xenableclass2;
 
        /* Clear our data area */
        ha->bars = bars;
@@ -2243,6 +2260,7 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
                ha->mbx_count = MAILBOX_REGISTER_COUNT;
                req_length = REQUEST_ENTRY_CNT_24XX;
                rsp_length = RESPONSE_ENTRY_CNT_2300;
+               ha->tgt.atio_q_length = ATIO_ENTRY_CNT_24XX;
                ha->max_loop_id = SNS_LAST_LOOP_ID_2300;
                ha->init_cb_size = sizeof(struct mid_init_cb_24xx);
                ha->gid_list_info_size = 8;
@@ -2258,6 +2276,7 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
                ha->mbx_count = MAILBOX_REGISTER_COUNT;
                req_length = REQUEST_ENTRY_CNT_24XX;
                rsp_length = RESPONSE_ENTRY_CNT_2300;
+               ha->tgt.atio_q_length = ATIO_ENTRY_CNT_24XX;
                ha->max_loop_id = SNS_LAST_LOOP_ID_2300;
                ha->init_cb_size = sizeof(struct mid_init_cb_24xx);
                ha->gid_list_info_size = 8;
@@ -2417,6 +2436,17 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
            host->max_cmd_len, host->max_channel, host->max_lun,
            host->transportt, sht->vendor_id);
 
+que_init:
+       /* Alloc arrays of request and response ring ptrs */
+       if (!qla2x00_alloc_queues(ha, req, rsp)) {
+               ql_log(ql_log_fatal, base_vha, 0x003d,
+                   "Failed to allocate memory for queue pointers..."
+                   "aborting.\n");
+               goto probe_init_failed;
+       }
+
+       qlt_probe_one_stage1(base_vha, ha);
+
        /* Set up the irqs */
        ret = qla2x00_request_irqs(ha, rsp);
        if (ret)
@@ -2424,20 +2454,10 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
 
        pci_save_state(pdev);
 
-       /* Alloc arrays of request and response ring ptrs */
-que_init:
-       if (!qla2x00_alloc_queues(ha)) {
-               ql_log(ql_log_fatal, base_vha, 0x003d,
-                   "Failed to allocate memory for queue pointers.. aborting.\n");
-               goto probe_init_failed;
-       }
-
-       ha->rsp_q_map[0] = rsp;
-       ha->req_q_map[0] = req;
+       /* Assign back pointers */
        rsp->req = req;
        req->rsp = rsp;
-       set_bit(0, ha->req_qid_map);
-       set_bit(0, ha->rsp_qid_map);
+
        /* FWI2-capable only. */
        req->req_q_in = &ha->iobase->isp24.req_q_in;
        req->req_q_out = &ha->iobase->isp24.req_q_out;
@@ -2514,6 +2534,14 @@ que_init:
        ql_dbg(ql_dbg_init, base_vha, 0x00ee,
            "DPC thread started successfully.\n");
 
+       /*
+        * If we're not coming up in initiator mode, we might sit for
+        * a while without waking up the dpc thread, which leads to a
+        * stuck process warning.  So just kick the dpc once here and
+        * let the kthread start (and go back to sleep in qla2x00_do_dpc).
+        */
+       qla2xxx_wake_dpc(base_vha);
+
 skip_dpc:
        list_add_tail(&base_vha->list, &ha->vp_list);
        base_vha->host->irq = ha->pdev->irq;
@@ -2559,7 +2587,11 @@ skip_dpc:
        ql_dbg(ql_dbg_init, base_vha, 0x00f2,
            "Init done and hba is online.\n");
 
-       scsi_scan_host(host);
+       if (qla_ini_mode_enabled(base_vha))
+               scsi_scan_host(host);
+       else
+               ql_dbg(ql_dbg_init, base_vha, 0x0122,
+                       "skipping scsi_scan_host() for non-initiator port\n");
 
        qla2x00_alloc_sysfs_attr(base_vha);
 
@@ -2577,11 +2609,17 @@ skip_dpc:
            base_vha->host_no,
            ha->isp_ops->fw_version_str(base_vha, fw_str));
 
+       qlt_add_target(ha, base_vha);
+
        return 0;
 
 probe_init_failed:
        qla2x00_free_req_que(ha, req);
+       ha->req_q_map[0] = NULL;
+       clear_bit(0, ha->req_qid_map);
        qla2x00_free_rsp_que(ha, rsp);
+       ha->rsp_q_map[0] = NULL;
+       clear_bit(0, ha->rsp_qid_map);
        ha->max_req_queues = ha->max_rsp_queues = 0;
 
 probe_failed:
@@ -2620,6 +2658,22 @@ probe_out:
        return ret;
 }
 
+static void
+qla2x00_stop_dpc_thread(scsi_qla_host_t *vha)
+{
+       struct qla_hw_data *ha = vha->hw;
+       struct task_struct *t = ha->dpc_thread;
+
+       if (ha->dpc_thread == NULL)
+               return;
+       /*
+        * qla2xxx_wake_dpc checks for ->dpc_thread
+        * so we need to zero it out.
+        */
+       ha->dpc_thread = NULL;
+       kthread_stop(t);
+}
+
 static void
 qla2x00_shutdown(struct pci_dev *pdev)
 {
@@ -2663,9 +2717,18 @@ qla2x00_remove_one(struct pci_dev *pdev)
        struct qla_hw_data  *ha;
        unsigned long flags;
 
+       /*
+        * If the PCI device is disabled that means that probe failed and any
+        * resources should be have cleaned up on probe exit.
+        */
+       if (!atomic_read(&pdev->enable_cnt))
+               return;
+
        base_vha = pci_get_drvdata(pdev);
        ha = base_vha->hw;
 
+       ha->flags.host_shutting_down = 1;
+
        mutex_lock(&ha->vport_lock);
        while (ha->cur_vport_count) {
                struct Scsi_Host *scsi_host;
@@ -2719,6 +2782,7 @@ qla2x00_remove_one(struct pci_dev *pdev)
                ha->dpc_thread = NULL;
                kthread_stop(t);
        }
+       qlt_remove_target(ha, base_vha);
 
        qla2x00_free_sysfs_attr(base_vha);
 
@@ -2770,17 +2834,7 @@ qla2x00_free_device(scsi_qla_host_t *vha)
        if (vha->timer_active)
                qla2x00_stop_timer(vha);
 
-       /* Kill the kernel thread for this host */
-       if (ha->dpc_thread) {
-               struct task_struct *t = ha->dpc_thread;
-
-               /*
-                * qla2xxx_wake_dpc checks for ->dpc_thread
-                * so we need to zero it out.
-                */
-               ha->dpc_thread = NULL;
-               kthread_stop(t);
-       }
+       qla2x00_stop_dpc_thread(vha);
 
        qla25xx_delete_queues(vha);
 
@@ -2842,8 +2896,10 @@ qla2x00_schedule_rport_del(struct scsi_qla_host *vha, fc_port_t *fcport,
                spin_unlock_irqrestore(vha->host->host_lock, flags);
                set_bit(FCPORT_UPDATE_NEEDED, &base_vha->dpc_flags);
                qla2xxx_wake_dpc(base_vha);
-       } else
+       } else {
                fc_remote_port_delete(rport);
+               qlt_fc_port_deleted(vha, fcport);
+       }
 }
 
 /*
@@ -2859,7 +2915,7 @@ void qla2x00_mark_device_lost(scsi_qla_host_t *vha, fc_port_t *fcport,
     int do_login, int defer)
 {
        if (atomic_read(&fcport->state) == FCS_ONLINE &&
-           vha->vp_idx == fcport->vp_idx) {
+           vha->vp_idx == fcport->vha->vp_idx) {
                qla2x00_set_fcport_state(fcport, FCS_DEVICE_LOST);
                qla2x00_schedule_rport_del(vha, fcport, defer);
        }
@@ -2908,7 +2964,7 @@ qla2x00_mark_all_devices_lost(scsi_qla_host_t *vha, int defer)
        fc_port_t *fcport;
 
        list_for_each_entry(fcport, &vha->vp_fcports, list) {
-               if (vha->vp_idx != 0 && vha->vp_idx != fcport->vp_idx)
+               if (vha->vp_idx != 0 && vha->vp_idx != fcport->vha->vp_idx)
                        continue;
 
                /*
@@ -2921,7 +2977,7 @@ qla2x00_mark_all_devices_lost(scsi_qla_host_t *vha, int defer)
                        qla2x00_set_fcport_state(fcport, FCS_DEVICE_LOST);
                        if (defer)
                                qla2x00_schedule_rport_del(vha, fcport, defer);
-                       else if (vha->vp_idx == fcport->vp_idx)
+                       else if (vha->vp_idx == fcport->vha->vp_idx)
                                qla2x00_schedule_rport_del(vha, fcport, defer);
                }
        }
@@ -2946,10 +3002,13 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len,
        if (!ha->init_cb)
                goto fail;
 
+       if (qlt_mem_alloc(ha) < 0)
+               goto fail_free_init_cb;
+
        ha->gid_list = dma_alloc_coherent(&ha->pdev->dev,
                qla2x00_gid_list_size(ha), &ha->gid_list_dma, GFP_KERNEL);
        if (!ha->gid_list)
-               goto fail_free_init_cb;
+               goto fail_free_tgt_mem;
 
        ha->srb_mempool = mempool_create_slab_pool(SRB_MIN_REQ, srb_cachep);
        if (!ha->srb_mempool)
@@ -3167,6 +3226,8 @@ fail_free_gid_list:
        ha->gid_list_dma);
        ha->gid_list = NULL;
        ha->gid_list_dma = 0;
+fail_free_tgt_mem:
+       qlt_mem_free(ha);
 fail_free_init_cb:
        dma_free_coherent(&ha->pdev->dev, ha->init_cb_size, ha->init_cb,
        ha->init_cb_dma);
@@ -3282,6 +3343,8 @@ qla2x00_mem_free(struct qla_hw_data *ha)
        if (ha->ctx_mempool)
                mempool_destroy(ha->ctx_mempool);
 
+       qlt_mem_free(ha);
+
        if (ha->init_cb)
                dma_free_coherent(&ha->pdev->dev, ha->init_cb_size,
                        ha->init_cb, ha->init_cb_dma);
@@ -3311,6 +3374,10 @@ qla2x00_mem_free(struct qla_hw_data *ha)
 
        ha->gid_list = NULL;
        ha->gid_list_dma = 0;
+
+       ha->tgt.atio_ring = NULL;
+       ha->tgt.atio_dma = 0;
+       ha->tgt.tgt_vp_map = NULL;
 }
 
 struct scsi_qla_host *qla2x00_create_host(struct scsi_host_template *sht,
@@ -3671,10 +3738,9 @@ qla2x00_do_dpc(void *data)
 
                ha->dpc_active = 1;
 
-               ql_dbg(ql_dbg_dpc, base_vha, 0x4001,
-                   "DPC handler waking up.\n");
-               ql_dbg(ql_dbg_dpc, base_vha, 0x4002,
-                   "dpc_flags=0x%lx.\n", base_vha->dpc_flags);
+               ql_dbg(ql_dbg_dpc + ql_dbg_verbose, base_vha, 0x4001,
+                   "DPC handler waking up, dpc_flags=0x%lx.\n",
+                   base_vha->dpc_flags);
 
                qla2x00_do_work(base_vha);
 
@@ -3740,6 +3806,16 @@ qla2x00_do_dpc(void *data)
                        clear_bit(FCPORT_UPDATE_NEEDED, &base_vha->dpc_flags);
                }
 
+               if (test_bit(SCR_PENDING, &base_vha->dpc_flags)) {
+                       int ret;
+                       ret = qla2x00_send_change_request(base_vha, 0x3, 0);
+                       if (ret != QLA_SUCCESS)
+                               ql_log(ql_log_warn, base_vha, 0x121,
+                                   "Failed to enable receiving of RSCN "
+                                   "requests: 0x%x.\n", ret);
+                       clear_bit(SCR_PENDING, &base_vha->dpc_flags);
+               }
+
                if (test_bit(ISP_QUIESCE_NEEDED, &base_vha->dpc_flags)) {
                        ql_dbg(ql_dbg_dpc, base_vha, 0x4009,
                            "Quiescence mode scheduled.\n");
@@ -4457,6 +4533,21 @@ qla2x00_module_init(void)
                return -ENOMEM;
        }
 
+       /* Initialize target kmem_cache and mem_pools */
+       ret = qlt_init();
+       if (ret < 0) {
+               kmem_cache_destroy(srb_cachep);
+               return ret;
+       } else if (ret > 0) {
+               /*
+                * If initiator mode is explictly disabled by qlt_init(),
+                * prevent scsi_transport_fc.c:fc_scsi_scan_rport() from
+                * performing scsi_scan_target() during LOOP UP event.
+                */
+               qla2xxx_transport_functions.disable_target_scan = 1;
+               qla2xxx_transport_vport_functions.disable_target_scan = 1;
+       }
+
        /* Derive version string. */
        strcpy(qla2x00_version_str, QLA2XXX_VERSION);
        if (ql2xextended_error_logging)
@@ -4468,6 +4559,7 @@ qla2x00_module_init(void)
                kmem_cache_destroy(srb_cachep);
                ql_log(ql_log_fatal, NULL, 0x0002,
                    "fc_attach_transport failed...Failing load!.\n");
+               qlt_exit();
                return -ENODEV;
        }
 
@@ -4481,6 +4573,7 @@ qla2x00_module_init(void)
            fc_attach_transport(&qla2xxx_transport_vport_functions);
        if (!qla2xxx_transport_vport_template) {
                kmem_cache_destroy(srb_cachep);
+               qlt_exit();
                fc_release_transport(qla2xxx_transport_template);
                ql_log(ql_log_fatal, NULL, 0x0004,
                    "fc_attach_transport vport failed...Failing load!.\n");
@@ -4492,6 +4585,7 @@ qla2x00_module_init(void)
        ret = pci_register_driver(&qla2xxx_pci_driver);
        if (ret) {
                kmem_cache_destroy(srb_cachep);
+               qlt_exit();
                fc_release_transport(qla2xxx_transport_template);
                fc_release_transport(qla2xxx_transport_vport_template);
                ql_log(ql_log_fatal, NULL, 0x0006,
@@ -4511,6 +4605,7 @@ qla2x00_module_exit(void)
        pci_unregister_driver(&qla2xxx_pci_driver);
        qla2x00_release_firmware();
        kmem_cache_destroy(srb_cachep);
+       qlt_exit();
        if (ctx_cachep)
                kmem_cache_destroy(ctx_cachep);
        fc_release_transport(qla2xxx_transport_template);
diff --git a/drivers/scsi/qla2xxx/qla_target.c b/drivers/scsi/qla2xxx/qla_target.c
new file mode 100644 (file)
index 0000000..04f80eb
--- /dev/null
@@ -0,0 +1,4973 @@
+/*
+ *  qla_target.c SCSI LLD infrastructure for QLogic 22xx/23xx/24xx/25xx
+ *
+ *  based on qla2x00t.c code:
+ *
+ *  Copyright (C) 2004 - 2010 Vladislav Bolkhovitin <vst@vlnb.net>
+ *  Copyright (C) 2004 - 2005 Leonid Stoljar
+ *  Copyright (C) 2006 Nathaniel Clark <nate@misrule.us>
+ *  Copyright (C) 2006 - 2010 ID7 Ltd.
+ *
+ *  Forward port and refactoring to modern qla2xxx and target/configfs
+ *
+ *  Copyright (C) 2010-2011 Nicholas A. Bellinger <nab@kernel.org>
+ *
+ *  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, version 2
+ *  of the License.
+ *
+ *  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/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/version.h>
+#include <linux/blkdev.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/list.h>
+#include <linux/workqueue.h>
+#include <asm/unaligned.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_tcq.h>
+#include <target/target_core_base.h>
+#include <target/target_core_fabric.h>
+
+#include "qla_def.h"
+#include "qla_target.h"
+
+static char *qlini_mode = QLA2XXX_INI_MODE_STR_ENABLED;
+module_param(qlini_mode, charp, S_IRUGO);
+MODULE_PARM_DESC(qlini_mode,
+       "Determines when initiator mode will be enabled. Possible values: "
+       "\"exclusive\" - initiator mode will be enabled on load, "
+       "disabled on enabling target mode and then on disabling target mode "
+       "enabled back; "
+       "\"disabled\" - initiator mode will never be enabled; "
+       "\"enabled\" (default) - initiator mode will always stay enabled.");
+
+static int ql2x_ini_mode = QLA2XXX_INI_MODE_EXCLUSIVE;
+
+/*
+ * From scsi/fc/fc_fcp.h
+ */
+enum fcp_resp_rsp_codes {
+       FCP_TMF_CMPL = 0,
+       FCP_DATA_LEN_INVALID = 1,
+       FCP_CMND_FIELDS_INVALID = 2,
+       FCP_DATA_PARAM_MISMATCH = 3,
+       FCP_TMF_REJECTED = 4,
+       FCP_TMF_FAILED = 5,
+       FCP_TMF_INVALID_LUN = 9,
+};
+
+/*
+ * fc_pri_ta from scsi/fc/fc_fcp.h
+ */
+#define FCP_PTA_SIMPLE      0   /* simple task attribute */
+#define FCP_PTA_HEADQ       1   /* head of queue task attribute */
+#define FCP_PTA_ORDERED     2   /* ordered task attribute */
+#define FCP_PTA_ACA         4   /* auto. contigent allegiance */
+#define FCP_PTA_MASK        7   /* mask for task attribute field */
+#define FCP_PRI_SHIFT       3   /* priority field starts in bit 3 */
+#define FCP_PRI_RESVD_MASK  0x80        /* reserved bits in priority field */
+
+/*
+ * This driver calls qla2x00_alloc_iocbs() and qla2x00_issue_marker(), which
+ * must be called under HW lock and could unlock/lock it inside.
+ * It isn't an issue, since in the current implementation on the time when
+ * those functions are called:
+ *
+ *   - Either context is IRQ and only IRQ handler can modify HW data,
+ *     including rings related fields,
+ *
+ *   - Or access to target mode variables from struct qla_tgt doesn't
+ *     cross those functions boundaries, except tgt_stop, which
+ *     additionally protected by irq_cmd_count.
+ */
+/* Predefs for callbacks handed to qla2xxx LLD */
+static void qlt_24xx_atio_pkt(struct scsi_qla_host *ha,
+       struct atio_from_isp *pkt);
+static void qlt_response_pkt(struct scsi_qla_host *ha, response_t *pkt);
+static int qlt_issue_task_mgmt(struct qla_tgt_sess *sess, uint32_t lun,
+       int fn, void *iocb, int flags);
+static void qlt_send_term_exchange(struct scsi_qla_host *ha, struct qla_tgt_cmd
+       *cmd, struct atio_from_isp *atio, int ha_locked);
+static void qlt_reject_free_srr_imm(struct scsi_qla_host *ha,
+       struct qla_tgt_srr_imm *imm, int ha_lock);
+/*
+ * Global Variables
+ */
+static struct kmem_cache *qla_tgt_cmd_cachep;
+static struct kmem_cache *qla_tgt_mgmt_cmd_cachep;
+static mempool_t *qla_tgt_mgmt_cmd_mempool;
+static struct workqueue_struct *qla_tgt_wq;
+static DEFINE_MUTEX(qla_tgt_mutex);
+static LIST_HEAD(qla_tgt_glist);
+
+/* ha->hardware_lock supposed to be held on entry (to protect tgt->sess_list) */
+static struct qla_tgt_sess *qlt_find_sess_by_port_name(
+       struct qla_tgt *tgt,
+       const uint8_t *port_name)
+{
+       struct qla_tgt_sess *sess;
+
+       list_for_each_entry(sess, &tgt->sess_list, sess_list_entry) {
+               if (!memcmp(sess->port_name, port_name, WWN_SIZE))
+                       return sess;
+       }
+
+       return NULL;
+}
+
+/* Might release hw lock, then reaquire!! */
+static inline int qlt_issue_marker(struct scsi_qla_host *vha, int vha_locked)
+{
+       /* Send marker if required */
+       if (unlikely(vha->marker_needed != 0)) {
+               int rc = qla2x00_issue_marker(vha, vha_locked);
+               if (rc != QLA_SUCCESS) {
+                       ql_dbg(ql_dbg_tgt, vha, 0xe03d,
+                           "qla_target(%d): issue_marker() failed\n",
+                           vha->vp_idx);
+               }
+               return rc;
+       }
+       return QLA_SUCCESS;
+}
+
+static inline
+struct scsi_qla_host *qlt_find_host_by_d_id(struct scsi_qla_host *vha,
+       uint8_t *d_id)
+{
+       struct qla_hw_data *ha = vha->hw;
+       uint8_t vp_idx;
+
+       if ((vha->d_id.b.area != d_id[1]) || (vha->d_id.b.domain != d_id[0]))
+               return NULL;
+
+       if (vha->d_id.b.al_pa == d_id[2])
+               return vha;
+
+       BUG_ON(ha->tgt.tgt_vp_map == NULL);
+       vp_idx = ha->tgt.tgt_vp_map[d_id[2]].idx;
+       if (likely(test_bit(vp_idx, ha->vp_idx_map)))
+               return ha->tgt.tgt_vp_map[vp_idx].vha;
+
+       return NULL;
+}
+
+static inline
+struct scsi_qla_host *qlt_find_host_by_vp_idx(struct scsi_qla_host *vha,
+       uint16_t vp_idx)
+{
+       struct qla_hw_data *ha = vha->hw;
+
+       if (vha->vp_idx == vp_idx)
+               return vha;
+
+       BUG_ON(ha->tgt.tgt_vp_map == NULL);
+       if (likely(test_bit(vp_idx, ha->vp_idx_map)))
+               return ha->tgt.tgt_vp_map[vp_idx].vha;
+
+       return NULL;
+}
+
+void qlt_24xx_atio_pkt_all_vps(struct scsi_qla_host *vha,
+       struct atio_from_isp *atio)
+{
+       switch (atio->u.raw.entry_type) {
+       case ATIO_TYPE7:
+       {
+               struct scsi_qla_host *host = qlt_find_host_by_d_id(vha,
+                   atio->u.isp24.fcp_hdr.d_id);
+               if (unlikely(NULL == host)) {
+                       ql_dbg(ql_dbg_tgt, vha, 0xe03e,
+                           "qla_target(%d): Received ATIO_TYPE7 "
+                           "with unknown d_id %x:%x:%x\n", vha->vp_idx,
+                           atio->u.isp24.fcp_hdr.d_id[0],
+                           atio->u.isp24.fcp_hdr.d_id[1],
+                           atio->u.isp24.fcp_hdr.d_id[2]);
+                       break;
+               }
+               qlt_24xx_atio_pkt(host, atio);
+               break;
+       }
+
+       case IMMED_NOTIFY_TYPE:
+       {
+               struct scsi_qla_host *host = vha;
+               struct imm_ntfy_from_isp *entry =
+                   (struct imm_ntfy_from_isp *)atio;
+
+               if ((entry->u.isp24.vp_index != 0xFF) &&
+                   (entry->u.isp24.nport_handle != 0xFFFF)) {
+                       host = qlt_find_host_by_vp_idx(vha,
+                           entry->u.isp24.vp_index);
+                       if (unlikely(!host)) {
+                               ql_dbg(ql_dbg_tgt, vha, 0xe03f,
+                                   "qla_target(%d): Received "
+                                   "ATIO (IMMED_NOTIFY_TYPE) "
+                                   "with unknown vp_index %d\n",
+                                   vha->vp_idx, entry->u.isp24.vp_index);
+                               break;
+                       }
+               }
+               qlt_24xx_atio_pkt(host, atio);
+               break;
+       }
+
+       default:
+               ql_dbg(ql_dbg_tgt, vha, 0xe040,
+                   "qla_target(%d): Received unknown ATIO atio "
+                   "type %x\n", vha->vp_idx, atio->u.raw.entry_type);
+               break;
+       }
+
+       return;
+}
+
+void qlt_response_pkt_all_vps(struct scsi_qla_host *vha, response_t *pkt)
+{
+       switch (pkt->entry_type) {
+       case CTIO_TYPE7:
+       {
+               struct ctio7_from_24xx *entry = (struct ctio7_from_24xx *)pkt;
+               struct scsi_qla_host *host = qlt_find_host_by_vp_idx(vha,
+                   entry->vp_index);
+               if (unlikely(!host)) {
+                       ql_dbg(ql_dbg_tgt, vha, 0xe041,
+                           "qla_target(%d): Response pkt (CTIO_TYPE7) "
+                           "received, with unknown vp_index %d\n",
+                           vha->vp_idx, entry->vp_index);
+                       break;
+               }
+               qlt_response_pkt(host, pkt);
+               break;
+       }
+
+       case IMMED_NOTIFY_TYPE:
+       {
+               struct scsi_qla_host *host = vha;
+               struct imm_ntfy_from_isp *entry =
+                   (struct imm_ntfy_from_isp *)pkt;
+
+               host = qlt_find_host_by_vp_idx(vha, entry->u.isp24.vp_index);
+               if (unlikely(!host)) {
+                       ql_dbg(ql_dbg_tgt, vha, 0xe042,
+                           "qla_target(%d): Response pkt (IMMED_NOTIFY_TYPE) "
+                           "received, with unknown vp_index %d\n",
+                           vha->vp_idx, entry->u.isp24.vp_index);
+                       break;
+               }
+               qlt_response_pkt(host, pkt);
+               break;
+       }
+
+       case NOTIFY_ACK_TYPE:
+       {
+               struct scsi_qla_host *host = vha;
+               struct nack_to_isp *entry = (struct nack_to_isp *)pkt;
+
+               if (0xFF != entry->u.isp24.vp_index) {
+                       host = qlt_find_host_by_vp_idx(vha,
+                           entry->u.isp24.vp_index);
+                       if (unlikely(!host)) {
+                               ql_dbg(ql_dbg_tgt, vha, 0xe043,
+                                   "qla_target(%d): Response "
+                                   "pkt (NOTIFY_ACK_TYPE) "
+                                   "received, with unknown "
+                                   "vp_index %d\n", vha->vp_idx,
+                                   entry->u.isp24.vp_index);
+                               break;
+                       }
+               }
+               qlt_response_pkt(host, pkt);
+               break;
+       }
+
+       case ABTS_RECV_24XX:
+       {
+               struct abts_recv_from_24xx *entry =
+                   (struct abts_recv_from_24xx *)pkt;
+               struct scsi_qla_host *host = qlt_find_host_by_vp_idx(vha,
+                   entry->vp_index);
+               if (unlikely(!host)) {
+                       ql_dbg(ql_dbg_tgt, vha, 0xe044,
+                           "qla_target(%d): Response pkt "
+                           "(ABTS_RECV_24XX) received, with unknown "
+                           "vp_index %d\n", vha->vp_idx, entry->vp_index);
+                       break;
+               }
+               qlt_response_pkt(host, pkt);
+               break;
+       }
+
+       case ABTS_RESP_24XX:
+       {
+               struct abts_resp_to_24xx *entry =
+                   (struct abts_resp_to_24xx *)pkt;
+               struct scsi_qla_host *host = qlt_find_host_by_vp_idx(vha,
+                   entry->vp_index);
+               if (unlikely(!host)) {
+                       ql_dbg(ql_dbg_tgt, vha, 0xe045,
+                           "qla_target(%d): Response pkt "
+                           "(ABTS_RECV_24XX) received, with unknown "
+                           "vp_index %d\n", vha->vp_idx, entry->vp_index);
+                       break;
+               }
+               qlt_response_pkt(host, pkt);
+               break;
+       }
+
+       default:
+               qlt_response_pkt(vha, pkt);
+               break;
+       }
+
+}
+
+static void qlt_free_session_done(struct work_struct *work)
+{
+       struct qla_tgt_sess *sess = container_of(work, struct qla_tgt_sess,
+           free_work);
+       struct qla_tgt *tgt = sess->tgt;
+       struct scsi_qla_host *vha = sess->vha;
+       struct qla_hw_data *ha = vha->hw;
+
+       BUG_ON(!tgt);
+       /*
+        * Release the target session for FC Nexus from fabric module code.
+        */
+       if (sess->se_sess != NULL)
+               ha->tgt.tgt_ops->free_session(sess);
+
+       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf001,
+           "Unregistration of sess %p finished\n", sess);
+
+       kfree(sess);
+       /*
+        * We need to protect against race, when tgt is freed before or
+        * inside wake_up()
+        */
+       tgt->sess_count--;
+       if (tgt->sess_count == 0)
+               wake_up_all(&tgt->waitQ);
+}
+
+/* ha->hardware_lock supposed to be held on entry */
+void qlt_unreg_sess(struct qla_tgt_sess *sess)
+{
+       struct scsi_qla_host *vha = sess->vha;
+
+       vha->hw->tgt.tgt_ops->clear_nacl_from_fcport_map(sess);
+
+       list_del(&sess->sess_list_entry);
+       if (sess->deleted)
+               list_del(&sess->del_list_entry);
+
+       INIT_WORK(&sess->free_work, qlt_free_session_done);
+       schedule_work(&sess->free_work);
+}
+EXPORT_SYMBOL(qlt_unreg_sess);
+
+/* ha->hardware_lock supposed to be held on entry */
+static int qlt_reset(struct scsi_qla_host *vha, void *iocb, int mcmd)
+{
+       struct qla_hw_data *ha = vha->hw;
+       struct qla_tgt_sess *sess = NULL;
+       uint32_t unpacked_lun, lun = 0;
+       uint16_t loop_id;
+       int res = 0;
+       struct imm_ntfy_from_isp *n = (struct imm_ntfy_from_isp *)iocb;
+       struct atio_from_isp *a = (struct atio_from_isp *)iocb;
+
+       loop_id = le16_to_cpu(n->u.isp24.nport_handle);
+       if (loop_id == 0xFFFF) {
+#if 0 /* FIXME: Re-enable Global event handling.. */
+               /* Global event */
+               atomic_inc(&ha->tgt.qla_tgt->tgt_global_resets_count);
+               qlt_clear_tgt_db(ha->tgt.qla_tgt, 1);
+               if (!list_empty(&ha->tgt.qla_tgt->sess_list)) {
+                       sess = list_entry(ha->tgt.qla_tgt->sess_list.next,
+                           typeof(*sess), sess_list_entry);
+                       switch (mcmd) {
+                       case QLA_TGT_NEXUS_LOSS_SESS:
+                               mcmd = QLA_TGT_NEXUS_LOSS;
+                               break;
+                       case QLA_TGT_ABORT_ALL_SESS:
+                               mcmd = QLA_TGT_ABORT_ALL;
+                               break;
+                       case QLA_TGT_NEXUS_LOSS:
+                       case QLA_TGT_ABORT_ALL:
+                               break;
+                       default:
+                               ql_dbg(ql_dbg_tgt, vha, 0xe046,
+                                   "qla_target(%d): Not allowed "
+                                   "command %x in %s", vha->vp_idx,
+                                   mcmd, __func__);
+                               sess = NULL;
+                               break;
+                       }
+               } else
+                       sess = NULL;
+#endif
+       } else {
+               sess = ha->tgt.tgt_ops->find_sess_by_loop_id(vha, loop_id);
+       }
+
+       ql_dbg(ql_dbg_tgt, vha, 0xe000,
+           "Using sess for qla_tgt_reset: %p\n", sess);
+       if (!sess) {
+               res = -ESRCH;
+               return res;
+       }
+
+       ql_dbg(ql_dbg_tgt, vha, 0xe047,
+           "scsi(%ld): resetting (session %p from port "
+           "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x, "
+           "mcmd %x, loop_id %d)\n", vha->host_no, sess,
+           sess->port_name[0], sess->port_name[1],
+           sess->port_name[2], sess->port_name[3],
+           sess->port_name[4], sess->port_name[5],
+           sess->port_name[6], sess->port_name[7],
+           mcmd, loop_id);
+
+       lun = a->u.isp24.fcp_cmnd.lun;
+       unpacked_lun = scsilun_to_int((struct scsi_lun *)&lun);
+
+       return qlt_issue_task_mgmt(sess, unpacked_lun, mcmd,
+           iocb, QLA24XX_MGMT_SEND_NACK);
+}
+
+/* ha->hardware_lock supposed to be held on entry */
+static void qlt_schedule_sess_for_deletion(struct qla_tgt_sess *sess,
+       bool immediate)
+{
+       struct qla_tgt *tgt = sess->tgt;
+       uint32_t dev_loss_tmo = tgt->ha->port_down_retry_count + 5;
+
+       if (sess->deleted)
+               return;
+
+       ql_dbg(ql_dbg_tgt, sess->vha, 0xe001,
+           "Scheduling sess %p for deletion\n", sess);
+       list_add_tail(&sess->del_list_entry, &tgt->del_sess_list);
+       sess->deleted = 1;
+
+       if (immediate)
+               dev_loss_tmo = 0;
+
+       sess->expires = jiffies + dev_loss_tmo * HZ;
+
+       ql_dbg(ql_dbg_tgt, sess->vha, 0xe048,
+           "qla_target(%d): session for port %02x:%02x:%02x:"
+           "%02x:%02x:%02x:%02x:%02x (loop ID %d) scheduled for "
+           "deletion in %u secs (expires: %lu) immed: %d\n",
+           sess->vha->vp_idx,
+           sess->port_name[0], sess->port_name[1],
+           sess->port_name[2], sess->port_name[3],
+           sess->port_name[4], sess->port_name[5],
+           sess->port_name[6], sess->port_name[7],
+           sess->loop_id, dev_loss_tmo, sess->expires, immediate);
+
+       if (immediate)
+               schedule_delayed_work(&tgt->sess_del_work, 0);
+       else
+               schedule_delayed_work(&tgt->sess_del_work,
+                   jiffies - sess->expires);
+}
+
+/* ha->hardware_lock supposed to be held on entry */
+static void qlt_clear_tgt_db(struct qla_tgt *tgt, bool local_only)
+{
+       struct qla_tgt_sess *sess;
+
+       list_for_each_entry(sess, &tgt->sess_list, sess_list_entry)
+               qlt_schedule_sess_for_deletion(sess, true);
+
+       /* At this point tgt could be already dead */
+}
+
+static int qla24xx_get_loop_id(struct scsi_qla_host *vha, const uint8_t *s_id,
+       uint16_t *loop_id)
+{
+       struct qla_hw_data *ha = vha->hw;
+       dma_addr_t gid_list_dma;
+       struct gid_list_info *gid_list;
+       char *id_iter;
+       int res, rc, i;
+       uint16_t entries;
+
+       gid_list = dma_alloc_coherent(&ha->pdev->dev, qla2x00_gid_list_size(ha),
+           &gid_list_dma, GFP_KERNEL);
+       if (!gid_list) {
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf044,
+                   "qla_target(%d): DMA Alloc failed of %u\n",
+                   vha->vp_idx, qla2x00_gid_list_size(ha));
+               return -ENOMEM;
+       }
+
+       /* Get list of logged in devices */
+       rc = qla2x00_get_id_list(vha, gid_list, gid_list_dma, &entries);
+       if (rc != QLA_SUCCESS) {
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf045,
+                   "qla_target(%d): get_id_list() failed: %x\n",
+                   vha->vp_idx, rc);
+               res = -1;
+               goto out_free_id_list;
+       }
+
+       id_iter = (char *)gid_list;
+       res = -1;
+       for (i = 0; i < entries; i++) {
+               struct gid_list_info *gid = (struct gid_list_info *)id_iter;
+               if ((gid->al_pa == s_id[2]) &&
+                   (gid->area == s_id[1]) &&
+                   (gid->domain == s_id[0])) {
+                       *loop_id = le16_to_cpu(gid->loop_id);
+                       res = 0;
+                       break;
+               }
+               id_iter += ha->gid_list_info_size;
+       }
+
+out_free_id_list:
+       dma_free_coherent(&ha->pdev->dev, qla2x00_gid_list_size(ha),
+           gid_list, gid_list_dma);
+       return res;
+}
+
+static bool qlt_check_fcport_exist(struct scsi_qla_host *vha,
+       struct qla_tgt_sess *sess)
+{
+       struct qla_hw_data *ha = vha->hw;
+       struct qla_port_24xx_data *pmap24;
+       bool res, found = false;
+       int rc, i;
+       uint16_t loop_id = 0xFFFF; /* to eliminate compiler's warning */
+       uint16_t entries;
+       void *pmap;
+       int pmap_len;
+       fc_port_t *fcport;
+       int global_resets;
+
+retry:
+       global_resets = atomic_read(&ha->tgt.qla_tgt->tgt_global_resets_count);
+
+       rc = qla2x00_get_node_name_list(vha, &pmap, &pmap_len);
+       if (rc != QLA_SUCCESS) {
+               res = false;
+               goto out;
+       }
+
+       pmap24 = pmap;
+       entries = pmap_len/sizeof(*pmap24);
+
+       for (i = 0; i < entries; ++i) {
+               if (!memcmp(sess->port_name, pmap24[i].port_name, WWN_SIZE)) {
+                       loop_id = le16_to_cpu(pmap24[i].loop_id);
+                       found = true;
+                       break;
+               }
+       }
+
+       kfree(pmap);
+
+       if (!found) {
+               res = false;
+               goto out;
+       }
+
+       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf046,
+           "qlt_check_fcport_exist(): loop_id %d", loop_id);
+
+       fcport = kzalloc(sizeof(*fcport), GFP_KERNEL);
+       if (fcport == NULL) {
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf047,
+                   "qla_target(%d): Allocation of tmp FC port failed",
+                   vha->vp_idx);
+               res = false;
+               goto out;
+       }
+
+       fcport->loop_id = loop_id;
+
+       rc = qla2x00_get_port_database(vha, fcport, 0);
+       if (rc != QLA_SUCCESS) {
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf048,
+                   "qla_target(%d): Failed to retrieve fcport "
+                   "information -- get_port_database() returned %x "
+                   "(loop_id=0x%04x)", vha->vp_idx, rc, loop_id);
+               res = false;
+               goto out_free_fcport;
+       }
+
+       if (global_resets !=
+           atomic_read(&ha->tgt.qla_tgt->tgt_global_resets_count)) {
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf002,
+                   "qla_target(%d): global reset during session discovery"
+                   " (counter was %d, new %d), retrying",
+                   vha->vp_idx, global_resets,
+                   atomic_read(&ha->tgt.qla_tgt->tgt_global_resets_count));
+               goto retry;
+       }
+
+       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf003,
+           "Updating sess %p s_id %x:%x:%x, loop_id %d) to d_id %x:%x:%x, "
+           "loop_id %d", sess, sess->s_id.b.domain, sess->s_id.b.al_pa,
+           sess->s_id.b.area, sess->loop_id, fcport->d_id.b.domain,
+           fcport->d_id.b.al_pa, fcport->d_id.b.area, fcport->loop_id);
+
+       sess->s_id = fcport->d_id;
+       sess->loop_id = fcport->loop_id;
+       sess->conf_compl_supported = !!(fcport->flags &
+           FCF_CONF_COMP_SUPPORTED);
+
+       res = true;
+
+out_free_fcport:
+       kfree(fcport);
+
+out:
+       return res;
+}
+
+/* ha->hardware_lock supposed to be held on entry */
+static void qlt_undelete_sess(struct qla_tgt_sess *sess)
+{
+       BUG_ON(!sess->deleted);
+
+       list_del(&sess->del_list_entry);
+       sess->deleted = 0;
+}
+
+static void qlt_del_sess_work_fn(struct delayed_work *work)
+{
+       struct qla_tgt *tgt = container_of(work, struct qla_tgt,
+           sess_del_work);
+       struct scsi_qla_host *vha = tgt->vha;
+       struct qla_hw_data *ha = vha->hw;
+       struct qla_tgt_sess *sess;
+       unsigned long flags;
+
+       spin_lock_irqsave(&ha->hardware_lock, flags);
+       while (!list_empty(&tgt->del_sess_list)) {
+               sess = list_entry(tgt->del_sess_list.next, typeof(*sess),
+                   del_list_entry);
+               if (time_after_eq(jiffies, sess->expires)) {
+                       bool cancel;
+
+                       qlt_undelete_sess(sess);
+
+                       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+                       cancel = qlt_check_fcport_exist(vha, sess);
+
+                       if (cancel) {
+                               if (sess->deleted) {
+                                       /*
+                                        * sess was again deleted while we were
+                                        * discovering it
+                                        */
+                                       spin_lock_irqsave(&ha->hardware_lock,
+                                           flags);
+                                       continue;
+                               }
+
+                               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf049,
+                                   "qla_target(%d): cancel deletion of "
+                                   "session for port %02x:%02x:%02x:%02x:%02x:"
+                                   "%02x:%02x:%02x (loop ID %d), because "
+                                   " it isn't deleted by firmware",
+                                   vha->vp_idx, sess->port_name[0],
+                                   sess->port_name[1], sess->port_name[2],
+                                   sess->port_name[3], sess->port_name[4],
+                                   sess->port_name[5], sess->port_name[6],
+                                   sess->port_name[7], sess->loop_id);
+                       } else {
+                               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf004,
+                                   "Timeout: sess %p about to be deleted\n",
+                                   sess);
+                               ha->tgt.tgt_ops->shutdown_sess(sess);
+                               ha->tgt.tgt_ops->put_sess(sess);
+                       }
+
+                       spin_lock_irqsave(&ha->hardware_lock, flags);
+               } else {
+                       schedule_delayed_work(&tgt->sess_del_work,
+                           jiffies - sess->expires);
+                       break;
+               }
+       }
+       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+}
+
+/*
+ * Adds an extra ref to allow to drop hw lock after adding sess to the list.
+ * Caller must put it.
+ */
+static struct qla_tgt_sess *qlt_create_sess(
+       struct scsi_qla_host *vha,
+       fc_port_t *fcport,
+       bool local)
+{
+       struct qla_hw_data *ha = vha->hw;
+       struct qla_tgt_sess *sess;
+       unsigned long flags;
+       unsigned char be_sid[3];
+
+       /* Check to avoid double sessions */
+       spin_lock_irqsave(&ha->hardware_lock, flags);
+       list_for_each_entry(sess, &ha->tgt.qla_tgt->sess_list,
+                               sess_list_entry) {
+               if (!memcmp(sess->port_name, fcport->port_name, WWN_SIZE)) {
+                       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf005,
+                           "Double sess %p found (s_id %x:%x:%x, "
+                           "loop_id %d), updating to d_id %x:%x:%x, "
+                           "loop_id %d", sess, sess->s_id.b.domain,
+                           sess->s_id.b.al_pa, sess->s_id.b.area,
+                           sess->loop_id, fcport->d_id.b.domain,
+                           fcport->d_id.b.al_pa, fcport->d_id.b.area,
+                           fcport->loop_id);
+
+                       if (sess->deleted)
+                               qlt_undelete_sess(sess);
+
+                       kref_get(&sess->se_sess->sess_kref);
+                       sess->s_id = fcport->d_id;
+                       sess->loop_id = fcport->loop_id;
+                       sess->conf_compl_supported = !!(fcport->flags &
+                           FCF_CONF_COMP_SUPPORTED);
+                       if (sess->local && !local)
+                               sess->local = 0;
+                       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+                       return sess;
+               }
+       }
+       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+       sess = kzalloc(sizeof(*sess), GFP_KERNEL);
+       if (!sess) {
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf04a,
+                   "qla_target(%u): session allocation failed, "
+                   "all commands from port %02x:%02x:%02x:%02x:"
+                   "%02x:%02x:%02x:%02x will be refused", vha->vp_idx,
+                   fcport->port_name[0], fcport->port_name[1],
+                   fcport->port_name[2], fcport->port_name[3],
+                   fcport->port_name[4], fcport->port_name[5],
+                   fcport->port_name[6], fcport->port_name[7]);
+
+               return NULL;
+       }
+       sess->tgt = ha->tgt.qla_tgt;
+       sess->vha = vha;
+       sess->s_id = fcport->d_id;
+       sess->loop_id = fcport->loop_id;
+       sess->local = local;
+
+       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf006,
+           "Adding sess %p to tgt %p via ->check_initiator_node_acl()\n",
+           sess, ha->tgt.qla_tgt);
+
+       be_sid[0] = sess->s_id.b.domain;
+       be_sid[1] = sess->s_id.b.area;
+       be_sid[2] = sess->s_id.b.al_pa;
+       /*
+        * Determine if this fc_port->port_name is allowed to access
+        * target mode using explict NodeACLs+MappedLUNs, or using
+        * TPG demo mode.  If this is successful a target mode FC nexus
+        * is created.
+        */
+       if (ha->tgt.tgt_ops->check_initiator_node_acl(vha,
+           &fcport->port_name[0], sess, &be_sid[0], fcport->loop_id) < 0) {
+               kfree(sess);
+               return NULL;
+       }
+       /*
+        * Take an extra reference to ->sess_kref here to handle qla_tgt_sess
+        * access across ->hardware_lock reaquire.
+        */
+       kref_get(&sess->se_sess->sess_kref);
+
+       sess->conf_compl_supported = !!(fcport->flags &
+           FCF_CONF_COMP_SUPPORTED);
+       BUILD_BUG_ON(sizeof(sess->port_name) != sizeof(fcport->port_name));
+       memcpy(sess->port_name, fcport->port_name, sizeof(sess->port_name));
+
+       spin_lock_irqsave(&ha->hardware_lock, flags);
+       list_add_tail(&sess->sess_list_entry, &ha->tgt.qla_tgt->sess_list);
+       ha->tgt.qla_tgt->sess_count++;
+       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf04b,
+           "qla_target(%d): %ssession for wwn %02x:%02x:%02x:%02x:"
+           "%02x:%02x:%02x:%02x (loop_id %d, s_id %x:%x:%x, confirmed"
+           " completion %ssupported) added\n",
+           vha->vp_idx, local ?  "local " : "", fcport->port_name[0],
+           fcport->port_name[1], fcport->port_name[2], fcport->port_name[3],
+           fcport->port_name[4], fcport->port_name[5], fcport->port_name[6],
+           fcport->port_name[7], fcport->loop_id, sess->s_id.b.domain,
+           sess->s_id.b.area, sess->s_id.b.al_pa, sess->conf_compl_supported ?
+           "" : "not ");
+
+       return sess;
+}
+
+/*
+ * Called from drivers/scsi/qla2xxx/qla_init.c:qla2x00_reg_remote_port()
+ */
+void qlt_fc_port_added(struct scsi_qla_host *vha, fc_port_t *fcport)
+{
+       struct qla_hw_data *ha = vha->hw;
+       struct qla_tgt *tgt = ha->tgt.qla_tgt;
+       struct qla_tgt_sess *sess;
+       unsigned long flags;
+
+       if (!vha->hw->tgt.tgt_ops)
+               return;
+
+       if (!tgt || (fcport->port_type != FCT_INITIATOR))
+               return;
+
+       spin_lock_irqsave(&ha->hardware_lock, flags);
+       if (tgt->tgt_stop) {
+               spin_unlock_irqrestore(&ha->hardware_lock, flags);
+               return;
+       }
+       sess = qlt_find_sess_by_port_name(tgt, fcport->port_name);
+       if (!sess) {
+               spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+               mutex_lock(&ha->tgt.tgt_mutex);
+               sess = qlt_create_sess(vha, fcport, false);
+               mutex_unlock(&ha->tgt.tgt_mutex);
+
+               spin_lock_irqsave(&ha->hardware_lock, flags);
+       } else {
+               kref_get(&sess->se_sess->sess_kref);
+
+               if (sess->deleted) {
+                       qlt_undelete_sess(sess);
+
+                       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf04c,
+                           "qla_target(%u): %ssession for port %02x:"
+                           "%02x:%02x:%02x:%02x:%02x:%02x:%02x (loop ID %d) "
+                           "reappeared\n", vha->vp_idx, sess->local ? "local "
+                           : "", sess->port_name[0], sess->port_name[1],
+                           sess->port_name[2], sess->port_name[3],
+                           sess->port_name[4], sess->port_name[5],
+                           sess->port_name[6], sess->port_name[7],
+                           sess->loop_id);
+
+                       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf007,
+                           "Reappeared sess %p\n", sess);
+               }
+               sess->s_id = fcport->d_id;
+               sess->loop_id = fcport->loop_id;
+               sess->conf_compl_supported = !!(fcport->flags &
+                   FCF_CONF_COMP_SUPPORTED);
+       }
+
+       if (sess && sess->local) {
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf04d,
+                   "qla_target(%u): local session for "
+                   "port %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x "
+                   "(loop ID %d) became global\n", vha->vp_idx,
+                   fcport->port_name[0], fcport->port_name[1],
+                   fcport->port_name[2], fcport->port_name[3],
+                   fcport->port_name[4], fcport->port_name[5],
+                   fcport->port_name[6], fcport->port_name[7],
+                   sess->loop_id);
+               sess->local = 0;
+       }
+       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+       ha->tgt.tgt_ops->put_sess(sess);
+}
+
+void qlt_fc_port_deleted(struct scsi_qla_host *vha, fc_port_t *fcport)
+{
+       struct qla_hw_data *ha = vha->hw;
+       struct qla_tgt *tgt = ha->tgt.qla_tgt;
+       struct qla_tgt_sess *sess;
+       unsigned long flags;
+
+       if (!vha->hw->tgt.tgt_ops)
+               return;
+
+       if (!tgt || (fcport->port_type != FCT_INITIATOR))
+               return;
+
+       spin_lock_irqsave(&ha->hardware_lock, flags);
+       if (tgt->tgt_stop) {
+               spin_unlock_irqrestore(&ha->hardware_lock, flags);
+               return;
+       }
+       sess = qlt_find_sess_by_port_name(tgt, fcport->port_name);
+       if (!sess) {
+               spin_unlock_irqrestore(&ha->hardware_lock, flags);
+               return;
+       }
+
+       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf008, "qla_tgt_fc_port_deleted %p", sess);
+
+       sess->local = 1;
+       qlt_schedule_sess_for_deletion(sess, false);
+       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+}
+
+static inline int test_tgt_sess_count(struct qla_tgt *tgt)
+{
+       struct qla_hw_data *ha = tgt->ha;
+       unsigned long flags;
+       int res;
+       /*
+        * We need to protect against race, when tgt is freed before or
+        * inside wake_up()
+        */
+       spin_lock_irqsave(&ha->hardware_lock, flags);
+       ql_dbg(ql_dbg_tgt, tgt->vha, 0xe002,
+           "tgt %p, empty(sess_list)=%d sess_count=%d\n",
+           tgt, list_empty(&tgt->sess_list), tgt->sess_count);
+       res = (tgt->sess_count == 0);
+       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+       return res;
+}
+
+/* Called by tcm_qla2xxx configfs code */
+void qlt_stop_phase1(struct qla_tgt *tgt)
+{
+       struct scsi_qla_host *vha = tgt->vha;
+       struct qla_hw_data *ha = tgt->ha;
+       unsigned long flags;
+
+       if (tgt->tgt_stop || tgt->tgt_stopped) {
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf04e,
+                   "Already in tgt->tgt_stop or tgt_stopped state\n");
+               dump_stack();
+               return;
+       }
+
+       ql_dbg(ql_dbg_tgt, vha, 0xe003, "Stopping target for host %ld(%p)\n",
+           vha->host_no, vha);
+       /*
+        * Mutex needed to sync with qla_tgt_fc_port_[added,deleted].
+        * Lock is needed, because we still can get an incoming packet.
+        */
+       mutex_lock(&ha->tgt.tgt_mutex);
+       spin_lock_irqsave(&ha->hardware_lock, flags);
+       tgt->tgt_stop = 1;
+       qlt_clear_tgt_db(tgt, true);
+       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+       mutex_unlock(&ha->tgt.tgt_mutex);
+
+       flush_delayed_work_sync(&tgt->sess_del_work);
+
+       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf009,
+           "Waiting for sess works (tgt %p)", tgt);
+       spin_lock_irqsave(&tgt->sess_work_lock, flags);
+       while (!list_empty(&tgt->sess_works_list)) {
+               spin_unlock_irqrestore(&tgt->sess_work_lock, flags);
+               flush_scheduled_work();
+               spin_lock_irqsave(&tgt->sess_work_lock, flags);
+       }
+       spin_unlock_irqrestore(&tgt->sess_work_lock, flags);
+
+       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf00a,
+           "Waiting for tgt %p: list_empty(sess_list)=%d "
+           "sess_count=%d\n", tgt, list_empty(&tgt->sess_list),
+           tgt->sess_count);
+
+       wait_event(tgt->waitQ, test_tgt_sess_count(tgt));
+
+       /* Big hammer */
+       if (!ha->flags.host_shutting_down && qla_tgt_mode_enabled(vha))
+               qlt_disable_vha(vha);
+
+       /* Wait for sessions to clear out (just in case) */
+       wait_event(tgt->waitQ, test_tgt_sess_count(tgt));
+}
+EXPORT_SYMBOL(qlt_stop_phase1);
+
+/* Called by tcm_qla2xxx configfs code */
+void qlt_stop_phase2(struct qla_tgt *tgt)
+{
+       struct qla_hw_data *ha = tgt->ha;
+       unsigned long flags;
+
+       if (tgt->tgt_stopped) {
+               ql_dbg(ql_dbg_tgt_mgt, tgt->vha, 0xf04f,
+                   "Already in tgt->tgt_stopped state\n");
+               dump_stack();
+               return;
+       }
+
+       ql_dbg(ql_dbg_tgt_mgt, tgt->vha, 0xf00b,
+           "Waiting for %d IRQ commands to complete (tgt %p)",
+           tgt->irq_cmd_count, tgt);
+
+       mutex_lock(&ha->tgt.tgt_mutex);
+       spin_lock_irqsave(&ha->hardware_lock, flags);
+       while (tgt->irq_cmd_count != 0) {
+               spin_unlock_irqrestore(&ha->hardware_lock, flags);
+               udelay(2);
+               spin_lock_irqsave(&ha->hardware_lock, flags);
+       }
+       tgt->tgt_stop = 0;
+       tgt->tgt_stopped = 1;
+       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+       mutex_unlock(&ha->tgt.tgt_mutex);
+
+       ql_dbg(ql_dbg_tgt_mgt, tgt->vha, 0xf00c, "Stop of tgt %p finished",
+           tgt);
+}
+EXPORT_SYMBOL(qlt_stop_phase2);
+
+/* Called from qlt_remove_target() -> qla2x00_remove_one() */
+void qlt_release(struct qla_tgt *tgt)
+{
+       struct qla_hw_data *ha = tgt->ha;
+
+       if ((ha->tgt.qla_tgt != NULL) && !tgt->tgt_stopped)
+               qlt_stop_phase2(tgt);
+
+       ha->tgt.qla_tgt = NULL;
+
+       ql_dbg(ql_dbg_tgt_mgt, tgt->vha, 0xf00d,
+           "Release of tgt %p finished\n", tgt);
+
+       kfree(tgt);
+}
+
+/* ha->hardware_lock supposed to be held on entry */
+static int qlt_sched_sess_work(struct qla_tgt *tgt, int type,
+       const void *param, unsigned int param_size)
+{
+       struct qla_tgt_sess_work_param *prm;
+       unsigned long flags;
+
+       prm = kzalloc(sizeof(*prm), GFP_ATOMIC);
+       if (!prm) {
+               ql_dbg(ql_dbg_tgt_mgt, tgt->vha, 0xf050,
+                   "qla_target(%d): Unable to create session "
+                   "work, command will be refused", 0);
+               return -ENOMEM;
+       }
+
+       ql_dbg(ql_dbg_tgt_mgt, tgt->vha, 0xf00e,
+           "Scheduling work (type %d, prm %p)"
+           " to find session for param %p (size %d, tgt %p)\n",
+           type, prm, param, param_size, tgt);
+
+       prm->type = type;
+       memcpy(&prm->tm_iocb, param, param_size);
+
+       spin_lock_irqsave(&tgt->sess_work_lock, flags);
+       list_add_tail(&prm->sess_works_list_entry, &tgt->sess_works_list);
+       spin_unlock_irqrestore(&tgt->sess_work_lock, flags);
+
+       schedule_work(&tgt->sess_work);
+
+       return 0;
+}
+
+/*
+ * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire
+ */
+static void qlt_send_notify_ack(struct scsi_qla_host *vha,
+       struct imm_ntfy_from_isp *ntfy,
+       uint32_t add_flags, uint16_t resp_code, int resp_code_valid,
+       uint16_t srr_flags, uint16_t srr_reject_code, uint8_t srr_explan)
+{
+       struct qla_hw_data *ha = vha->hw;
+       request_t *pkt;
+       struct nack_to_isp *nack;
+
+       ql_dbg(ql_dbg_tgt, vha, 0xe004, "Sending NOTIFY_ACK (ha=%p)\n", ha);
+
+       /* Send marker if required */
+       if (qlt_issue_marker(vha, 1) != QLA_SUCCESS)
+               return;
+
+       pkt = (request_t *)qla2x00_alloc_iocbs(vha, NULL);
+       if (!pkt) {
+               ql_dbg(ql_dbg_tgt, vha, 0xe049,
+                   "qla_target(%d): %s failed: unable to allocate "
+                   "request packet\n", vha->vp_idx, __func__);
+               return;
+       }
+
+       if (ha->tgt.qla_tgt != NULL)
+               ha->tgt.qla_tgt->notify_ack_expected++;
+
+       pkt->entry_type = NOTIFY_ACK_TYPE;
+       pkt->entry_count = 1;
+
+       nack = (struct nack_to_isp *)pkt;
+       nack->ox_id = ntfy->ox_id;
+
+       nack->u.isp24.nport_handle = ntfy->u.isp24.nport_handle;
+       if (le16_to_cpu(ntfy->u.isp24.status) == IMM_NTFY_ELS) {
+               nack->u.isp24.flags = ntfy->u.isp24.flags &
+                       __constant_cpu_to_le32(NOTIFY24XX_FLAGS_PUREX_IOCB);
+       }
+       nack->u.isp24.srr_rx_id = ntfy->u.isp24.srr_rx_id;
+       nack->u.isp24.status = ntfy->u.isp24.status;
+       nack->u.isp24.status_subcode = ntfy->u.isp24.status_subcode;
+       nack->u.isp24.exchange_address = ntfy->u.isp24.exchange_address;
+       nack->u.isp24.srr_rel_offs = ntfy->u.isp24.srr_rel_offs;
+       nack->u.isp24.srr_ui = ntfy->u.isp24.srr_ui;
+       nack->u.isp24.srr_flags = cpu_to_le16(srr_flags);
+       nack->u.isp24.srr_reject_code = srr_reject_code;
+       nack->u.isp24.srr_reject_code_expl = srr_explan;
+       nack->u.isp24.vp_index = ntfy->u.isp24.vp_index;
+
+       ql_dbg(ql_dbg_tgt, vha, 0xe005,
+           "qla_target(%d): Sending 24xx Notify Ack %d\n",
+           vha->vp_idx, nack->u.isp24.status);
+
+       qla2x00_start_iocbs(vha, vha->req);
+}
+
+/*
+ * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire
+ */
+static void qlt_24xx_send_abts_resp(struct scsi_qla_host *vha,
+       struct abts_recv_from_24xx *abts, uint32_t status,
+       bool ids_reversed)
+{
+       struct qla_hw_data *ha = vha->hw;
+       struct abts_resp_to_24xx *resp;
+       uint32_t f_ctl;
+       uint8_t *p;
+
+       ql_dbg(ql_dbg_tgt, vha, 0xe006,
+           "Sending task mgmt ABTS response (ha=%p, atio=%p, status=%x\n",
+           ha, abts, status);
+
+       /* Send marker if required */
+       if (qlt_issue_marker(vha, 1) != QLA_SUCCESS)
+               return;
+
+       resp = (struct abts_resp_to_24xx *)qla2x00_alloc_iocbs(vha, NULL);
+       if (!resp) {
+               ql_dbg(ql_dbg_tgt, vha, 0xe04a,
+                   "qla_target(%d): %s failed: unable to allocate "
+                   "request packet", vha->vp_idx, __func__);
+               return;
+       }
+
+       resp->entry_type = ABTS_RESP_24XX;
+       resp->entry_count = 1;
+       resp->nport_handle = abts->nport_handle;
+       resp->vp_index = vha->vp_idx;
+       resp->sof_type = abts->sof_type;
+       resp->exchange_address = abts->exchange_address;
+       resp->fcp_hdr_le = abts->fcp_hdr_le;
+       f_ctl = __constant_cpu_to_le32(F_CTL_EXCH_CONTEXT_RESP |
+           F_CTL_LAST_SEQ | F_CTL_END_SEQ |
+           F_CTL_SEQ_INITIATIVE);
+       p = (uint8_t *)&f_ctl;
+       resp->fcp_hdr_le.f_ctl[0] = *p++;
+       resp->fcp_hdr_le.f_ctl[1] = *p++;
+       resp->fcp_hdr_le.f_ctl[2] = *p;
+       if (ids_reversed) {
+               resp->fcp_hdr_le.d_id[0] = abts->fcp_hdr_le.d_id[0];
+               resp->fcp_hdr_le.d_id[1] = abts->fcp_hdr_le.d_id[1];
+               resp->fcp_hdr_le.d_id[2] = abts->fcp_hdr_le.d_id[2];
+               resp->fcp_hdr_le.s_id[0] = abts->fcp_hdr_le.s_id[0];
+               resp->fcp_hdr_le.s_id[1] = abts->fcp_hdr_le.s_id[1];
+               resp->fcp_hdr_le.s_id[2] = abts->fcp_hdr_le.s_id[2];
+       } else {
+               resp->fcp_hdr_le.d_id[0] = abts->fcp_hdr_le.s_id[0];
+               resp->fcp_hdr_le.d_id[1] = abts->fcp_hdr_le.s_id[1];
+               resp->fcp_hdr_le.d_id[2] = abts->fcp_hdr_le.s_id[2];
+               resp->fcp_hdr_le.s_id[0] = abts->fcp_hdr_le.d_id[0];
+               resp->fcp_hdr_le.s_id[1] = abts->fcp_hdr_le.d_id[1];
+               resp->fcp_hdr_le.s_id[2] = abts->fcp_hdr_le.d_id[2];
+       }
+       resp->exchange_addr_to_abort = abts->exchange_addr_to_abort;
+       if (status == FCP_TMF_CMPL) {
+               resp->fcp_hdr_le.r_ctl = R_CTL_BASIC_LINK_SERV | R_CTL_B_ACC;
+               resp->payload.ba_acct.seq_id_valid = SEQ_ID_INVALID;
+               resp->payload.ba_acct.low_seq_cnt = 0x0000;
+               resp->payload.ba_acct.high_seq_cnt = 0xFFFF;
+               resp->payload.ba_acct.ox_id = abts->fcp_hdr_le.ox_id;
+               resp->payload.ba_acct.rx_id = abts->fcp_hdr_le.rx_id;
+       } else {
+               resp->fcp_hdr_le.r_ctl = R_CTL_BASIC_LINK_SERV | R_CTL_B_RJT;
+               resp->payload.ba_rjt.reason_code =
+                       BA_RJT_REASON_CODE_UNABLE_TO_PERFORM;
+               /* Other bytes are zero */
+       }
+
+       ha->tgt.qla_tgt->abts_resp_expected++;
+
+       qla2x00_start_iocbs(vha, vha->req);
+}
+
+/*
+ * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire
+ */
+static void qlt_24xx_retry_term_exchange(struct scsi_qla_host *vha,
+       struct abts_resp_from_24xx_fw *entry)
+{
+       struct ctio7_to_24xx *ctio;
+
+       ql_dbg(ql_dbg_tgt, vha, 0xe007,
+           "Sending retry TERM EXCH CTIO7 (ha=%p)\n", vha->hw);
+       /* Send marker if required */
+       if (qlt_issue_marker(vha, 1) != QLA_SUCCESS)
+               return;
+
+       ctio = (struct ctio7_to_24xx *)qla2x00_alloc_iocbs(vha, NULL);
+       if (ctio == NULL) {
+               ql_dbg(ql_dbg_tgt, vha, 0xe04b,
+                   "qla_target(%d): %s failed: unable to allocate "
+                   "request packet\n", vha->vp_idx, __func__);
+               return;
+       }
+
+       /*
+        * We've got on entrance firmware's response on by us generated
+        * ABTS response. So, in it ID fields are reversed.
+        */
+
+       ctio->entry_type = CTIO_TYPE7;
+       ctio->entry_count = 1;
+       ctio->nport_handle = entry->nport_handle;
+       ctio->handle = QLA_TGT_SKIP_HANDLE |    CTIO_COMPLETION_HANDLE_MARK;
+       ctio->timeout = __constant_cpu_to_le16(QLA_TGT_TIMEOUT);
+       ctio->vp_index = vha->vp_idx;
+       ctio->initiator_id[0] = entry->fcp_hdr_le.d_id[0];
+       ctio->initiator_id[1] = entry->fcp_hdr_le.d_id[1];
+       ctio->initiator_id[2] = entry->fcp_hdr_le.d_id[2];
+       ctio->exchange_addr = entry->exchange_addr_to_abort;
+       ctio->u.status1.flags =
+           __constant_cpu_to_le16(CTIO7_FLAGS_STATUS_MODE_1 |
+               CTIO7_FLAGS_TERMINATE);
+       ctio->u.status1.ox_id = entry->fcp_hdr_le.ox_id;
+
+       qla2x00_start_iocbs(vha, vha->req);
+
+       qlt_24xx_send_abts_resp(vha, (struct abts_recv_from_24xx *)entry,
+           FCP_TMF_CMPL, true);
+}
+
+/* ha->hardware_lock supposed to be held on entry */
+static int __qlt_24xx_handle_abts(struct scsi_qla_host *vha,
+       struct abts_recv_from_24xx *abts, struct qla_tgt_sess *sess)
+{
+       struct qla_hw_data *ha = vha->hw;
+       struct qla_tgt_mgmt_cmd *mcmd;
+       int rc;
+
+       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf00f,
+           "qla_target(%d): task abort (tag=%d)\n",
+           vha->vp_idx, abts->exchange_addr_to_abort);
+
+       mcmd = mempool_alloc(qla_tgt_mgmt_cmd_mempool, GFP_ATOMIC);
+       if (mcmd == NULL) {
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf051,
+                   "qla_target(%d): %s: Allocation of ABORT cmd failed",
+                   vha->vp_idx, __func__);
+               return -ENOMEM;
+       }
+       memset(mcmd, 0, sizeof(*mcmd));
+
+       mcmd->sess = sess;
+       memcpy(&mcmd->orig_iocb.abts, abts, sizeof(mcmd->orig_iocb.abts));
+
+       rc = ha->tgt.tgt_ops->handle_tmr(mcmd, 0, TMR_ABORT_TASK,
+           abts->exchange_addr_to_abort);
+       if (rc != 0) {
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf052,
+                   "qla_target(%d):  tgt_ops->handle_tmr()"
+                   " failed: %d", vha->vp_idx, rc);
+               mempool_free(mcmd, qla_tgt_mgmt_cmd_mempool);
+               return -EFAULT;
+       }
+
+       return 0;
+}
+
+/*
+ * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire
+ */
+static void qlt_24xx_handle_abts(struct scsi_qla_host *vha,
+       struct abts_recv_from_24xx *abts)
+{
+       struct qla_hw_data *ha = vha->hw;
+       struct qla_tgt_sess *sess;
+       uint32_t tag = abts->exchange_addr_to_abort;
+       uint8_t s_id[3];
+       int rc;
+
+       if (le32_to_cpu(abts->fcp_hdr_le.parameter) & ABTS_PARAM_ABORT_SEQ) {
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf053,
+                   "qla_target(%d): ABTS: Abort Sequence not "
+                   "supported\n", vha->vp_idx);
+               qlt_24xx_send_abts_resp(vha, abts, FCP_TMF_REJECTED, false);
+               return;
+       }
+
+       if (tag == ATIO_EXCHANGE_ADDRESS_UNKNOWN) {
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf010,
+                   "qla_target(%d): ABTS: Unknown Exchange "
+                   "Address received\n", vha->vp_idx);
+               qlt_24xx_send_abts_resp(vha, abts, FCP_TMF_REJECTED, false);
+               return;
+       }
+
+       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf011,
+           "qla_target(%d): task abort (s_id=%x:%x:%x, "
+           "tag=%d, param=%x)\n", vha->vp_idx, abts->fcp_hdr_le.s_id[2],
+           abts->fcp_hdr_le.s_id[1], abts->fcp_hdr_le.s_id[0], tag,
+           le32_to_cpu(abts->fcp_hdr_le.parameter));
+
+       s_id[0] = abts->fcp_hdr_le.s_id[2];
+       s_id[1] = abts->fcp_hdr_le.s_id[1];
+       s_id[2] = abts->fcp_hdr_le.s_id[0];
+
+       sess = ha->tgt.tgt_ops->find_sess_by_s_id(vha, s_id);
+       if (!sess) {
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf012,
+                   "qla_target(%d): task abort for non-existant session\n",
+                   vha->vp_idx);
+               rc = qlt_sched_sess_work(ha->tgt.qla_tgt,
+                   QLA_TGT_SESS_WORK_ABORT, abts, sizeof(*abts));
+               if (rc != 0) {
+                       qlt_24xx_send_abts_resp(vha, abts, FCP_TMF_REJECTED,
+                           false);
+               }
+               return;
+       }
+
+       rc = __qlt_24xx_handle_abts(vha, abts, sess);
+       if (rc != 0) {
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf054,
+                   "qla_target(%d): __qlt_24xx_handle_abts() failed: %d\n",
+                   vha->vp_idx, rc);
+               qlt_24xx_send_abts_resp(vha, abts, FCP_TMF_REJECTED, false);
+               return;
+       }
+}
+
+/*
+ * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire
+ */
+static void qlt_24xx_send_task_mgmt_ctio(struct scsi_qla_host *ha,
+       struct qla_tgt_mgmt_cmd *mcmd, uint32_t resp_code)
+{
+       struct atio_from_isp *atio = &mcmd->orig_iocb.atio;
+       struct ctio7_to_24xx *ctio;
+
+       ql_dbg(ql_dbg_tgt, ha, 0xe008,
+           "Sending task mgmt CTIO7 (ha=%p, atio=%p, resp_code=%x\n",
+           ha, atio, resp_code);
+
+       /* Send marker if required */
+       if (qlt_issue_marker(ha, 1) != QLA_SUCCESS)
+               return;
+
+       ctio = (struct ctio7_to_24xx *)qla2x00_alloc_iocbs(ha, NULL);
+       if (ctio == NULL) {
+               ql_dbg(ql_dbg_tgt, ha, 0xe04c,
+                   "qla_target(%d): %s failed: unable to allocate "
+                   "request packet\n", ha->vp_idx, __func__);
+               return;
+       }
+
+       ctio->entry_type = CTIO_TYPE7;
+       ctio->entry_count = 1;
+       ctio->handle = QLA_TGT_SKIP_HANDLE | CTIO_COMPLETION_HANDLE_MARK;
+       ctio->nport_handle = mcmd->sess->loop_id;
+       ctio->timeout = __constant_cpu_to_le16(QLA_TGT_TIMEOUT);
+       ctio->vp_index = ha->vp_idx;
+       ctio->initiator_id[0] = atio->u.isp24.fcp_hdr.s_id[2];
+       ctio->initiator_id[1] = atio->u.isp24.fcp_hdr.s_id[1];
+       ctio->initiator_id[2] = atio->u.isp24.fcp_hdr.s_id[0];
+       ctio->exchange_addr = atio->u.isp24.exchange_addr;
+       ctio->u.status1.flags = (atio->u.isp24.attr << 9) |
+           __constant_cpu_to_le16(CTIO7_FLAGS_STATUS_MODE_1 |
+               CTIO7_FLAGS_SEND_STATUS);
+       ctio->u.status1.ox_id = swab16(atio->u.isp24.fcp_hdr.ox_id);
+       ctio->u.status1.scsi_status =
+           __constant_cpu_to_le16(SS_RESPONSE_INFO_LEN_VALID);
+       ctio->u.status1.response_len = __constant_cpu_to_le16(8);
+       ((uint32_t *)ctio->u.status1.sense_data)[0] = cpu_to_be32(resp_code);
+
+       qla2x00_start_iocbs(ha, ha->req);
+}
+
+void qlt_free_mcmd(struct qla_tgt_mgmt_cmd *mcmd)
+{
+       mempool_free(mcmd, qla_tgt_mgmt_cmd_mempool);
+}
+EXPORT_SYMBOL(qlt_free_mcmd);
+
+/* callback from target fabric module code */
+void qlt_xmit_tm_rsp(struct qla_tgt_mgmt_cmd *mcmd)
+{
+       struct scsi_qla_host *vha = mcmd->sess->vha;
+       struct qla_hw_data *ha = vha->hw;
+       unsigned long flags;
+
+       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf013,
+           "TM response mcmd (%p) status %#x state %#x",
+           mcmd, mcmd->fc_tm_rsp, mcmd->flags);
+
+       spin_lock_irqsave(&ha->hardware_lock, flags);
+       if (mcmd->flags == QLA24XX_MGMT_SEND_NACK)
+               qlt_send_notify_ack(vha, &mcmd->orig_iocb.imm_ntfy,
+                   0, 0, 0, 0, 0, 0);
+       else {
+               if (mcmd->se_cmd.se_tmr_req->function == TMR_ABORT_TASK)
+                       qlt_24xx_send_abts_resp(vha, &mcmd->orig_iocb.abts,
+                           mcmd->fc_tm_rsp, false);
+               else
+                       qlt_24xx_send_task_mgmt_ctio(vha, mcmd,
+                           mcmd->fc_tm_rsp);
+       }
+       /*
+        * Make the callback for ->free_mcmd() to queue_work() and invoke
+        * target_put_sess_cmd() to drop cmd_kref to 1.  The final
+        * target_put_sess_cmd() call will be made from TFO->check_stop_free()
+        * -> tcm_qla2xxx_check_stop_free() to release the TMR associated se_cmd
+        * descriptor after TFO->queue_tm_rsp() -> tcm_qla2xxx_queue_tm_rsp() ->
+        * qlt_xmit_tm_rsp() returns here..
+        */
+       ha->tgt.tgt_ops->free_mcmd(mcmd);
+       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+}
+EXPORT_SYMBOL(qlt_xmit_tm_rsp);
+
+/* No locks */
+static int qlt_pci_map_calc_cnt(struct qla_tgt_prm *prm)
+{
+       struct qla_tgt_cmd *cmd = prm->cmd;
+
+       BUG_ON(cmd->sg_cnt == 0);
+
+       prm->sg = (struct scatterlist *)cmd->sg;
+       prm->seg_cnt = pci_map_sg(prm->tgt->ha->pdev, cmd->sg,
+           cmd->sg_cnt, cmd->dma_data_direction);
+       if (unlikely(prm->seg_cnt == 0))
+               goto out_err;
+
+       prm->cmd->sg_mapped = 1;
+
+       /*
+        * If greater than four sg entries then we need to allocate
+        * the continuation entries
+        */
+       if (prm->seg_cnt > prm->tgt->datasegs_per_cmd)
+               prm->req_cnt += DIV_ROUND_UP(prm->seg_cnt -
+                   prm->tgt->datasegs_per_cmd, prm->tgt->datasegs_per_cont);
+
+       ql_dbg(ql_dbg_tgt, prm->cmd->vha, 0xe009, "seg_cnt=%d, req_cnt=%d\n",
+           prm->seg_cnt, prm->req_cnt);
+       return 0;
+
+out_err:
+       ql_dbg(ql_dbg_tgt, prm->cmd->vha, 0xe04d,
+           "qla_target(%d): PCI mapping failed: sg_cnt=%d",
+           0, prm->cmd->sg_cnt);
+       return -1;
+}
+
+static inline void qlt_unmap_sg(struct scsi_qla_host *vha,
+       struct qla_tgt_cmd *cmd)
+{
+       struct qla_hw_data *ha = vha->hw;
+
+       BUG_ON(!cmd->sg_mapped);
+       pci_unmap_sg(ha->pdev, cmd->sg, cmd->sg_cnt, cmd->dma_data_direction);
+       cmd->sg_mapped = 0;
+}
+
+static int qlt_check_reserve_free_req(struct scsi_qla_host *vha,
+       uint32_t req_cnt)
+{
+       struct qla_hw_data *ha = vha->hw;
+       device_reg_t __iomem *reg = ha->iobase;
+       uint32_t cnt;
+
+       if (vha->req->cnt < (req_cnt + 2)) {
+               cnt = (uint16_t)RD_REG_DWORD(&reg->isp24.req_q_out);
+
+               ql_dbg(ql_dbg_tgt, vha, 0xe00a,
+                   "Request ring circled: cnt=%d, vha->->ring_index=%d, "
+                   "vha->req->cnt=%d, req_cnt=%d\n", cnt,
+                   vha->req->ring_index, vha->req->cnt, req_cnt);
+               if  (vha->req->ring_index < cnt)
+                       vha->req->cnt = cnt - vha->req->ring_index;
+               else
+                       vha->req->cnt = vha->req->length -
+                           (vha->req->ring_index - cnt);
+       }
+
+       if (unlikely(vha->req->cnt < (req_cnt + 2))) {
+               ql_dbg(ql_dbg_tgt, vha, 0xe00b,
+                   "qla_target(%d): There is no room in the "
+                   "request ring: vha->req->ring_index=%d, vha->req->cnt=%d, "
+                   "req_cnt=%d\n", vha->vp_idx, vha->req->ring_index,
+                   vha->req->cnt, req_cnt);
+               return -EAGAIN;
+       }
+       vha->req->cnt -= req_cnt;
+
+       return 0;
+}
+
+/*
+ * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire
+ */
+static inline void *qlt_get_req_pkt(struct scsi_qla_host *vha)
+{
+       /* Adjust ring index. */
+       vha->req->ring_index++;
+       if (vha->req->ring_index == vha->req->length) {
+               vha->req->ring_index = 0;
+               vha->req->ring_ptr = vha->req->ring;
+       } else {
+               vha->req->ring_ptr++;
+       }
+       return (cont_entry_t *)vha->req->ring_ptr;
+}
+
+/* ha->hardware_lock supposed to be held on entry */
+static inline uint32_t qlt_make_handle(struct scsi_qla_host *vha)
+{
+       struct qla_hw_data *ha = vha->hw;
+       uint32_t h;
+
+       h = ha->tgt.current_handle;
+       /* always increment cmd handle */
+       do {
+               ++h;
+               if (h > MAX_OUTSTANDING_COMMANDS)
+                       h = 1; /* 0 is QLA_TGT_NULL_HANDLE */
+               if (h == ha->tgt.current_handle) {
+                       ql_dbg(ql_dbg_tgt, vha, 0xe04e,
+                           "qla_target(%d): Ran out of "
+                           "empty cmd slots in ha %p\n", vha->vp_idx, ha);
+                       h = QLA_TGT_NULL_HANDLE;
+                       break;
+               }
+       } while ((h == QLA_TGT_NULL_HANDLE) ||
+           (h == QLA_TGT_SKIP_HANDLE) ||
+           (ha->tgt.cmds[h-1] != NULL));
+
+       if (h != QLA_TGT_NULL_HANDLE)
+               ha->tgt.current_handle = h;
+
+       return h;
+}
+
+/* ha->hardware_lock supposed to be held on entry */
+static int qlt_24xx_build_ctio_pkt(struct qla_tgt_prm *prm,
+       struct scsi_qla_host *vha)
+{
+       uint32_t h;
+       struct ctio7_to_24xx *pkt;
+       struct qla_hw_data *ha = vha->hw;
+       struct atio_from_isp *atio = &prm->cmd->atio;
+
+       pkt = (struct ctio7_to_24xx *)vha->req->ring_ptr;
+       prm->pkt = pkt;
+       memset(pkt, 0, sizeof(*pkt));
+
+       pkt->entry_type = CTIO_TYPE7;
+       pkt->entry_count = (uint8_t)prm->req_cnt;
+       pkt->vp_index = vha->vp_idx;
+
+       h = qlt_make_handle(vha);
+       if (unlikely(h == QLA_TGT_NULL_HANDLE)) {
+               /*
+                * CTIO type 7 from the firmware doesn't provide a way to
+                * know the initiator's LOOP ID, hence we can't find
+                * the session and, so, the command.
+                */
+               return -EAGAIN;
+       } else
+               ha->tgt.cmds[h-1] = prm->cmd;
+
+       pkt->handle = h | CTIO_COMPLETION_HANDLE_MARK;
+       pkt->nport_handle = prm->cmd->loop_id;
+       pkt->timeout = __constant_cpu_to_le16(QLA_TGT_TIMEOUT);
+       pkt->initiator_id[0] = atio->u.isp24.fcp_hdr.s_id[2];
+       pkt->initiator_id[1] = atio->u.isp24.fcp_hdr.s_id[1];
+       pkt->initiator_id[2] = atio->u.isp24.fcp_hdr.s_id[0];
+       pkt->exchange_addr = atio->u.isp24.exchange_addr;
+       pkt->u.status0.flags |= (atio->u.isp24.attr << 9);
+       pkt->u.status0.ox_id = swab16(atio->u.isp24.fcp_hdr.ox_id);
+       pkt->u.status0.relative_offset = cpu_to_le32(prm->cmd->offset);
+
+       ql_dbg(ql_dbg_tgt, vha, 0xe00c,
+           "qla_target(%d): handle(cmd) -> %08x, timeout %d, ox_id %#x\n",
+           vha->vp_idx, pkt->handle, QLA_TGT_TIMEOUT,
+           le16_to_cpu(pkt->u.status0.ox_id));
+       return 0;
+}
+
+/*
+ * ha->hardware_lock supposed to be held on entry. We have already made sure
+ * that there is sufficient amount of request entries to not drop it.
+ */
+static void qlt_load_cont_data_segments(struct qla_tgt_prm *prm,
+       struct scsi_qla_host *vha)
+{
+       int cnt;
+       uint32_t *dword_ptr;
+       int enable_64bit_addressing = prm->tgt->tgt_enable_64bit_addr;
+
+       /* Build continuation packets */
+       while (prm->seg_cnt > 0) {
+               cont_a64_entry_t *cont_pkt64 =
+                       (cont_a64_entry_t *)qlt_get_req_pkt(vha);
+
+               /*
+                * Make sure that from cont_pkt64 none of
+                * 64-bit specific fields used for 32-bit
+                * addressing. Cast to (cont_entry_t *) for
+                * that.
+                */
+
+               memset(cont_pkt64, 0, sizeof(*cont_pkt64));
+
+               cont_pkt64->entry_count = 1;
+               cont_pkt64->sys_define = 0;
+
+               if (enable_64bit_addressing) {
+                       cont_pkt64->entry_type = CONTINUE_A64_TYPE;
+                       dword_ptr =
+                           (uint32_t *)&cont_pkt64->dseg_0_address;
+               } else {
+                       cont_pkt64->entry_type = CONTINUE_TYPE;
+                       dword_ptr =
+                           (uint32_t *)&((cont_entry_t *)
+                               cont_pkt64)->dseg_0_address;
+               }
+
+               /* Load continuation entry data segments */
+               for (cnt = 0;
+                   cnt < prm->tgt->datasegs_per_cont && prm->seg_cnt;
+                   cnt++, prm->seg_cnt--) {
+                       *dword_ptr++ =
+                           cpu_to_le32(pci_dma_lo32
+                               (sg_dma_address(prm->sg)));
+                       if (enable_64bit_addressing) {
+                               *dword_ptr++ =
+                                   cpu_to_le32(pci_dma_hi32
+                                       (sg_dma_address
+                                       (prm->sg)));
+                       }
+                       *dword_ptr++ = cpu_to_le32(sg_dma_len(prm->sg));
+
+                       ql_dbg(ql_dbg_tgt, vha, 0xe00d,
+                           "S/G Segment Cont. phys_addr=%llx:%llx, len=%d\n",
+                           (long long unsigned int)
+                           pci_dma_hi32(sg_dma_address(prm->sg)),
+                           (long long unsigned int)
+                           pci_dma_lo32(sg_dma_address(prm->sg)),
+                           (int)sg_dma_len(prm->sg));
+
+                       prm->sg = sg_next(prm->sg);
+               }
+       }
+}
+
+/*
+ * ha->hardware_lock supposed to be held on entry. We have already made sure
+ * that there is sufficient amount of request entries to not drop it.
+ */
+static void qlt_load_data_segments(struct qla_tgt_prm *prm,
+       struct scsi_qla_host *vha)
+{
+       int cnt;
+       uint32_t *dword_ptr;
+       int enable_64bit_addressing = prm->tgt->tgt_enable_64bit_addr;
+       struct ctio7_to_24xx *pkt24 = (struct ctio7_to_24xx *)prm->pkt;
+
+       ql_dbg(ql_dbg_tgt, vha, 0xe00e,
+           "iocb->scsi_status=%x, iocb->flags=%x\n",
+           le16_to_cpu(pkt24->u.status0.scsi_status),
+           le16_to_cpu(pkt24->u.status0.flags));
+
+       pkt24->u.status0.transfer_length = cpu_to_le32(prm->cmd->bufflen);
+
+       /* Setup packet address segment pointer */
+       dword_ptr = pkt24->u.status0.dseg_0_address;
+
+       /* Set total data segment count */
+       if (prm->seg_cnt)
+               pkt24->dseg_count = cpu_to_le16(prm->seg_cnt);
+
+       if (prm->seg_cnt == 0) {
+               /* No data transfer */
+               *dword_ptr++ = 0;
+               *dword_ptr = 0;
+               return;
+       }
+
+       /* If scatter gather */
+       ql_dbg(ql_dbg_tgt, vha, 0xe00f, "%s", "Building S/G data segments...");
+
+       /* Load command entry data segments */
+       for (cnt = 0;
+           (cnt < prm->tgt->datasegs_per_cmd) && prm->seg_cnt;
+           cnt++, prm->seg_cnt--) {
+               *dword_ptr++ =
+                   cpu_to_le32(pci_dma_lo32(sg_dma_address(prm->sg)));
+               if (enable_64bit_addressing) {
+                       *dword_ptr++ =
+                           cpu_to_le32(pci_dma_hi32(
+                               sg_dma_address(prm->sg)));
+               }
+               *dword_ptr++ = cpu_to_le32(sg_dma_len(prm->sg));
+
+               ql_dbg(ql_dbg_tgt, vha, 0xe010,
+                   "S/G Segment phys_addr=%llx:%llx, len=%d\n",
+                   (long long unsigned int)pci_dma_hi32(sg_dma_address(
+                   prm->sg)),
+                   (long long unsigned int)pci_dma_lo32(sg_dma_address(
+                   prm->sg)),
+                   (int)sg_dma_len(prm->sg));
+
+               prm->sg = sg_next(prm->sg);
+       }
+
+       qlt_load_cont_data_segments(prm, vha);
+}
+
+static inline int qlt_has_data(struct qla_tgt_cmd *cmd)
+{
+       return cmd->bufflen > 0;
+}
+
+/*
+ * Called without ha->hardware_lock held
+ */
+static int qlt_pre_xmit_response(struct qla_tgt_cmd *cmd,
+       struct qla_tgt_prm *prm, int xmit_type, uint8_t scsi_status,
+       uint32_t *full_req_cnt)
+{
+       struct qla_tgt *tgt = cmd->tgt;
+       struct scsi_qla_host *vha = tgt->vha;
+       struct qla_hw_data *ha = vha->hw;
+       struct se_cmd *se_cmd = &cmd->se_cmd;
+
+       if (unlikely(cmd->aborted)) {
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf014,
+                   "qla_target(%d): terminating exchange "
+                   "for aborted cmd=%p (se_cmd=%p, tag=%d)", vha->vp_idx, cmd,
+                   se_cmd, cmd->tag);
+
+               cmd->state = QLA_TGT_STATE_ABORTED;
+
+               qlt_send_term_exchange(vha, cmd, &cmd->atio, 0);
+
+               /* !! At this point cmd could be already freed !! */
+               return QLA_TGT_PRE_XMIT_RESP_CMD_ABORTED;
+       }
+
+       ql_dbg(ql_dbg_tgt, vha, 0xe011, "qla_target(%d): tag=%u\n",
+           vha->vp_idx, cmd->tag);
+
+       prm->cmd = cmd;
+       prm->tgt = tgt;
+       prm->rq_result = scsi_status;
+       prm->sense_buffer = &cmd->sense_buffer[0];
+       prm->sense_buffer_len = TRANSPORT_SENSE_BUFFER;
+       prm->sg = NULL;
+       prm->seg_cnt = -1;
+       prm->req_cnt = 1;
+       prm->add_status_pkt = 0;
+
+       ql_dbg(ql_dbg_tgt, vha, 0xe012, "rq_result=%x, xmit_type=%x\n",
+           prm->rq_result, xmit_type);
+
+       /* Send marker if required */
+       if (qlt_issue_marker(vha, 0) != QLA_SUCCESS)
+               return -EFAULT;
+
+       ql_dbg(ql_dbg_tgt, vha, 0xe013, "CTIO start: vha(%d)\n", vha->vp_idx);
+
+       if ((xmit_type & QLA_TGT_XMIT_DATA) && qlt_has_data(cmd)) {
+               if  (qlt_pci_map_calc_cnt(prm) != 0)
+                       return -EAGAIN;
+       }
+
+       *full_req_cnt = prm->req_cnt;
+
+       if (se_cmd->se_cmd_flags & SCF_UNDERFLOW_BIT) {
+               prm->residual = se_cmd->residual_count;
+               ql_dbg(ql_dbg_tgt, vha, 0xe014,
+                   "Residual underflow: %d (tag %d, "
+                   "op %x, bufflen %d, rq_result %x)\n", prm->residual,
+                   cmd->tag, se_cmd->t_task_cdb ? se_cmd->t_task_cdb[0] : 0,
+                   cmd->bufflen, prm->rq_result);
+               prm->rq_result |= SS_RESIDUAL_UNDER;
+       } else if (se_cmd->se_cmd_flags & SCF_OVERFLOW_BIT) {
+               prm->residual = se_cmd->residual_count;
+               ql_dbg(ql_dbg_tgt, vha, 0xe015,
+                   "Residual overflow: %d (tag %d, "
+                   "op %x, bufflen %d, rq_result %x)\n", prm->residual,
+                   cmd->tag, se_cmd->t_task_cdb ? se_cmd->t_task_cdb[0] : 0,
+                   cmd->bufflen, prm->rq_result);
+               prm->rq_result |= SS_RESIDUAL_OVER;
+       }
+
+       if (xmit_type & QLA_TGT_XMIT_STATUS) {
+               /*
+                * If QLA_TGT_XMIT_DATA is not set, add_status_pkt will be
+                * ignored in *xmit_response() below
+                */
+               if (qlt_has_data(cmd)) {
+                       if (QLA_TGT_SENSE_VALID(prm->sense_buffer) ||
+                           (IS_FWI2_CAPABLE(ha) &&
+                           (prm->rq_result != 0))) {
+                               prm->add_status_pkt = 1;
+                               (*full_req_cnt)++;
+                       }
+               }
+       }
+
+       ql_dbg(ql_dbg_tgt, vha, 0xe016,
+           "req_cnt=%d, full_req_cnt=%d, add_status_pkt=%d\n",
+           prm->req_cnt, *full_req_cnt, prm->add_status_pkt);
+
+       return 0;
+}
+
+static inline int qlt_need_explicit_conf(struct qla_hw_data *ha,
+       struct qla_tgt_cmd *cmd, int sending_sense)
+{
+       if (ha->tgt.enable_class_2)
+               return 0;
+
+       if (sending_sense)
+               return cmd->conf_compl_supported;
+       else
+               return ha->tgt.enable_explicit_conf &&
+                   cmd->conf_compl_supported;
+}
+
+#ifdef CONFIG_QLA_TGT_DEBUG_SRR
+/*
+ *  Original taken from the XFS code
+ */
+static unsigned long qlt_srr_random(void)
+{
+       static int Inited;
+       static unsigned long RandomValue;
+       static DEFINE_SPINLOCK(lock);
+       /* cycles pseudo-randomly through all values between 1 and 2^31 - 2 */
+       register long rv;
+       register long lo;
+       register long hi;
+       unsigned long flags;
+
+       spin_lock_irqsave(&lock, flags);
+       if (!Inited) {
+               RandomValue = jiffies;
+               Inited = 1;
+       }
+       rv = RandomValue;
+       hi = rv / 127773;
+       lo = rv % 127773;
+       rv = 16807 * lo - 2836 * hi;
+       if (rv <= 0)
+               rv += 2147483647;
+       RandomValue = rv;
+       spin_unlock_irqrestore(&lock, flags);
+       return rv;
+}
+
+static void qlt_check_srr_debug(struct qla_tgt_cmd *cmd, int *xmit_type)
+{
+#if 0 /* This is not a real status packets lost, so it won't lead to SRR */
+       if ((*xmit_type & QLA_TGT_XMIT_STATUS) && (qlt_srr_random() % 200)
+           == 50) {
+               *xmit_type &= ~QLA_TGT_XMIT_STATUS;
+               ql_dbg(ql_dbg_tgt_mgt, cmd->vha, 0xf015,
+                   "Dropping cmd %p (tag %d) status", cmd, cmd->tag);
+       }
+#endif
+       /*
+        * It's currently not possible to simulate SRRs for FCP_WRITE without
+        * a physical link layer failure, so don't even try here..
+        */
+       if (cmd->dma_data_direction != DMA_FROM_DEVICE)
+               return;
+
+       if (qlt_has_data(cmd) && (cmd->sg_cnt > 1) &&
+           ((qlt_srr_random() % 100) == 20)) {
+               int i, leave = 0;
+               unsigned int tot_len = 0;
+
+               while (leave == 0)
+                       leave = qlt_srr_random() % cmd->sg_cnt;
+
+               for (i = 0; i < leave; i++)
+                       tot_len += cmd->sg[i].length;
+
+               ql_dbg(ql_dbg_tgt_mgt, cmd->vha, 0xf016,
+                   "Cutting cmd %p (tag %d) buffer"
+                   " tail to len %d, sg_cnt %d (cmd->bufflen %d,"
+                   " cmd->sg_cnt %d)", cmd, cmd->tag, tot_len, leave,
+                   cmd->bufflen, cmd->sg_cnt);
+
+               cmd->bufflen = tot_len;
+               cmd->sg_cnt = leave;
+       }
+
+       if (qlt_has_data(cmd) && ((qlt_srr_random() % 100) == 70)) {
+               unsigned int offset = qlt_srr_random() % cmd->bufflen;
+
+               ql_dbg(ql_dbg_tgt_mgt, cmd->vha, 0xf017,
+                   "Cutting cmd %p (tag %d) buffer head "
+                   "to offset %d (cmd->bufflen %d)", cmd, cmd->tag, offset,
+                   cmd->bufflen);
+               if (offset == 0)
+                       *xmit_type &= ~QLA_TGT_XMIT_DATA;
+               else if (qlt_set_data_offset(cmd, offset)) {
+                       ql_dbg(ql_dbg_tgt_mgt, cmd->vha, 0xf018,
+                           "qlt_set_data_offset() failed (tag %d)", cmd->tag);
+               }
+       }
+}
+#else
+static inline void qlt_check_srr_debug(struct qla_tgt_cmd *cmd, int *xmit_type)
+{}
+#endif
+
+static void qlt_24xx_init_ctio_to_isp(struct ctio7_to_24xx *ctio,
+       struct qla_tgt_prm *prm)
+{
+       prm->sense_buffer_len = min_t(uint32_t, prm->sense_buffer_len,
+           (uint32_t)sizeof(ctio->u.status1.sense_data));
+       ctio->u.status0.flags |=
+           __constant_cpu_to_le16(CTIO7_FLAGS_SEND_STATUS);
+       if (qlt_need_explicit_conf(prm->tgt->ha, prm->cmd, 0)) {
+               ctio->u.status0.flags |= __constant_cpu_to_le16(
+                   CTIO7_FLAGS_EXPLICIT_CONFORM |
+                   CTIO7_FLAGS_CONFORM_REQ);
+       }
+       ctio->u.status0.residual = cpu_to_le32(prm->residual);
+       ctio->u.status0.scsi_status = cpu_to_le16(prm->rq_result);
+       if (QLA_TGT_SENSE_VALID(prm->sense_buffer)) {
+               int i;
+
+               if (qlt_need_explicit_conf(prm->tgt->ha, prm->cmd, 1)) {
+                       if (prm->cmd->se_cmd.scsi_status != 0) {
+                               ql_dbg(ql_dbg_tgt, prm->cmd->vha, 0xe017,
+                                   "Skipping EXPLICIT_CONFORM and "
+                                   "CTIO7_FLAGS_CONFORM_REQ for FCP READ w/ "
+                                   "non GOOD status\n");
+                               goto skip_explict_conf;
+                       }
+                       ctio->u.status1.flags |= __constant_cpu_to_le16(
+                           CTIO7_FLAGS_EXPLICIT_CONFORM |
+                           CTIO7_FLAGS_CONFORM_REQ);
+               }
+skip_explict_conf:
+               ctio->u.status1.flags &=
+                   ~__constant_cpu_to_le16(CTIO7_FLAGS_STATUS_MODE_0);
+               ctio->u.status1.flags |=
+                   __constant_cpu_to_le16(CTIO7_FLAGS_STATUS_MODE_1);
+               ctio->u.status1.scsi_status |=
+                   __constant_cpu_to_le16(SS_SENSE_LEN_VALID);
+               ctio->u.status1.sense_length =
+                   cpu_to_le16(prm->sense_buffer_len);
+               for (i = 0; i < prm->sense_buffer_len/4; i++)
+                       ((uint32_t *)ctio->u.status1.sense_data)[i] =
+                               cpu_to_be32(((uint32_t *)prm->sense_buffer)[i]);
+#if 0
+               if (unlikely((prm->sense_buffer_len % 4) != 0)) {
+                       static int q;
+                       if (q < 10) {
+                               ql_dbg(ql_dbg_tgt, vha, 0xe04f,
+                                   "qla_target(%d): %d bytes of sense "
+                                   "lost", prm->tgt->ha->vp_idx,
+                                   prm->sense_buffer_len % 4);
+                               q++;
+                       }
+               }
+#endif
+       } else {
+               ctio->u.status1.flags &=
+                   ~__constant_cpu_to_le16(CTIO7_FLAGS_STATUS_MODE_0);
+               ctio->u.status1.flags |=
+                   __constant_cpu_to_le16(CTIO7_FLAGS_STATUS_MODE_1);
+               ctio->u.status1.sense_length = 0;
+               memset(ctio->u.status1.sense_data, 0,
+                   sizeof(ctio->u.status1.sense_data));
+       }
+
+       /* Sense with len > 24, is it possible ??? */
+}
+
+/*
+ * Callback to setup response of xmit_type of QLA_TGT_XMIT_DATA and *
+ * QLA_TGT_XMIT_STATUS for >= 24xx silicon
+ */
+int qlt_xmit_response(struct qla_tgt_cmd *cmd, int xmit_type,
+       uint8_t scsi_status)
+{
+       struct scsi_qla_host *vha = cmd->vha;
+       struct qla_hw_data *ha = vha->hw;
+       struct ctio7_to_24xx *pkt;
+       struct qla_tgt_prm prm;
+       uint32_t full_req_cnt = 0;
+       unsigned long flags = 0;
+       int res;
+
+       memset(&prm, 0, sizeof(prm));
+       qlt_check_srr_debug(cmd, &xmit_type);
+
+       ql_dbg(ql_dbg_tgt, cmd->vha, 0xe018,
+           "is_send_status=%d, cmd->bufflen=%d, cmd->sg_cnt=%d, "
+           "cmd->dma_data_direction=%d\n", (xmit_type & QLA_TGT_XMIT_STATUS) ?
+           1 : 0, cmd->bufflen, cmd->sg_cnt, cmd->dma_data_direction);
+
+       res = qlt_pre_xmit_response(cmd, &prm, xmit_type, scsi_status,
+           &full_req_cnt);
+       if (unlikely(res != 0)) {
+               if (res == QLA_TGT_PRE_XMIT_RESP_CMD_ABORTED)
+                       return 0;
+
+               return res;
+       }
+
+       spin_lock_irqsave(&ha->hardware_lock, flags);
+
+       /* Does F/W have an IOCBs for this request */
+       res = qlt_check_reserve_free_req(vha, full_req_cnt);
+       if (unlikely(res))
+               goto out_unmap_unlock;
+
+       res = qlt_24xx_build_ctio_pkt(&prm, vha);
+       if (unlikely(res != 0))
+               goto out_unmap_unlock;
+
+
+       pkt = (struct ctio7_to_24xx *)prm.pkt;
+
+       if (qlt_has_data(cmd) && (xmit_type & QLA_TGT_XMIT_DATA)) {
+               pkt->u.status0.flags |=
+                   __constant_cpu_to_le16(CTIO7_FLAGS_DATA_IN |
+                       CTIO7_FLAGS_STATUS_MODE_0);
+
+               qlt_load_data_segments(&prm, vha);
+
+               if (prm.add_status_pkt == 0) {
+                       if (xmit_type & QLA_TGT_XMIT_STATUS) {
+                               pkt->u.status0.scsi_status =
+                                   cpu_to_le16(prm.rq_result);
+                               pkt->u.status0.residual =
+                                   cpu_to_le32(prm.residual);
+                               pkt->u.status0.flags |= __constant_cpu_to_le16(
+                                   CTIO7_FLAGS_SEND_STATUS);
+                               if (qlt_need_explicit_conf(ha, cmd, 0)) {
+                                       pkt->u.status0.flags |=
+                                           __constant_cpu_to_le16(
+                                               CTIO7_FLAGS_EXPLICIT_CONFORM |
+                                               CTIO7_FLAGS_CONFORM_REQ);
+                               }
+                       }
+
+               } else {
+                       /*
+                        * We have already made sure that there is sufficient
+                        * amount of request entries to not drop HW lock in
+                        * req_pkt().
+                        */
+                       struct ctio7_to_24xx *ctio =
+                               (struct ctio7_to_24xx *)qlt_get_req_pkt(vha);
+
+                       ql_dbg(ql_dbg_tgt, vha, 0xe019,
+                           "Building additional status packet\n");
+
+                       memcpy(ctio, pkt, sizeof(*ctio));
+                       ctio->entry_count = 1;
+                       ctio->dseg_count = 0;
+                       ctio->u.status1.flags &= ~__constant_cpu_to_le16(
+                           CTIO7_FLAGS_DATA_IN);
+
+                       /* Real finish is ctio_m1's finish */
+                       pkt->handle |= CTIO_INTERMEDIATE_HANDLE_MARK;
+                       pkt->u.status0.flags |= __constant_cpu_to_le16(
+                           CTIO7_FLAGS_DONT_RET_CTIO);
+                       qlt_24xx_init_ctio_to_isp((struct ctio7_to_24xx *)ctio,
+                           &prm);
+                       pr_debug("Status CTIO7: %p\n", ctio);
+               }
+       } else
+               qlt_24xx_init_ctio_to_isp(pkt, &prm);
+
+
+       cmd->state = QLA_TGT_STATE_PROCESSED; /* Mid-level is done processing */
+
+       ql_dbg(ql_dbg_tgt, vha, 0xe01a,
+           "Xmitting CTIO7 response pkt for 24xx: %p scsi_status: 0x%02x\n",
+           pkt, scsi_status);
+
+       qla2x00_start_iocbs(vha, vha->req);
+       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+       return 0;
+
+out_unmap_unlock:
+       if (cmd->sg_mapped)
+               qlt_unmap_sg(vha, cmd);
+       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+       return res;
+}
+EXPORT_SYMBOL(qlt_xmit_response);
+
+int qlt_rdy_to_xfer(struct qla_tgt_cmd *cmd)
+{
+       struct ctio7_to_24xx *pkt;
+       struct scsi_qla_host *vha = cmd->vha;
+       struct qla_hw_data *ha = vha->hw;
+       struct qla_tgt *tgt = cmd->tgt;
+       struct qla_tgt_prm prm;
+       unsigned long flags;
+       int res = 0;
+
+       memset(&prm, 0, sizeof(prm));
+       prm.cmd = cmd;
+       prm.tgt = tgt;
+       prm.sg = NULL;
+       prm.req_cnt = 1;
+
+       /* Send marker if required */
+       if (qlt_issue_marker(vha, 0) != QLA_SUCCESS)
+               return -EIO;
+
+       ql_dbg(ql_dbg_tgt, vha, 0xe01b, "CTIO_start: vha(%d)",
+           (int)vha->vp_idx);
+
+       /* Calculate number of entries and segments required */
+       if (qlt_pci_map_calc_cnt(&prm) != 0)
+               return -EAGAIN;
+
+       spin_lock_irqsave(&ha->hardware_lock, flags);
+
+       /* Does F/W have an IOCBs for this request */
+       res = qlt_check_reserve_free_req(vha, prm.req_cnt);
+       if (res != 0)
+               goto out_unlock_free_unmap;
+
+       res = qlt_24xx_build_ctio_pkt(&prm, vha);
+       if (unlikely(res != 0))
+               goto out_unlock_free_unmap;
+       pkt = (struct ctio7_to_24xx *)prm.pkt;
+       pkt->u.status0.flags |= __constant_cpu_to_le16(CTIO7_FLAGS_DATA_OUT |
+           CTIO7_FLAGS_STATUS_MODE_0);
+       qlt_load_data_segments(&prm, vha);
+
+       cmd->state = QLA_TGT_STATE_NEED_DATA;
+
+       qla2x00_start_iocbs(vha, vha->req);
+       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+       return res;
+
+out_unlock_free_unmap:
+       if (cmd->sg_mapped)
+               qlt_unmap_sg(vha, cmd);
+       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+       return res;
+}
+EXPORT_SYMBOL(qlt_rdy_to_xfer);
+
+/* If hardware_lock held on entry, might drop it, then reaquire */
+/* This function sends the appropriate CTIO to ISP 2xxx or 24xx */
+static int __qlt_send_term_exchange(struct scsi_qla_host *vha,
+       struct qla_tgt_cmd *cmd,
+       struct atio_from_isp *atio)
+{
+       struct ctio7_to_24xx *ctio24;
+       struct qla_hw_data *ha = vha->hw;
+       request_t *pkt;
+       int ret = 0;
+
+       ql_dbg(ql_dbg_tgt, vha, 0xe01c, "Sending TERM EXCH CTIO (ha=%p)\n", ha);
+
+       pkt = (request_t *)qla2x00_alloc_iocbs(vha, NULL);
+       if (pkt == NULL) {
+               ql_dbg(ql_dbg_tgt, vha, 0xe050,
+                   "qla_target(%d): %s failed: unable to allocate "
+                   "request packet\n", vha->vp_idx, __func__);
+               return -ENOMEM;
+       }
+
+       if (cmd != NULL) {
+               if (cmd->state < QLA_TGT_STATE_PROCESSED) {
+                       ql_dbg(ql_dbg_tgt, vha, 0xe051,
+                           "qla_target(%d): Terminating cmd %p with "
+                           "incorrect state %d\n", vha->vp_idx, cmd,
+                           cmd->state);
+               } else
+                       ret = 1;
+       }
+
+       pkt->entry_count = 1;
+       pkt->handle = QLA_TGT_SKIP_HANDLE | CTIO_COMPLETION_HANDLE_MARK;
+
+       ctio24 = (struct ctio7_to_24xx *)pkt;
+       ctio24->entry_type = CTIO_TYPE7;
+       ctio24->nport_handle = cmd ? cmd->loop_id : CTIO7_NHANDLE_UNRECOGNIZED;
+       ctio24->timeout = __constant_cpu_to_le16(QLA_TGT_TIMEOUT);
+       ctio24->vp_index = vha->vp_idx;
+       ctio24->initiator_id[0] = atio->u.isp24.fcp_hdr.s_id[2];
+       ctio24->initiator_id[1] = atio->u.isp24.fcp_hdr.s_id[1];
+       ctio24->initiator_id[2] = atio->u.isp24.fcp_hdr.s_id[0];
+       ctio24->exchange_addr = atio->u.isp24.exchange_addr;
+       ctio24->u.status1.flags = (atio->u.isp24.attr << 9) |
+           __constant_cpu_to_le16(CTIO7_FLAGS_STATUS_MODE_1 |
+               CTIO7_FLAGS_TERMINATE);
+       ctio24->u.status1.ox_id = swab16(atio->u.isp24.fcp_hdr.ox_id);
+
+       /* Most likely, it isn't needed */
+       ctio24->u.status1.residual = get_unaligned((uint32_t *)
+           &atio->u.isp24.fcp_cmnd.add_cdb[
+           atio->u.isp24.fcp_cmnd.add_cdb_len]);
+       if (ctio24->u.status1.residual != 0)
+               ctio24->u.status1.scsi_status |= SS_RESIDUAL_UNDER;
+
+       qla2x00_start_iocbs(vha, vha->req);
+       return ret;
+}
+
+static void qlt_send_term_exchange(struct scsi_qla_host *vha,
+       struct qla_tgt_cmd *cmd, struct atio_from_isp *atio, int ha_locked)
+{
+       unsigned long flags;
+       int rc;
+
+       if (qlt_issue_marker(vha, ha_locked) < 0)
+               return;
+
+       if (ha_locked) {
+               rc = __qlt_send_term_exchange(vha, cmd, atio);
+               goto done;
+       }
+       spin_lock_irqsave(&vha->hw->hardware_lock, flags);
+       rc = __qlt_send_term_exchange(vha, cmd, atio);
+       spin_unlock_irqrestore(&vha->hw->hardware_lock, flags);
+done:
+       if (rc == 1) {
+               if (!ha_locked && !in_interrupt())
+                       msleep(250); /* just in case */
+
+               vha->hw->tgt.tgt_ops->free_cmd(cmd);
+       }
+}
+
+void qlt_free_cmd(struct qla_tgt_cmd *cmd)
+{
+       BUG_ON(cmd->sg_mapped);
+
+       if (unlikely(cmd->free_sg))
+               kfree(cmd->sg);
+       kmem_cache_free(qla_tgt_cmd_cachep, cmd);
+}
+EXPORT_SYMBOL(qlt_free_cmd);
+
+/* ha->hardware_lock supposed to be held on entry */
+static int qlt_prepare_srr_ctio(struct scsi_qla_host *vha,
+       struct qla_tgt_cmd *cmd, void *ctio)
+{
+       struct qla_tgt_srr_ctio *sc;
+       struct qla_hw_data *ha = vha->hw;
+       struct qla_tgt *tgt = ha->tgt.qla_tgt;
+       struct qla_tgt_srr_imm *imm;
+
+       tgt->ctio_srr_id++;
+
+       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf019,
+           "qla_target(%d): CTIO with SRR status received\n", vha->vp_idx);
+
+       if (!ctio) {
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf055,
+                   "qla_target(%d): SRR CTIO, but ctio is NULL\n",
+                   vha->vp_idx);
+               return -EINVAL;
+       }
+
+       sc = kzalloc(sizeof(*sc), GFP_ATOMIC);
+       if (sc != NULL) {
+               sc->cmd = cmd;
+               /* IRQ is already OFF */
+               spin_lock(&tgt->srr_lock);
+               sc->srr_id = tgt->ctio_srr_id;
+               list_add_tail(&sc->srr_list_entry,
+                   &tgt->srr_ctio_list);
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf01a,
+                   "CTIO SRR %p added (id %d)\n", sc, sc->srr_id);
+               if (tgt->imm_srr_id == tgt->ctio_srr_id) {
+                       int found = 0;
+                       list_for_each_entry(imm, &tgt->srr_imm_list,
+                           srr_list_entry) {
+                               if (imm->srr_id == sc->srr_id) {
+                                       found = 1;
+                                       break;
+                               }
+                       }
+                       if (found) {
+                               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf01b,
+                                   "Scheduling srr work\n");
+                               schedule_work(&tgt->srr_work);
+                       } else {
+                               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf056,
+                                   "qla_target(%d): imm_srr_id "
+                                   "== ctio_srr_id (%d), but there is no "
+                                   "corresponding SRR IMM, deleting CTIO "
+                                   "SRR %p\n", vha->vp_idx,
+                                   tgt->ctio_srr_id, sc);
+                               list_del(&sc->srr_list_entry);
+                               spin_unlock(&tgt->srr_lock);
+
+                               kfree(sc);
+                               return -EINVAL;
+                       }
+               }
+               spin_unlock(&tgt->srr_lock);
+       } else {
+               struct qla_tgt_srr_imm *ti;
+
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf057,
+                   "qla_target(%d): Unable to allocate SRR CTIO entry\n",
+                   vha->vp_idx);
+               spin_lock(&tgt->srr_lock);
+               list_for_each_entry_safe(imm, ti, &tgt->srr_imm_list,
+                   srr_list_entry) {
+                       if (imm->srr_id == tgt->ctio_srr_id) {
+                               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf01c,
+                                   "IMM SRR %p deleted (id %d)\n",
+                                   imm, imm->srr_id);
+                               list_del(&imm->srr_list_entry);
+                               qlt_reject_free_srr_imm(vha, imm, 1);
+                       }
+               }
+               spin_unlock(&tgt->srr_lock);
+
+               return -ENOMEM;
+       }
+
+       return 0;
+}
+
+/*
+ * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire
+ */
+static int qlt_term_ctio_exchange(struct scsi_qla_host *vha, void *ctio,
+       struct qla_tgt_cmd *cmd, uint32_t status)
+{
+       int term = 0;
+
+       if (ctio != NULL) {
+               struct ctio7_from_24xx *c = (struct ctio7_from_24xx *)ctio;
+               term = !(c->flags &
+                   __constant_cpu_to_le16(OF_TERM_EXCH));
+       } else
+               term = 1;
+
+       if (term)
+               qlt_send_term_exchange(vha, cmd, &cmd->atio, 1);
+
+       return term;
+}
+
+/* ha->hardware_lock supposed to be held on entry */
+static inline struct qla_tgt_cmd *qlt_get_cmd(struct scsi_qla_host *vha,
+       uint32_t handle)
+{
+       struct qla_hw_data *ha = vha->hw;
+
+       handle--;
+       if (ha->tgt.cmds[handle] != NULL) {
+               struct qla_tgt_cmd *cmd = ha->tgt.cmds[handle];
+               ha->tgt.cmds[handle] = NULL;
+               return cmd;
+       } else
+               return NULL;
+}
+
+/* ha->hardware_lock supposed to be held on entry */
+static struct qla_tgt_cmd *qlt_ctio_to_cmd(struct scsi_qla_host *vha,
+       uint32_t handle, void *ctio)
+{
+       struct qla_tgt_cmd *cmd = NULL;
+
+       /* Clear out internal marks */
+       handle &= ~(CTIO_COMPLETION_HANDLE_MARK |
+           CTIO_INTERMEDIATE_HANDLE_MARK);
+
+       if (handle != QLA_TGT_NULL_HANDLE) {
+               if (unlikely(handle == QLA_TGT_SKIP_HANDLE)) {
+                       ql_dbg(ql_dbg_tgt, vha, 0xe01d, "%s",
+                           "SKIP_HANDLE CTIO\n");
+                       return NULL;
+               }
+               /* handle-1 is actually used */
+               if (unlikely(handle > MAX_OUTSTANDING_COMMANDS)) {
+                       ql_dbg(ql_dbg_tgt, vha, 0xe052,
+                           "qla_target(%d): Wrong handle %x received\n",
+                           vha->vp_idx, handle);
+                       return NULL;
+               }
+               cmd = qlt_get_cmd(vha, handle);
+               if (unlikely(cmd == NULL)) {
+                       ql_dbg(ql_dbg_tgt, vha, 0xe053,
+                           "qla_target(%d): Suspicious: unable to "
+                           "find the command with handle %x\n", vha->vp_idx,
+                           handle);
+                       return NULL;
+               }
+       } else if (ctio != NULL) {
+               /* We can't get loop ID from CTIO7 */
+               ql_dbg(ql_dbg_tgt, vha, 0xe054,
+                   "qla_target(%d): Wrong CTIO received: QLA24xx doesn't "
+                   "support NULL handles\n", vha->vp_idx);
+               return NULL;
+       }
+
+       return cmd;
+}
+
+/*
+ * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire
+ */
+static void qlt_do_ctio_completion(struct scsi_qla_host *vha, uint32_t handle,
+       uint32_t status, void *ctio)
+{
+       struct qla_hw_data *ha = vha->hw;
+       struct se_cmd *se_cmd;
+       struct target_core_fabric_ops *tfo;
+       struct qla_tgt_cmd *cmd;
+
+       ql_dbg(ql_dbg_tgt, vha, 0xe01e,
+           "qla_target(%d): handle(ctio %p status %#x) <- %08x\n",
+           vha->vp_idx, ctio, status, handle);
+
+       if (handle & CTIO_INTERMEDIATE_HANDLE_MARK) {
+               /* That could happen only in case of an error/reset/abort */
+               if (status != CTIO_SUCCESS) {
+                       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf01d,
+                           "Intermediate CTIO received"
+                           " (status %x)\n", status);
+               }
+               return;
+       }
+
+       cmd = qlt_ctio_to_cmd(vha, handle, ctio);
+       if (cmd == NULL) {
+               if (status != CTIO_SUCCESS)
+                       qlt_term_ctio_exchange(vha, ctio, NULL, status);
+               return;
+       }
+       se_cmd = &cmd->se_cmd;
+       tfo = se_cmd->se_tfo;
+
+       if (cmd->sg_mapped)
+               qlt_unmap_sg(vha, cmd);
+
+       if (unlikely(status != CTIO_SUCCESS)) {
+               switch (status & 0xFFFF) {
+               case CTIO_LIP_RESET:
+               case CTIO_TARGET_RESET:
+               case CTIO_ABORTED:
+               case CTIO_TIMEOUT:
+               case CTIO_INVALID_RX_ID:
+                       /* They are OK */
+                       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf058,
+                           "qla_target(%d): CTIO with "
+                           "status %#x received, state %x, se_cmd %p, "
+                           "(LIP_RESET=e, ABORTED=2, TARGET_RESET=17, "
+                           "TIMEOUT=b, INVALID_RX_ID=8)\n", vha->vp_idx,
+                           status, cmd->state, se_cmd);
+                       break;
+
+               case CTIO_PORT_LOGGED_OUT:
+               case CTIO_PORT_UNAVAILABLE:
+                       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf059,
+                           "qla_target(%d): CTIO with PORT LOGGED "
+                           "OUT (29) or PORT UNAVAILABLE (28) status %x "
+                           "received (state %x, se_cmd %p)\n", vha->vp_idx,
+                           status, cmd->state, se_cmd);
+                       break;
+
+               case CTIO_SRR_RECEIVED:
+                       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf05a,
+                           "qla_target(%d): CTIO with SRR_RECEIVED"
+                           " status %x received (state %x, se_cmd %p)\n",
+                           vha->vp_idx, status, cmd->state, se_cmd);
+                       if (qlt_prepare_srr_ctio(vha, cmd, ctio) != 0)
+                               break;
+                       else
+                               return;
+
+               default:
+                       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf05b,
+                           "qla_target(%d): CTIO with error status "
+                           "0x%x received (state %x, se_cmd %p\n",
+                           vha->vp_idx, status, cmd->state, se_cmd);
+                       break;
+               }
+
+               if (cmd->state != QLA_TGT_STATE_NEED_DATA)
+                       if (qlt_term_ctio_exchange(vha, ctio, cmd, status))
+                               return;
+       }
+
+       if (cmd->state == QLA_TGT_STATE_PROCESSED) {
+               ql_dbg(ql_dbg_tgt, vha, 0xe01f, "Command %p finished\n", cmd);
+       } else if (cmd->state == QLA_TGT_STATE_NEED_DATA) {
+               int rx_status = 0;
+
+               cmd->state = QLA_TGT_STATE_DATA_IN;
+
+               if (unlikely(status != CTIO_SUCCESS))
+                       rx_status = -EIO;
+               else
+                       cmd->write_data_transferred = 1;
+
+               ql_dbg(ql_dbg_tgt, vha, 0xe020,
+                   "Data received, context %x, rx_status %d\n",
+                   0x0, rx_status);
+
+               ha->tgt.tgt_ops->handle_data(cmd);
+               return;
+       } else if (cmd->state == QLA_TGT_STATE_ABORTED) {
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf01e,
+                   "Aborted command %p (tag %d) finished\n", cmd, cmd->tag);
+       } else {
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf05c,
+                   "qla_target(%d): A command in state (%d) should "
+                   "not return a CTIO complete\n", vha->vp_idx, cmd->state);
+       }
+
+       if (unlikely(status != CTIO_SUCCESS)) {
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf01f, "Finishing failed CTIO\n");
+               dump_stack();
+       }
+
+       ha->tgt.tgt_ops->free_cmd(cmd);
+}
+
+/* ha->hardware_lock supposed to be held on entry */
+/* called via callback from qla2xxx */
+void qlt_ctio_completion(struct scsi_qla_host *vha, uint32_t handle)
+{
+       struct qla_hw_data *ha = vha->hw;
+       struct qla_tgt *tgt = ha->tgt.qla_tgt;
+
+       if (likely(tgt == NULL)) {
+               ql_dbg(ql_dbg_tgt, vha, 0xe021,
+                   "CTIO, but target mode not enabled"
+                   " (ha %d %p handle %#x)", vha->vp_idx, ha, handle);
+               return;
+       }
+
+       tgt->irq_cmd_count++;
+       qlt_do_ctio_completion(vha, handle, CTIO_SUCCESS, NULL);
+       tgt->irq_cmd_count--;
+}
+
+static inline int qlt_get_fcp_task_attr(struct scsi_qla_host *vha,
+       uint8_t task_codes)
+{
+       int fcp_task_attr;
+
+       switch (task_codes) {
+       case ATIO_SIMPLE_QUEUE:
+               fcp_task_attr = MSG_SIMPLE_TAG;
+               break;
+       case ATIO_HEAD_OF_QUEUE:
+               fcp_task_attr = MSG_HEAD_TAG;
+               break;
+       case ATIO_ORDERED_QUEUE:
+               fcp_task_attr = MSG_ORDERED_TAG;
+               break;
+       case ATIO_ACA_QUEUE:
+               fcp_task_attr = MSG_ACA_TAG;
+               break;
+       case ATIO_UNTAGGED:
+               fcp_task_attr = MSG_SIMPLE_TAG;
+               break;
+       default:
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf05d,
+                   "qla_target: unknown task code %x, use ORDERED instead\n",
+                   task_codes);
+               fcp_task_attr = MSG_ORDERED_TAG;
+               break;
+       }
+
+       return fcp_task_attr;
+}
+
+static struct qla_tgt_sess *qlt_make_local_sess(struct scsi_qla_host *,
+                                       uint8_t *);
+/*
+ * Process context for I/O path into tcm_qla2xxx code
+ */
+static void qlt_do_work(struct work_struct *work)
+{
+       struct qla_tgt_cmd *cmd = container_of(work, struct qla_tgt_cmd, work);
+       scsi_qla_host_t *vha = cmd->vha;
+       struct qla_hw_data *ha = vha->hw;
+       struct qla_tgt *tgt = ha->tgt.qla_tgt;
+       struct qla_tgt_sess *sess = NULL;
+       struct atio_from_isp *atio = &cmd->atio;
+       unsigned char *cdb;
+       unsigned long flags;
+       uint32_t data_length;
+       int ret, fcp_task_attr, data_dir, bidi = 0;
+
+       if (tgt->tgt_stop)
+               goto out_term;
+
+       spin_lock_irqsave(&ha->hardware_lock, flags);
+       sess = ha->tgt.tgt_ops->find_sess_by_s_id(vha,
+           atio->u.isp24.fcp_hdr.s_id);
+       if (sess) {
+               if (unlikely(sess->tearing_down)) {
+                       sess = NULL;
+                       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+                       goto out_term;
+               } else {
+                       /*
+                        * Do the extra kref_get() before dropping
+                        * qla_hw_data->hardware_lock.
+                        */
+                       kref_get(&sess->se_sess->sess_kref);
+               }
+       }
+       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+       if (unlikely(!sess)) {
+               uint8_t *s_id = atio->u.isp24.fcp_hdr.s_id;
+
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf022,
+                       "qla_target(%d): Unable to find wwn login"
+                       " (s_id %x:%x:%x), trying to create it manually\n",
+                       vha->vp_idx, s_id[0], s_id[1], s_id[2]);
+
+               if (atio->u.raw.entry_count > 1) {
+                       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf023,
+                               "Dropping multy entry cmd %p\n", cmd);
+                       goto out_term;
+               }
+
+               mutex_lock(&ha->tgt.tgt_mutex);
+               sess = qlt_make_local_sess(vha, s_id);
+               /* sess has an extra creation ref. */
+               mutex_unlock(&ha->tgt.tgt_mutex);
+
+               if (!sess)
+                       goto out_term;
+       }
+
+       cmd->sess = sess;
+       cmd->loop_id = sess->loop_id;
+       cmd->conf_compl_supported = sess->conf_compl_supported;
+
+       cdb = &atio->u.isp24.fcp_cmnd.cdb[0];
+       cmd->tag = atio->u.isp24.exchange_addr;
+       cmd->unpacked_lun = scsilun_to_int(
+           (struct scsi_lun *)&atio->u.isp24.fcp_cmnd.lun);
+
+       if (atio->u.isp24.fcp_cmnd.rddata &&
+           atio->u.isp24.fcp_cmnd.wrdata) {
+               bidi = 1;
+               data_dir = DMA_TO_DEVICE;
+       } else if (atio->u.isp24.fcp_cmnd.rddata)
+               data_dir = DMA_FROM_DEVICE;
+       else if (atio->u.isp24.fcp_cmnd.wrdata)
+               data_dir = DMA_TO_DEVICE;
+       else
+               data_dir = DMA_NONE;
+
+       fcp_task_attr = qlt_get_fcp_task_attr(vha,
+           atio->u.isp24.fcp_cmnd.task_attr);
+       data_length = be32_to_cpu(get_unaligned((uint32_t *)
+           &atio->u.isp24.fcp_cmnd.add_cdb[
+           atio->u.isp24.fcp_cmnd.add_cdb_len]));
+
+       ql_dbg(ql_dbg_tgt, vha, 0xe022,
+           "qla_target: START qla command: %p lun: 0x%04x (tag %d)\n",
+           cmd, cmd->unpacked_lun, cmd->tag);
+
+       ret = vha->hw->tgt.tgt_ops->handle_cmd(vha, cmd, cdb, data_length,
+           fcp_task_attr, data_dir, bidi);
+       if (ret != 0)
+               goto out_term;
+       /*
+        * Drop extra session reference from qla_tgt_handle_cmd_for_atio*(
+        */
+       ha->tgt.tgt_ops->put_sess(sess);
+       return;
+
+out_term:
+       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf020, "Terminating work cmd %p", cmd);
+       /*
+        * cmd has not sent to target yet, so pass NULL as the second argument
+        */
+       spin_lock_irqsave(&ha->hardware_lock, flags);
+       qlt_send_term_exchange(vha, NULL, &cmd->atio, 1);
+       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+       if (sess)
+               ha->tgt.tgt_ops->put_sess(sess);
+}
+
+/* ha->hardware_lock supposed to be held on entry */
+static int qlt_handle_cmd_for_atio(struct scsi_qla_host *vha,
+       struct atio_from_isp *atio)
+{
+       struct qla_hw_data *ha = vha->hw;
+       struct qla_tgt *tgt = ha->tgt.qla_tgt;
+       struct qla_tgt_cmd *cmd;
+
+       if (unlikely(tgt->tgt_stop)) {
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf021,
+                   "New command while device %p is shutting down\n", tgt);
+               return -EFAULT;
+       }
+
+       cmd = kmem_cache_zalloc(qla_tgt_cmd_cachep, GFP_ATOMIC);
+       if (!cmd) {
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf05e,
+                   "qla_target(%d): Allocation of cmd failed\n", vha->vp_idx);
+               return -ENOMEM;
+       }
+
+       INIT_LIST_HEAD(&cmd->cmd_list);
+
+       memcpy(&cmd->atio, atio, sizeof(*atio));
+       cmd->state = QLA_TGT_STATE_NEW;
+       cmd->tgt = ha->tgt.qla_tgt;
+       cmd->vha = vha;
+
+       INIT_WORK(&cmd->work, qlt_do_work);
+       queue_work(qla_tgt_wq, &cmd->work);
+       return 0;
+
+}
+
+/* ha->hardware_lock supposed to be held on entry */
+static int qlt_issue_task_mgmt(struct qla_tgt_sess *sess, uint32_t lun,
+       int fn, void *iocb, int flags)
+{
+       struct scsi_qla_host *vha = sess->vha;
+       struct qla_hw_data *ha = vha->hw;
+       struct qla_tgt_mgmt_cmd *mcmd;
+       int res;
+       uint8_t tmr_func;
+
+       mcmd = mempool_alloc(qla_tgt_mgmt_cmd_mempool, GFP_ATOMIC);
+       if (!mcmd) {
+               ql_dbg(ql_dbg_tgt_tmr, vha, 0x10009,
+                   "qla_target(%d): Allocation of management "
+                   "command failed, some commands and their data could "
+                   "leak\n", vha->vp_idx);
+               return -ENOMEM;
+       }
+       memset(mcmd, 0, sizeof(*mcmd));
+       mcmd->sess = sess;
+
+       if (iocb) {
+               memcpy(&mcmd->orig_iocb.imm_ntfy, iocb,
+                   sizeof(mcmd->orig_iocb.imm_ntfy));
+       }
+       mcmd->tmr_func = fn;
+       mcmd->flags = flags;
+
+       switch (fn) {
+       case QLA_TGT_CLEAR_ACA:
+               ql_dbg(ql_dbg_tgt_tmr, vha, 0x10000,
+                   "qla_target(%d): CLEAR_ACA received\n", sess->vha->vp_idx);
+               tmr_func = TMR_CLEAR_ACA;
+               break;
+
+       case QLA_TGT_TARGET_RESET:
+               ql_dbg(ql_dbg_tgt_tmr, vha, 0x10001,
+                   "qla_target(%d): TARGET_RESET received\n",
+                   sess->vha->vp_idx);
+               tmr_func = TMR_TARGET_WARM_RESET;
+               break;
+
+       case QLA_TGT_LUN_RESET:
+               ql_dbg(ql_dbg_tgt_tmr, vha, 0x10002,
+                   "qla_target(%d): LUN_RESET received\n", sess->vha->vp_idx);
+               tmr_func = TMR_LUN_RESET;
+               break;
+
+       case QLA_TGT_CLEAR_TS:
+               ql_dbg(ql_dbg_tgt_tmr, vha, 0x10003,
+                   "qla_target(%d): CLEAR_TS received\n", sess->vha->vp_idx);
+               tmr_func = TMR_CLEAR_TASK_SET;
+               break;
+
+       case QLA_TGT_ABORT_TS:
+               ql_dbg(ql_dbg_tgt_tmr, vha, 0x10004,
+                   "qla_target(%d): ABORT_TS received\n", sess->vha->vp_idx);
+               tmr_func = TMR_ABORT_TASK_SET;
+               break;
+#if 0
+       case QLA_TGT_ABORT_ALL:
+               ql_dbg(ql_dbg_tgt_tmr, vha, 0x10005,
+                   "qla_target(%d): Doing ABORT_ALL_TASKS\n",
+                   sess->vha->vp_idx);
+               tmr_func = 0;
+               break;
+
+       case QLA_TGT_ABORT_ALL_SESS:
+               ql_dbg(ql_dbg_tgt_tmr, vha, 0x10006,
+                   "qla_target(%d): Doing ABORT_ALL_TASKS_SESS\n",
+                   sess->vha->vp_idx);
+               tmr_func = 0;
+               break;
+
+       case QLA_TGT_NEXUS_LOSS_SESS:
+               ql_dbg(ql_dbg_tgt_tmr, vha, 0x10007,
+                   "qla_target(%d): Doing NEXUS_LOSS_SESS\n",
+                   sess->vha->vp_idx);
+               tmr_func = 0;
+               break;
+
+       case QLA_TGT_NEXUS_LOSS:
+               ql_dbg(ql_dbg_tgt_tmr, vha, 0x10008,
+                   "qla_target(%d): Doing NEXUS_LOSS\n", sess->vha->vp_idx);
+               tmr_func = 0;
+               break;
+#endif
+       default:
+               ql_dbg(ql_dbg_tgt_tmr, vha, 0x1000a,
+                   "qla_target(%d): Unknown task mgmt fn 0x%x\n",
+                   sess->vha->vp_idx, fn);
+               mempool_free(mcmd, qla_tgt_mgmt_cmd_mempool);
+               return -ENOSYS;
+       }
+
+       res = ha->tgt.tgt_ops->handle_tmr(mcmd, lun, tmr_func, 0);
+       if (res != 0) {
+               ql_dbg(ql_dbg_tgt_tmr, vha, 0x1000b,
+                   "qla_target(%d): tgt.tgt_ops->handle_tmr() failed: %d\n",
+                   sess->vha->vp_idx, res);
+               mempool_free(mcmd, qla_tgt_mgmt_cmd_mempool);
+               return -EFAULT;
+       }
+
+       return 0;
+}
+
+/* ha->hardware_lock supposed to be held on entry */
+static int qlt_handle_task_mgmt(struct scsi_qla_host *vha, void *iocb)
+{
+       struct atio_from_isp *a = (struct atio_from_isp *)iocb;
+       struct qla_hw_data *ha = vha->hw;
+       struct qla_tgt *tgt;
+       struct qla_tgt_sess *sess;
+       uint32_t lun, unpacked_lun;
+       int lun_size, fn;
+
+       tgt = ha->tgt.qla_tgt;
+
+       lun = a->u.isp24.fcp_cmnd.lun;
+       lun_size = sizeof(a->u.isp24.fcp_cmnd.lun);
+       fn = a->u.isp24.fcp_cmnd.task_mgmt_flags;
+       sess = ha->tgt.tgt_ops->find_sess_by_s_id(vha,
+           a->u.isp24.fcp_hdr.s_id);
+       unpacked_lun = scsilun_to_int((struct scsi_lun *)&lun);
+
+       if (!sess) {
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf024,
+                   "qla_target(%d): task mgmt fn 0x%x for "
+                   "non-existant session\n", vha->vp_idx, fn);
+               return qlt_sched_sess_work(tgt, QLA_TGT_SESS_WORK_TM, iocb,
+                   sizeof(struct atio_from_isp));
+       }
+
+       return qlt_issue_task_mgmt(sess, unpacked_lun, fn, iocb, 0);
+}
+
+/* ha->hardware_lock supposed to be held on entry */
+static int __qlt_abort_task(struct scsi_qla_host *vha,
+       struct imm_ntfy_from_isp *iocb, struct qla_tgt_sess *sess)
+{
+       struct atio_from_isp *a = (struct atio_from_isp *)iocb;
+       struct qla_hw_data *ha = vha->hw;
+       struct qla_tgt_mgmt_cmd *mcmd;
+       uint32_t lun, unpacked_lun;
+       int rc;
+
+       mcmd = mempool_alloc(qla_tgt_mgmt_cmd_mempool, GFP_ATOMIC);
+       if (mcmd == NULL) {
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf05f,
+                   "qla_target(%d): %s: Allocation of ABORT cmd failed\n",
+                   vha->vp_idx, __func__);
+               return -ENOMEM;
+       }
+       memset(mcmd, 0, sizeof(*mcmd));
+
+       mcmd->sess = sess;
+       memcpy(&mcmd->orig_iocb.imm_ntfy, iocb,
+           sizeof(mcmd->orig_iocb.imm_ntfy));
+
+       lun = a->u.isp24.fcp_cmnd.lun;
+       unpacked_lun = scsilun_to_int((struct scsi_lun *)&lun);
+
+       rc = ha->tgt.tgt_ops->handle_tmr(mcmd, unpacked_lun, TMR_ABORT_TASK,
+           le16_to_cpu(iocb->u.isp2x.seq_id));
+       if (rc != 0) {
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf060,
+                   "qla_target(%d): tgt_ops->handle_tmr() failed: %d\n",
+                   vha->vp_idx, rc);
+               mempool_free(mcmd, qla_tgt_mgmt_cmd_mempool);
+               return -EFAULT;
+       }
+
+       return 0;
+}
+
+/* ha->hardware_lock supposed to be held on entry */
+static int qlt_abort_task(struct scsi_qla_host *vha,
+       struct imm_ntfy_from_isp *iocb)
+{
+       struct qla_hw_data *ha = vha->hw;
+       struct qla_tgt_sess *sess;
+       int loop_id;
+
+       loop_id = GET_TARGET_ID(ha, (struct atio_from_isp *)iocb);
+
+       sess = ha->tgt.tgt_ops->find_sess_by_loop_id(vha, loop_id);
+       if (sess == NULL) {
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf025,
+                   "qla_target(%d): task abort for unexisting "
+                   "session\n", vha->vp_idx);
+               return qlt_sched_sess_work(ha->tgt.qla_tgt,
+                   QLA_TGT_SESS_WORK_ABORT, iocb, sizeof(*iocb));
+       }
+
+       return __qlt_abort_task(vha, iocb, sess);
+}
+
+/*
+ * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire
+ */
+static int qlt_24xx_handle_els(struct scsi_qla_host *vha,
+       struct imm_ntfy_from_isp *iocb)
+{
+       struct qla_hw_data *ha = vha->hw;
+       int res = 0;
+
+       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf026,
+           "qla_target(%d): Port ID: 0x%02x:%02x:%02x"
+           " ELS opcode: 0x%02x\n", vha->vp_idx, iocb->u.isp24.port_id[0],
+           iocb->u.isp24.port_id[1], iocb->u.isp24.port_id[2],
+           iocb->u.isp24.status_subcode);
+
+       switch (iocb->u.isp24.status_subcode) {
+       case ELS_PLOGI:
+       case ELS_FLOGI:
+       case ELS_PRLI:
+       case ELS_LOGO:
+       case ELS_PRLO:
+               res = qlt_reset(vha, iocb, QLA_TGT_NEXUS_LOSS_SESS);
+               break;
+       case ELS_PDISC:
+       case ELS_ADISC:
+       {
+               struct qla_tgt *tgt = ha->tgt.qla_tgt;
+               if (tgt->link_reinit_iocb_pending) {
+                       qlt_send_notify_ack(vha, &tgt->link_reinit_iocb,
+                           0, 0, 0, 0, 0, 0);
+                       tgt->link_reinit_iocb_pending = 0;
+               }
+               res = 1; /* send notify ack */
+               break;
+       }
+
+       default:
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf061,
+                   "qla_target(%d): Unsupported ELS command %x "
+                   "received\n", vha->vp_idx, iocb->u.isp24.status_subcode);
+               res = qlt_reset(vha, iocb, QLA_TGT_NEXUS_LOSS_SESS);
+               break;
+       }
+
+       return res;
+}
+
+static int qlt_set_data_offset(struct qla_tgt_cmd *cmd, uint32_t offset)
+{
+       struct scatterlist *sg, *sgp, *sg_srr, *sg_srr_start = NULL;
+       size_t first_offset = 0, rem_offset = offset, tmp = 0;
+       int i, sg_srr_cnt, bufflen = 0;
+
+       ql_dbg(ql_dbg_tgt, cmd->vha, 0xe023,
+           "Entering qla_tgt_set_data_offset: cmd: %p, cmd->sg: %p, "
+           "cmd->sg_cnt: %u, direction: %d\n",
+           cmd, cmd->sg, cmd->sg_cnt, cmd->dma_data_direction);
+
+       /*
+        * FIXME: Reject non zero SRR relative offset until we can test
+        * this code properly.
+        */
+       pr_debug("Rejecting non zero SRR rel_offs: %u\n", offset);
+       return -1;
+
+       if (!cmd->sg || !cmd->sg_cnt) {
+               ql_dbg(ql_dbg_tgt, cmd->vha, 0xe055,
+                   "Missing cmd->sg or zero cmd->sg_cnt in"
+                   " qla_tgt_set_data_offset\n");
+               return -EINVAL;
+       }
+       /*
+        * Walk the current cmd->sg list until we locate the new sg_srr_start
+        */
+       for_each_sg(cmd->sg, sg, cmd->sg_cnt, i) {
+               ql_dbg(ql_dbg_tgt, cmd->vha, 0xe024,
+                   "sg[%d]: %p page: %p, length: %d, offset: %d\n",
+                   i, sg, sg_page(sg), sg->length, sg->offset);
+
+               if ((sg->length + tmp) > offset) {
+                       first_offset = rem_offset;
+                       sg_srr_start = sg;
+                       ql_dbg(ql_dbg_tgt, cmd->vha, 0xe025,
+                           "Found matching sg[%d], using %p as sg_srr_start, "
+                           "and using first_offset: %zu\n", i, sg,
+                           first_offset);
+                       break;
+               }
+               tmp += sg->length;
+               rem_offset -= sg->length;
+       }
+
+       if (!sg_srr_start) {
+               ql_dbg(ql_dbg_tgt, cmd->vha, 0xe056,
+                   "Unable to locate sg_srr_start for offset: %u\n", offset);
+               return -EINVAL;
+       }
+       sg_srr_cnt = (cmd->sg_cnt - i);
+
+       sg_srr = kzalloc(sizeof(struct scatterlist) * sg_srr_cnt, GFP_KERNEL);
+       if (!sg_srr) {
+               ql_dbg(ql_dbg_tgt, cmd->vha, 0xe057,
+                   "Unable to allocate sgp\n");
+               return -ENOMEM;
+       }
+       sg_init_table(sg_srr, sg_srr_cnt);
+       sgp = &sg_srr[0];
+       /*
+        * Walk the remaining list for sg_srr_start, mapping to the newly
+        * allocated sg_srr taking first_offset into account.
+        */
+       for_each_sg(sg_srr_start, sg, sg_srr_cnt, i) {
+               if (first_offset) {
+                       sg_set_page(sgp, sg_page(sg),
+                           (sg->length - first_offset), first_offset);
+                       first_offset = 0;
+               } else {
+                       sg_set_page(sgp, sg_page(sg), sg->length, 0);
+               }
+               bufflen += sgp->length;
+
+               sgp = sg_next(sgp);
+               if (!sgp)
+                       break;
+       }
+
+       cmd->sg = sg_srr;
+       cmd->sg_cnt = sg_srr_cnt;
+       cmd->bufflen = bufflen;
+       cmd->offset += offset;
+       cmd->free_sg = 1;
+
+       ql_dbg(ql_dbg_tgt, cmd->vha, 0xe026, "New cmd->sg: %p\n", cmd->sg);
+       ql_dbg(ql_dbg_tgt, cmd->vha, 0xe027, "New cmd->sg_cnt: %u\n",
+           cmd->sg_cnt);
+       ql_dbg(ql_dbg_tgt, cmd->vha, 0xe028, "New cmd->bufflen: %u\n",
+           cmd->bufflen);
+       ql_dbg(ql_dbg_tgt, cmd->vha, 0xe029, "New cmd->offset: %u\n",
+           cmd->offset);
+
+       if (cmd->sg_cnt < 0)
+               BUG();
+
+       if (cmd->bufflen < 0)
+               BUG();
+
+       return 0;
+}
+
+static inline int qlt_srr_adjust_data(struct qla_tgt_cmd *cmd,
+       uint32_t srr_rel_offs, int *xmit_type)
+{
+       int res = 0, rel_offs;
+
+       rel_offs = srr_rel_offs - cmd->offset;
+       ql_dbg(ql_dbg_tgt_mgt, cmd->vha, 0xf027, "srr_rel_offs=%d, rel_offs=%d",
+           srr_rel_offs, rel_offs);
+
+       *xmit_type = QLA_TGT_XMIT_ALL;
+
+       if (rel_offs < 0) {
+               ql_dbg(ql_dbg_tgt_mgt, cmd->vha, 0xf062,
+                   "qla_target(%d): SRR rel_offs (%d) < 0",
+                   cmd->vha->vp_idx, rel_offs);
+               res = -1;
+       } else if (rel_offs == cmd->bufflen)
+               *xmit_type = QLA_TGT_XMIT_STATUS;
+       else if (rel_offs > 0)
+               res = qlt_set_data_offset(cmd, rel_offs);
+
+       return res;
+}
+
+/* No locks, thread context */
+static void qlt_handle_srr(struct scsi_qla_host *vha,
+       struct qla_tgt_srr_ctio *sctio, struct qla_tgt_srr_imm *imm)
+{
+       struct imm_ntfy_from_isp *ntfy =
+           (struct imm_ntfy_from_isp *)&imm->imm_ntfy;
+       struct qla_hw_data *ha = vha->hw;
+       struct qla_tgt_cmd *cmd = sctio->cmd;
+       struct se_cmd *se_cmd = &cmd->se_cmd;
+       unsigned long flags;
+       int xmit_type = 0, resp = 0;
+       uint32_t offset;
+       uint16_t srr_ui;
+
+       offset = le32_to_cpu(ntfy->u.isp24.srr_rel_offs);
+       srr_ui = ntfy->u.isp24.srr_ui;
+
+       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf028, "SRR cmd %p, srr_ui %x\n",
+           cmd, srr_ui);
+
+       switch (srr_ui) {
+       case SRR_IU_STATUS:
+               spin_lock_irqsave(&ha->hardware_lock, flags);
+               qlt_send_notify_ack(vha, ntfy,
+                   0, 0, 0, NOTIFY_ACK_SRR_FLAGS_ACCEPT, 0, 0);
+               spin_unlock_irqrestore(&ha->hardware_lock, flags);
+               xmit_type = QLA_TGT_XMIT_STATUS;
+               resp = 1;
+               break;
+       case SRR_IU_DATA_IN:
+               if (!cmd->sg || !cmd->sg_cnt) {
+                       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf063,
+                           "Unable to process SRR_IU_DATA_IN due to"
+                           " missing cmd->sg, state: %d\n", cmd->state);
+                       dump_stack();
+                       goto out_reject;
+               }
+               if (se_cmd->scsi_status != 0) {
+                       ql_dbg(ql_dbg_tgt, vha, 0xe02a,
+                           "Rejecting SRR_IU_DATA_IN with non GOOD "
+                           "scsi_status\n");
+                       goto out_reject;
+               }
+               cmd->bufflen = se_cmd->data_length;
+
+               if (qlt_has_data(cmd)) {
+                       if (qlt_srr_adjust_data(cmd, offset, &xmit_type) != 0)
+                               goto out_reject;
+                       spin_lock_irqsave(&ha->hardware_lock, flags);
+                       qlt_send_notify_ack(vha, ntfy,
+                           0, 0, 0, NOTIFY_ACK_SRR_FLAGS_ACCEPT, 0, 0);
+                       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+                       resp = 1;
+               } else {
+                       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf064,
+                           "qla_target(%d): SRR for in data for cmd "
+                           "without them (tag %d, SCSI status %d), "
+                           "reject", vha->vp_idx, cmd->tag,
+                           cmd->se_cmd.scsi_status);
+                       goto out_reject;
+               }
+               break;
+       case SRR_IU_DATA_OUT:
+               if (!cmd->sg || !cmd->sg_cnt) {
+                       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf065,
+                           "Unable to process SRR_IU_DATA_OUT due to"
+                           " missing cmd->sg\n");
+                       dump_stack();
+                       goto out_reject;
+               }
+               if (se_cmd->scsi_status != 0) {
+                       ql_dbg(ql_dbg_tgt, vha, 0xe02b,
+                           "Rejecting SRR_IU_DATA_OUT"
+                           " with non GOOD scsi_status\n");
+                       goto out_reject;
+               }
+               cmd->bufflen = se_cmd->data_length;
+
+               if (qlt_has_data(cmd)) {
+                       if (qlt_srr_adjust_data(cmd, offset, &xmit_type) != 0)
+                               goto out_reject;
+                       spin_lock_irqsave(&ha->hardware_lock, flags);
+                       qlt_send_notify_ack(vha, ntfy,
+                           0, 0, 0, NOTIFY_ACK_SRR_FLAGS_ACCEPT, 0, 0);
+                       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+                       if (xmit_type & QLA_TGT_XMIT_DATA)
+                               qlt_rdy_to_xfer(cmd);
+               } else {
+                       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf066,
+                           "qla_target(%d): SRR for out data for cmd "
+                           "without them (tag %d, SCSI status %d), "
+                           "reject", vha->vp_idx, cmd->tag,
+                           cmd->se_cmd.scsi_status);
+                       goto out_reject;
+               }
+               break;
+       default:
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf067,
+                   "qla_target(%d): Unknown srr_ui value %x",
+                   vha->vp_idx, srr_ui);
+               goto out_reject;
+       }
+
+       /* Transmit response in case of status and data-in cases */
+       if (resp)
+               qlt_xmit_response(cmd, xmit_type, se_cmd->scsi_status);
+
+       return;
+
+out_reject:
+       spin_lock_irqsave(&ha->hardware_lock, flags);
+       qlt_send_notify_ack(vha, ntfy, 0, 0, 0,
+           NOTIFY_ACK_SRR_FLAGS_REJECT,
+           NOTIFY_ACK_SRR_REJECT_REASON_UNABLE_TO_PERFORM,
+           NOTIFY_ACK_SRR_FLAGS_REJECT_EXPL_NO_EXPL);
+       if (cmd->state == QLA_TGT_STATE_NEED_DATA) {
+               cmd->state = QLA_TGT_STATE_DATA_IN;
+               dump_stack();
+       } else
+               qlt_send_term_exchange(vha, cmd, &cmd->atio, 1);
+       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+}
+
+static void qlt_reject_free_srr_imm(struct scsi_qla_host *vha,
+       struct qla_tgt_srr_imm *imm, int ha_locked)
+{
+       struct qla_hw_data *ha = vha->hw;
+       unsigned long flags = 0;
+
+       if (!ha_locked)
+               spin_lock_irqsave(&ha->hardware_lock, flags);
+
+       qlt_send_notify_ack(vha, (void *)&imm->imm_ntfy, 0, 0, 0,
+           NOTIFY_ACK_SRR_FLAGS_REJECT,
+           NOTIFY_ACK_SRR_REJECT_REASON_UNABLE_TO_PERFORM,
+           NOTIFY_ACK_SRR_FLAGS_REJECT_EXPL_NO_EXPL);
+
+       if (!ha_locked)
+               spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+       kfree(imm);
+}
+
+static void qlt_handle_srr_work(struct work_struct *work)
+{
+       struct qla_tgt *tgt = container_of(work, struct qla_tgt, srr_work);
+       struct scsi_qla_host *vha = tgt->vha;
+       struct qla_tgt_srr_ctio *sctio;
+       unsigned long flags;
+
+       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf029, "Entering SRR work (tgt %p)\n",
+           tgt);
+
+restart:
+       spin_lock_irqsave(&tgt->srr_lock, flags);
+       list_for_each_entry(sctio, &tgt->srr_ctio_list, srr_list_entry) {
+               struct qla_tgt_srr_imm *imm, *i, *ti;
+               struct qla_tgt_cmd *cmd;
+               struct se_cmd *se_cmd;
+
+               imm = NULL;
+               list_for_each_entry_safe(i, ti, &tgt->srr_imm_list,
+                                               srr_list_entry) {
+                       if (i->srr_id == sctio->srr_id) {
+                               list_del(&i->srr_list_entry);
+                               if (imm) {
+                                       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf068,
+                                         "qla_target(%d): There must be "
+                                         "only one IMM SRR per CTIO SRR "
+                                         "(IMM SRR %p, id %d, CTIO %p\n",
+                                         vha->vp_idx, i, i->srr_id, sctio);
+                                       qlt_reject_free_srr_imm(tgt->vha, i, 0);
+                               } else
+                                       imm = i;
+                       }
+               }
+
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf02a,
+                   "IMM SRR %p, CTIO SRR %p (id %d)\n", imm, sctio,
+                   sctio->srr_id);
+
+               if (imm == NULL) {
+                       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf02b,
+                           "Not found matching IMM for SRR CTIO (id %d)\n",
+                           sctio->srr_id);
+                       continue;
+               } else
+                       list_del(&sctio->srr_list_entry);
+
+               spin_unlock_irqrestore(&tgt->srr_lock, flags);
+
+               cmd = sctio->cmd;
+               /*
+                * Reset qla_tgt_cmd SRR values and SGL pointer+count to follow
+                * tcm_qla2xxx_write_pending() and tcm_qla2xxx_queue_data_in()
+                * logic..
+                */
+               cmd->offset = 0;
+               if (cmd->free_sg) {
+                       kfree(cmd->sg);
+                       cmd->sg = NULL;
+                       cmd->free_sg = 0;
+               }
+               se_cmd = &cmd->se_cmd;
+
+               cmd->sg_cnt = se_cmd->t_data_nents;
+               cmd->sg = se_cmd->t_data_sg;
+
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf02c,
+                   "SRR cmd %p (se_cmd %p, tag %d, op %x), "
+                   "sg_cnt=%d, offset=%d", cmd, &cmd->se_cmd, cmd->tag,
+                   se_cmd->t_task_cdb[0], cmd->sg_cnt, cmd->offset);
+
+               qlt_handle_srr(vha, sctio, imm);
+
+               kfree(imm);
+               kfree(sctio);
+               goto restart;
+       }
+       spin_unlock_irqrestore(&tgt->srr_lock, flags);
+}
+
+/* ha->hardware_lock supposed to be held on entry */
+static void qlt_prepare_srr_imm(struct scsi_qla_host *vha,
+       struct imm_ntfy_from_isp *iocb)
+{
+       struct qla_tgt_srr_imm *imm;
+       struct qla_hw_data *ha = vha->hw;
+       struct qla_tgt *tgt = ha->tgt.qla_tgt;
+       struct qla_tgt_srr_ctio *sctio;
+
+       tgt->imm_srr_id++;
+
+       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf02d, "qla_target(%d): SRR received\n",
+           vha->vp_idx);
+
+       imm = kzalloc(sizeof(*imm), GFP_ATOMIC);
+       if (imm != NULL) {
+               memcpy(&imm->imm_ntfy, iocb, sizeof(imm->imm_ntfy));
+
+               /* IRQ is already OFF */
+               spin_lock(&tgt->srr_lock);
+               imm->srr_id = tgt->imm_srr_id;
+               list_add_tail(&imm->srr_list_entry,
+                   &tgt->srr_imm_list);
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf02e,
+                   "IMM NTFY SRR %p added (id %d, ui %x)\n",
+                   imm, imm->srr_id, iocb->u.isp24.srr_ui);
+               if (tgt->imm_srr_id == tgt->ctio_srr_id) {
+                       int found = 0;
+                       list_for_each_entry(sctio, &tgt->srr_ctio_list,
+                           srr_list_entry) {
+                               if (sctio->srr_id == imm->srr_id) {
+                                       found = 1;
+                                       break;
+                               }
+                       }
+                       if (found) {
+                               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf02f, "%s",
+                                   "Scheduling srr work\n");
+                               schedule_work(&tgt->srr_work);
+                       } else {
+                               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf030,
+                                   "qla_target(%d): imm_srr_id "
+                                   "== ctio_srr_id (%d), but there is no "
+                                   "corresponding SRR CTIO, deleting IMM "
+                                   "SRR %p\n", vha->vp_idx, tgt->ctio_srr_id,
+                                   imm);
+                               list_del(&imm->srr_list_entry);
+
+                               kfree(imm);
+
+                               spin_unlock(&tgt->srr_lock);
+                               goto out_reject;
+                       }
+               }
+               spin_unlock(&tgt->srr_lock);
+       } else {
+               struct qla_tgt_srr_ctio *ts;
+
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf069,
+                   "qla_target(%d): Unable to allocate SRR IMM "
+                   "entry, SRR request will be rejected\n", vha->vp_idx);
+
+               /* IRQ is already OFF */
+               spin_lock(&tgt->srr_lock);
+               list_for_each_entry_safe(sctio, ts, &tgt->srr_ctio_list,
+                   srr_list_entry) {
+                       if (sctio->srr_id == tgt->imm_srr_id) {
+                               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf031,
+                                   "CTIO SRR %p deleted (id %d)\n",
+                                   sctio, sctio->srr_id);
+                               list_del(&sctio->srr_list_entry);
+                               qlt_send_term_exchange(vha, sctio->cmd,
+                                   &sctio->cmd->atio, 1);
+                               kfree(sctio);
+                       }
+               }
+               spin_unlock(&tgt->srr_lock);
+               goto out_reject;
+       }
+
+       return;
+
+out_reject:
+       qlt_send_notify_ack(vha, iocb, 0, 0, 0,
+           NOTIFY_ACK_SRR_FLAGS_REJECT,
+           NOTIFY_ACK_SRR_REJECT_REASON_UNABLE_TO_PERFORM,
+           NOTIFY_ACK_SRR_FLAGS_REJECT_EXPL_NO_EXPL);
+}
+
+/*
+ * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire
+ */
+static void qlt_handle_imm_notify(struct scsi_qla_host *vha,
+       struct imm_ntfy_from_isp *iocb)
+{
+       struct qla_hw_data *ha = vha->hw;
+       uint32_t add_flags = 0;
+       int send_notify_ack = 1;
+       uint16_t status;
+
+       status = le16_to_cpu(iocb->u.isp2x.status);
+       switch (status) {
+       case IMM_NTFY_LIP_RESET:
+       {
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf032,
+                   "qla_target(%d): LIP reset (loop %#x), subcode %x\n",
+                   vha->vp_idx, le16_to_cpu(iocb->u.isp24.nport_handle),
+                   iocb->u.isp24.status_subcode);
+
+               if (qlt_reset(vha, iocb, QLA_TGT_ABORT_ALL) == 0)
+                       send_notify_ack = 0;
+               break;
+       }
+
+       case IMM_NTFY_LIP_LINK_REINIT:
+       {
+               struct qla_tgt *tgt = ha->tgt.qla_tgt;
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf033,
+                   "qla_target(%d): LINK REINIT (loop %#x, "
+                   "subcode %x)\n", vha->vp_idx,
+                   le16_to_cpu(iocb->u.isp24.nport_handle),
+                   iocb->u.isp24.status_subcode);
+               if (tgt->link_reinit_iocb_pending) {
+                       qlt_send_notify_ack(vha, &tgt->link_reinit_iocb,
+                           0, 0, 0, 0, 0, 0);
+               }
+               memcpy(&tgt->link_reinit_iocb, iocb, sizeof(*iocb));
+               tgt->link_reinit_iocb_pending = 1;
+               /*
+                * QLogic requires to wait after LINK REINIT for possible
+                * PDISC or ADISC ELS commands
+                */
+               send_notify_ack = 0;
+               break;
+       }
+
+       case IMM_NTFY_PORT_LOGOUT:
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf034,
+                   "qla_target(%d): Port logout (loop "
+                   "%#x, subcode %x)\n", vha->vp_idx,
+                   le16_to_cpu(iocb->u.isp24.nport_handle),
+                   iocb->u.isp24.status_subcode);
+
+               if (qlt_reset(vha, iocb, QLA_TGT_NEXUS_LOSS_SESS) == 0)
+                       send_notify_ack = 0;
+               /* The sessions will be cleared in the callback, if needed */
+               break;
+
+       case IMM_NTFY_GLBL_TPRLO:
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf035,
+                   "qla_target(%d): Global TPRLO (%x)\n", vha->vp_idx, status);
+               if (qlt_reset(vha, iocb, QLA_TGT_NEXUS_LOSS) == 0)
+                       send_notify_ack = 0;
+               /* The sessions will be cleared in the callback, if needed */
+               break;
+
+       case IMM_NTFY_PORT_CONFIG:
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf036,
+                   "qla_target(%d): Port config changed (%x)\n", vha->vp_idx,
+                   status);
+               if (qlt_reset(vha, iocb, QLA_TGT_ABORT_ALL) == 0)
+                       send_notify_ack = 0;
+               /* The sessions will be cleared in the callback, if needed */
+               break;
+
+       case IMM_NTFY_GLBL_LOGO:
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf06a,
+                   "qla_target(%d): Link failure detected\n",
+                   vha->vp_idx);
+               /* I_T nexus loss */
+               if (qlt_reset(vha, iocb, QLA_TGT_NEXUS_LOSS) == 0)
+                       send_notify_ack = 0;
+               break;
+
+       case IMM_NTFY_IOCB_OVERFLOW:
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf06b,
+                   "qla_target(%d): Cannot provide requested "
+                   "capability (IOCB overflowed the immediate notify "
+                   "resource count)\n", vha->vp_idx);
+               break;
+
+       case IMM_NTFY_ABORT_TASK:
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf037,
+                   "qla_target(%d): Abort Task (S %08x I %#x -> "
+                   "L %#x)\n", vha->vp_idx,
+                   le16_to_cpu(iocb->u.isp2x.seq_id),
+                   GET_TARGET_ID(ha, (struct atio_from_isp *)iocb),
+                   le16_to_cpu(iocb->u.isp2x.lun));
+               if (qlt_abort_task(vha, iocb) == 0)
+                       send_notify_ack = 0;
+               break;
+
+       case IMM_NTFY_RESOURCE:
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf06c,
+                   "qla_target(%d): Out of resources, host %ld\n",
+                   vha->vp_idx, vha->host_no);
+               break;
+
+       case IMM_NTFY_MSG_RX:
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf038,
+                   "qla_target(%d): Immediate notify task %x\n",
+                   vha->vp_idx, iocb->u.isp2x.task_flags);
+               if (qlt_handle_task_mgmt(vha, iocb) == 0)
+                       send_notify_ack = 0;
+               break;
+
+       case IMM_NTFY_ELS:
+               if (qlt_24xx_handle_els(vha, iocb) == 0)
+                       send_notify_ack = 0;
+               break;
+
+       case IMM_NTFY_SRR:
+               qlt_prepare_srr_imm(vha, iocb);
+               send_notify_ack = 0;
+               break;
+
+       default:
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf06d,
+                   "qla_target(%d): Received unknown immediate "
+                   "notify status %x\n", vha->vp_idx, status);
+               break;
+       }
+
+       if (send_notify_ack)
+               qlt_send_notify_ack(vha, iocb, add_flags, 0, 0, 0, 0, 0);
+}
+
+/*
+ * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire
+ * This function sends busy to ISP 2xxx or 24xx.
+ */
+static void qlt_send_busy(struct scsi_qla_host *vha,
+       struct atio_from_isp *atio, uint16_t status)
+{
+       struct ctio7_to_24xx *ctio24;
+       struct qla_hw_data *ha = vha->hw;
+       request_t *pkt;
+       struct qla_tgt_sess *sess = NULL;
+
+       sess = ha->tgt.tgt_ops->find_sess_by_s_id(vha,
+           atio->u.isp24.fcp_hdr.s_id);
+       if (!sess) {
+               qlt_send_term_exchange(vha, NULL, atio, 1);
+               return;
+       }
+       /* Sending marker isn't necessary, since we called from ISR */
+
+       pkt = (request_t *)qla2x00_alloc_iocbs(vha, NULL);
+       if (!pkt) {
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf06e,
+                   "qla_target(%d): %s failed: unable to allocate "
+                   "request packet", vha->vp_idx, __func__);
+               return;
+       }
+
+       pkt->entry_count = 1;
+       pkt->handle = QLA_TGT_SKIP_HANDLE | CTIO_COMPLETION_HANDLE_MARK;
+
+       ctio24 = (struct ctio7_to_24xx *)pkt;
+       ctio24->entry_type = CTIO_TYPE7;
+       ctio24->nport_handle = sess->loop_id;
+       ctio24->timeout = __constant_cpu_to_le16(QLA_TGT_TIMEOUT);
+       ctio24->vp_index = vha->vp_idx;
+       ctio24->initiator_id[0] = atio->u.isp24.fcp_hdr.s_id[2];
+       ctio24->initiator_id[1] = atio->u.isp24.fcp_hdr.s_id[1];
+       ctio24->initiator_id[2] = atio->u.isp24.fcp_hdr.s_id[0];
+       ctio24->exchange_addr = atio->u.isp24.exchange_addr;
+       ctio24->u.status1.flags = (atio->u.isp24.attr << 9) |
+           __constant_cpu_to_le16(
+               CTIO7_FLAGS_STATUS_MODE_1 | CTIO7_FLAGS_SEND_STATUS |
+               CTIO7_FLAGS_DONT_RET_CTIO);
+       /*
+        * CTIO from fw w/o se_cmd doesn't provide enough info to retry it,
+        * if the explicit conformation is used.
+        */
+       ctio24->u.status1.ox_id = swab16(atio->u.isp24.fcp_hdr.ox_id);
+       ctio24->u.status1.scsi_status = cpu_to_le16(status);
+       ctio24->u.status1.residual = get_unaligned((uint32_t *)
+           &atio->u.isp24.fcp_cmnd.add_cdb[
+           atio->u.isp24.fcp_cmnd.add_cdb_len]);
+       if (ctio24->u.status1.residual != 0)
+               ctio24->u.status1.scsi_status |= SS_RESIDUAL_UNDER;
+
+       qla2x00_start_iocbs(vha, vha->req);
+}
+
+/* ha->hardware_lock supposed to be held on entry */
+/* called via callback from qla2xxx */
+static void qlt_24xx_atio_pkt(struct scsi_qla_host *vha,
+       struct atio_from_isp *atio)
+{
+       struct qla_hw_data *ha = vha->hw;
+       struct qla_tgt *tgt = ha->tgt.qla_tgt;
+       int rc;
+
+       if (unlikely(tgt == NULL)) {
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf039,
+                   "ATIO pkt, but no tgt (ha %p)", ha);
+               return;
+       }
+       ql_dbg(ql_dbg_tgt, vha, 0xe02c,
+           "qla_target(%d): ATIO pkt %p: type %02x count %02x",
+           vha->vp_idx, atio, atio->u.raw.entry_type,
+           atio->u.raw.entry_count);
+       /*
+        * In tgt_stop mode we also should allow all requests to pass.
+        * Otherwise, some commands can stuck.
+        */
+
+       tgt->irq_cmd_count++;
+
+       switch (atio->u.raw.entry_type) {
+       case ATIO_TYPE7:
+               ql_dbg(ql_dbg_tgt, vha, 0xe02d,
+                   "ATIO_TYPE7 instance %d, lun %Lx, read/write %d/%d, "
+                   "add_cdb_len %d, data_length %04x, s_id %x:%x:%x\n",
+                   vha->vp_idx, atio->u.isp24.fcp_cmnd.lun,
+                   atio->u.isp24.fcp_cmnd.rddata,
+                   atio->u.isp24.fcp_cmnd.wrdata,
+                   atio->u.isp24.fcp_cmnd.add_cdb_len,
+                   be32_to_cpu(get_unaligned((uint32_t *)
+                       &atio->u.isp24.fcp_cmnd.add_cdb[
+                       atio->u.isp24.fcp_cmnd.add_cdb_len])),
+                   atio->u.isp24.fcp_hdr.s_id[0],
+                   atio->u.isp24.fcp_hdr.s_id[1],
+                   atio->u.isp24.fcp_hdr.s_id[2]);
+
+               if (unlikely(atio->u.isp24.exchange_addr ==
+                   ATIO_EXCHANGE_ADDRESS_UNKNOWN)) {
+                       ql_dbg(ql_dbg_tgt, vha, 0xe058,
+                           "qla_target(%d): ATIO_TYPE7 "
+                           "received with UNKNOWN exchange address, "
+                           "sending QUEUE_FULL\n", vha->vp_idx);
+                       qlt_send_busy(vha, atio, SAM_STAT_TASK_SET_FULL);
+                       break;
+               }
+               if (likely(atio->u.isp24.fcp_cmnd.task_mgmt_flags == 0))
+                       rc = qlt_handle_cmd_for_atio(vha, atio);
+               else
+                       rc = qlt_handle_task_mgmt(vha, atio);
+               if (unlikely(rc != 0)) {
+                       if (rc == -ESRCH) {
+#if 1 /* With TERM EXCHANGE some FC cards refuse to boot */
+                               qlt_send_busy(vha, atio, SAM_STAT_BUSY);
+#else
+                               qlt_send_term_exchange(vha, NULL, atio, 1);
+#endif
+                       } else {
+                               if (tgt->tgt_stop) {
+                                       ql_dbg(ql_dbg_tgt, vha, 0xe059,
+                                           "qla_target: Unable to send "
+                                           "command to target for req, "
+                                           "ignoring.\n");
+                               } else {
+                                       ql_dbg(ql_dbg_tgt, vha, 0xe05a,
+                                           "qla_target(%d): Unable to send "
+                                           "command to target, sending BUSY "
+                                           "status.\n", vha->vp_idx);
+                                       qlt_send_busy(vha, atio, SAM_STAT_BUSY);
+                               }
+                       }
+               }
+               break;
+
+       case IMMED_NOTIFY_TYPE:
+       {
+               if (unlikely(atio->u.isp2x.entry_status != 0)) {
+                       ql_dbg(ql_dbg_tgt, vha, 0xe05b,
+                           "qla_target(%d): Received ATIO packet %x "
+                           "with error status %x\n", vha->vp_idx,
+                           atio->u.raw.entry_type,
+                           atio->u.isp2x.entry_status);
+                       break;
+               }
+               ql_dbg(ql_dbg_tgt, vha, 0xe02e, "%s", "IMMED_NOTIFY ATIO");
+               qlt_handle_imm_notify(vha, (struct imm_ntfy_from_isp *)atio);
+               break;
+       }
+
+       default:
+               ql_dbg(ql_dbg_tgt, vha, 0xe05c,
+                   "qla_target(%d): Received unknown ATIO atio "
+                   "type %x\n", vha->vp_idx, atio->u.raw.entry_type);
+               break;
+       }
+
+       tgt->irq_cmd_count--;
+}
+
+/* ha->hardware_lock supposed to be held on entry */
+/* called via callback from qla2xxx */
+static void qlt_response_pkt(struct scsi_qla_host *vha, response_t *pkt)
+{
+       struct qla_hw_data *ha = vha->hw;
+       struct qla_tgt *tgt = ha->tgt.qla_tgt;
+
+       if (unlikely(tgt == NULL)) {
+               ql_dbg(ql_dbg_tgt, vha, 0xe05d,
+                   "qla_target(%d): Response pkt %x received, but no "
+                   "tgt (ha %p)\n", vha->vp_idx, pkt->entry_type, ha);
+               return;
+       }
+
+       ql_dbg(ql_dbg_tgt, vha, 0xe02f,
+           "qla_target(%d): response pkt %p: T %02x C %02x S %02x "
+           "handle %#x\n", vha->vp_idx, pkt, pkt->entry_type,
+           pkt->entry_count, pkt->entry_status, pkt->handle);
+
+       /*
+        * In tgt_stop mode we also should allow all requests to pass.
+        * Otherwise, some commands can stuck.
+        */
+
+       tgt->irq_cmd_count++;
+
+       switch (pkt->entry_type) {
+       case CTIO_TYPE7:
+       {
+               struct ctio7_from_24xx *entry = (struct ctio7_from_24xx *)pkt;
+               ql_dbg(ql_dbg_tgt, vha, 0xe030, "CTIO_TYPE7: instance %d\n",
+                   vha->vp_idx);
+               qlt_do_ctio_completion(vha, entry->handle,
+                   le16_to_cpu(entry->status)|(pkt->entry_status << 16),
+                   entry);
+               break;
+       }
+
+       case ACCEPT_TGT_IO_TYPE:
+       {
+               struct atio_from_isp *atio = (struct atio_from_isp *)pkt;
+               int rc;
+               ql_dbg(ql_dbg_tgt, vha, 0xe031,
+                   "ACCEPT_TGT_IO instance %d status %04x "
+                   "lun %04x read/write %d data_length %04x "
+                   "target_id %02x rx_id %04x\n ", vha->vp_idx,
+                   le16_to_cpu(atio->u.isp2x.status),
+                   le16_to_cpu(atio->u.isp2x.lun),
+                   atio->u.isp2x.execution_codes,
+                   le32_to_cpu(atio->u.isp2x.data_length), GET_TARGET_ID(ha,
+                   atio), atio->u.isp2x.rx_id);
+               if (atio->u.isp2x.status !=
+                   __constant_cpu_to_le16(ATIO_CDB_VALID)) {
+                       ql_dbg(ql_dbg_tgt, vha, 0xe05e,
+                           "qla_target(%d): ATIO with error "
+                           "status %x received\n", vha->vp_idx,
+                           le16_to_cpu(atio->u.isp2x.status));
+                       break;
+               }
+               ql_dbg(ql_dbg_tgt, vha, 0xe032,
+                   "FCP CDB: 0x%02x, sizeof(cdb): %lu",
+                   atio->u.isp2x.cdb[0], (unsigned long
+                   int)sizeof(atio->u.isp2x.cdb));
+
+               rc = qlt_handle_cmd_for_atio(vha, atio);
+               if (unlikely(rc != 0)) {
+                       if (rc == -ESRCH) {
+#if 1 /* With TERM EXCHANGE some FC cards refuse to boot */
+                               qlt_send_busy(vha, atio, 0);
+#else
+                               qlt_send_term_exchange(vha, NULL, atio, 1);
+#endif
+                       } else {
+                               if (tgt->tgt_stop) {
+                                       ql_dbg(ql_dbg_tgt, vha, 0xe05f,
+                                           "qla_target: Unable to send "
+                                           "command to target, sending TERM "
+                                           "EXCHANGE for rsp\n");
+                                       qlt_send_term_exchange(vha, NULL,
+                                           atio, 1);
+                               } else {
+                                       ql_dbg(ql_dbg_tgt, vha, 0xe060,
+                                           "qla_target(%d): Unable to send "
+                                           "command to target, sending BUSY "
+                                           "status\n", vha->vp_idx);
+                                       qlt_send_busy(vha, atio, 0);
+                               }
+                       }
+               }
+       }
+       break;
+
+       case CONTINUE_TGT_IO_TYPE:
+       {
+               struct ctio_to_2xxx *entry = (struct ctio_to_2xxx *)pkt;
+               ql_dbg(ql_dbg_tgt, vha, 0xe033,
+                   "CONTINUE_TGT_IO: instance %d\n", vha->vp_idx);
+               qlt_do_ctio_completion(vha, entry->handle,
+                   le16_to_cpu(entry->status)|(pkt->entry_status << 16),
+                   entry);
+               break;
+       }
+
+       case CTIO_A64_TYPE:
+       {
+               struct ctio_to_2xxx *entry = (struct ctio_to_2xxx *)pkt;
+               ql_dbg(ql_dbg_tgt, vha, 0xe034, "CTIO_A64: instance %d\n",
+                   vha->vp_idx);
+               qlt_do_ctio_completion(vha, entry->handle,
+                   le16_to_cpu(entry->status)|(pkt->entry_status << 16),
+                   entry);
+               break;
+       }
+
+       case IMMED_NOTIFY_TYPE:
+               ql_dbg(ql_dbg_tgt, vha, 0xe035, "%s", "IMMED_NOTIFY\n");
+               qlt_handle_imm_notify(vha, (struct imm_ntfy_from_isp *)pkt);
+               break;
+
+       case NOTIFY_ACK_TYPE:
+               if (tgt->notify_ack_expected > 0) {
+                       struct nack_to_isp *entry = (struct nack_to_isp *)pkt;
+                       ql_dbg(ql_dbg_tgt, vha, 0xe036,
+                           "NOTIFY_ACK seq %08x status %x\n",
+                           le16_to_cpu(entry->u.isp2x.seq_id),
+                           le16_to_cpu(entry->u.isp2x.status));
+                       tgt->notify_ack_expected--;
+                       if (entry->u.isp2x.status !=
+                           __constant_cpu_to_le16(NOTIFY_ACK_SUCCESS)) {
+                               ql_dbg(ql_dbg_tgt, vha, 0xe061,
+                                   "qla_target(%d): NOTIFY_ACK "
+                                   "failed %x\n", vha->vp_idx,
+                                   le16_to_cpu(entry->u.isp2x.status));
+                       }
+               } else {
+                       ql_dbg(ql_dbg_tgt, vha, 0xe062,
+                           "qla_target(%d): Unexpected NOTIFY_ACK received\n",
+                           vha->vp_idx);
+               }
+               break;
+
+       case ABTS_RECV_24XX:
+               ql_dbg(ql_dbg_tgt, vha, 0xe037,
+                   "ABTS_RECV_24XX: instance %d\n", vha->vp_idx);
+               qlt_24xx_handle_abts(vha, (struct abts_recv_from_24xx *)pkt);
+               break;
+
+       case ABTS_RESP_24XX:
+               if (tgt->abts_resp_expected > 0) {
+                       struct abts_resp_from_24xx_fw *entry =
+                               (struct abts_resp_from_24xx_fw *)pkt;
+                       ql_dbg(ql_dbg_tgt, vha, 0xe038,
+                           "ABTS_RESP_24XX: compl_status %x\n",
+                           entry->compl_status);
+                       tgt->abts_resp_expected--;
+                       if (le16_to_cpu(entry->compl_status) !=
+                           ABTS_RESP_COMPL_SUCCESS) {
+                               if ((entry->error_subcode1 == 0x1E) &&
+                                   (entry->error_subcode2 == 0)) {
+                                       /*
+                                        * We've got a race here: aborted
+                                        * exchange not terminated, i.e.
+                                        * response for the aborted command was
+                                        * sent between the abort request was
+                                        * received and processed.
+                                        * Unfortunately, the firmware has a
+                                        * silly requirement that all aborted
+                                        * exchanges must be explicitely
+                                        * terminated, otherwise it refuses to
+                                        * send responses for the abort
+                                        * requests. So, we have to
+                                        * (re)terminate the exchange and retry
+                                        * the abort response.
+                                        */
+                                       qlt_24xx_retry_term_exchange(vha,
+                                           entry);
+                               } else
+                                       ql_dbg(ql_dbg_tgt, vha, 0xe063,
+                                           "qla_target(%d): ABTS_RESP_24XX "
+                                           "failed %x (subcode %x:%x)",
+                                           vha->vp_idx, entry->compl_status,
+                                           entry->error_subcode1,
+                                           entry->error_subcode2);
+                       }
+               } else {
+                       ql_dbg(ql_dbg_tgt, vha, 0xe064,
+                           "qla_target(%d): Unexpected ABTS_RESP_24XX "
+                           "received\n", vha->vp_idx);
+               }
+               break;
+
+       default:
+               ql_dbg(ql_dbg_tgt, vha, 0xe065,
+                   "qla_target(%d): Received unknown response pkt "
+                   "type %x\n", vha->vp_idx, pkt->entry_type);
+               break;
+       }
+
+       tgt->irq_cmd_count--;
+}
+
+/*
+ * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire
+ */
+void qlt_async_event(uint16_t code, struct scsi_qla_host *vha,
+       uint16_t *mailbox)
+{
+       struct qla_hw_data *ha = vha->hw;
+       struct qla_tgt *tgt = ha->tgt.qla_tgt;
+       int reason_code;
+
+       ql_dbg(ql_dbg_tgt, vha, 0xe039,
+           "scsi(%ld): ha state %d init_done %d oper_mode %d topo %d\n",
+           vha->host_no, atomic_read(&vha->loop_state), vha->flags.init_done,
+           ha->operating_mode, ha->current_topology);
+
+       if (!ha->tgt.tgt_ops)
+               return;
+
+       if (unlikely(tgt == NULL)) {
+               ql_dbg(ql_dbg_tgt, vha, 0xe03a,
+                   "ASYNC EVENT %#x, but no tgt (ha %p)\n", code, ha);
+               return;
+       }
+
+       if (((code == MBA_POINT_TO_POINT) || (code == MBA_CHG_IN_CONNECTION)) &&
+           IS_QLA2100(ha))
+               return;
+       /*
+        * In tgt_stop mode we also should allow all requests to pass.
+        * Otherwise, some commands can stuck.
+        */
+
+       tgt->irq_cmd_count++;
+
+       switch (code) {
+       case MBA_RESET:                 /* Reset */
+       case MBA_SYSTEM_ERR:            /* System Error */
+       case MBA_REQ_TRANSFER_ERR:      /* Request Transfer Error */
+       case MBA_RSP_TRANSFER_ERR:      /* Response Transfer Error */
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf03a,
+                   "qla_target(%d): System error async event %#x "
+                   "occured", vha->vp_idx, code);
+               break;
+       case MBA_WAKEUP_THRES:          /* Request Queue Wake-up. */
+               set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
+               break;
+
+       case MBA_LOOP_UP:
+       {
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf03b,
+                   "qla_target(%d): Async LOOP_UP occured "
+                   "(m[1]=%x, m[2]=%x, m[3]=%x, m[4]=%x)", vha->vp_idx,
+                   le16_to_cpu(mailbox[1]), le16_to_cpu(mailbox[2]),
+                   le16_to_cpu(mailbox[3]), le16_to_cpu(mailbox[4]));
+               if (tgt->link_reinit_iocb_pending) {
+                       qlt_send_notify_ack(vha, (void *)&tgt->link_reinit_iocb,
+                           0, 0, 0, 0, 0, 0);
+                       tgt->link_reinit_iocb_pending = 0;
+               }
+               break;
+       }
+
+       case MBA_LIP_OCCURRED:
+       case MBA_LOOP_DOWN:
+       case MBA_LIP_RESET:
+       case MBA_RSCN_UPDATE:
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf03c,
+                   "qla_target(%d): Async event %#x occured "
+                   "(m[1]=%x, m[2]=%x, m[3]=%x, m[4]=%x)", vha->vp_idx, code,
+                   le16_to_cpu(mailbox[1]), le16_to_cpu(mailbox[2]),
+                   le16_to_cpu(mailbox[3]), le16_to_cpu(mailbox[4]));
+               break;
+
+       case MBA_PORT_UPDATE:
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf03d,
+                   "qla_target(%d): Port update async event %#x "
+                   "occured: updating the ports database (m[1]=%x, m[2]=%x, "
+                   "m[3]=%x, m[4]=%x)", vha->vp_idx, code,
+                   le16_to_cpu(mailbox[1]), le16_to_cpu(mailbox[2]),
+                   le16_to_cpu(mailbox[3]), le16_to_cpu(mailbox[4]));
+               reason_code = le16_to_cpu(mailbox[2]);
+               if (reason_code == 0x4)
+                       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf03e,
+                           "Async MB 2: Got PLOGI Complete\n");
+               else if (reason_code == 0x7)
+                       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf03f,
+                           "Async MB 2: Port Logged Out\n");
+               break;
+
+       default:
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf040,
+                   "qla_target(%d): Async event %#x occured: "
+                   "ignore (m[1]=%x, m[2]=%x, m[3]=%x, m[4]=%x)", vha->vp_idx,
+                   code, le16_to_cpu(mailbox[1]), le16_to_cpu(mailbox[2]),
+                   le16_to_cpu(mailbox[3]), le16_to_cpu(mailbox[4]));
+               break;
+       }
+
+       tgt->irq_cmd_count--;
+}
+
+static fc_port_t *qlt_get_port_database(struct scsi_qla_host *vha,
+       uint16_t loop_id)
+{
+       fc_port_t *fcport;
+       int rc;
+
+       fcport = kzalloc(sizeof(*fcport), GFP_KERNEL);
+       if (!fcport) {
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf06f,
+                   "qla_target(%d): Allocation of tmp FC port failed",
+                   vha->vp_idx);
+               return NULL;
+       }
+
+       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf041, "loop_id %d", loop_id);
+
+       fcport->loop_id = loop_id;
+
+       rc = qla2x00_get_port_database(vha, fcport, 0);
+       if (rc != QLA_SUCCESS) {
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf070,
+                   "qla_target(%d): Failed to retrieve fcport "
+                   "information -- get_port_database() returned %x "
+                   "(loop_id=0x%04x)", vha->vp_idx, rc, loop_id);
+               kfree(fcport);
+               return NULL;
+       }
+
+       return fcport;
+}
+
+/* Must be called under tgt_mutex */
+static struct qla_tgt_sess *qlt_make_local_sess(struct scsi_qla_host *vha,
+       uint8_t *s_id)
+{
+       struct qla_hw_data *ha = vha->hw;
+       struct qla_tgt_sess *sess = NULL;
+       fc_port_t *fcport = NULL;
+       int rc, global_resets;
+       uint16_t loop_id = 0;
+
+retry:
+       global_resets = atomic_read(&ha->tgt.qla_tgt->tgt_global_resets_count);
+
+       rc = qla24xx_get_loop_id(vha, s_id, &loop_id);
+       if (rc != 0) {
+               if ((s_id[0] == 0xFF) &&
+                   (s_id[1] == 0xFC)) {
+                       /*
+                        * This is Domain Controller, so it should be
+                        * OK to drop SCSI commands from it.
+                        */
+                       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf042,
+                           "Unable to find initiator with S_ID %x:%x:%x",
+                           s_id[0], s_id[1], s_id[2]);
+               } else
+                       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf071,
+                           "qla_target(%d): Unable to find "
+                           "initiator with S_ID %x:%x:%x",
+                           vha->vp_idx, s_id[0], s_id[1],
+                           s_id[2]);
+               return NULL;
+       }
+
+       fcport = qlt_get_port_database(vha, loop_id);
+       if (!fcport)
+               return NULL;
+
+       if (global_resets !=
+           atomic_read(&ha->tgt.qla_tgt->tgt_global_resets_count)) {
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf043,
+                   "qla_target(%d): global reset during session discovery "
+                   "(counter was %d, new %d), retrying", vha->vp_idx,
+                   global_resets,
+                   atomic_read(&ha->tgt.qla_tgt->tgt_global_resets_count));
+               goto retry;
+       }
+
+       sess = qlt_create_sess(vha, fcport, true);
+
+       kfree(fcport);
+       return sess;
+}
+
+static void qlt_abort_work(struct qla_tgt *tgt,
+       struct qla_tgt_sess_work_param *prm)
+{
+       struct scsi_qla_host *vha = tgt->vha;
+       struct qla_hw_data *ha = vha->hw;
+       struct qla_tgt_sess *sess = NULL;
+       unsigned long flags;
+       uint32_t be_s_id;
+       uint8_t s_id[3];
+       int rc;
+
+       spin_lock_irqsave(&ha->hardware_lock, flags);
+
+       if (tgt->tgt_stop)
+               goto out_term;
+
+       s_id[0] = prm->abts.fcp_hdr_le.s_id[2];
+       s_id[1] = prm->abts.fcp_hdr_le.s_id[1];
+       s_id[2] = prm->abts.fcp_hdr_le.s_id[0];
+
+       sess = ha->tgt.tgt_ops->find_sess_by_s_id(vha,
+           (unsigned char *)&be_s_id);
+       if (!sess) {
+               spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+               mutex_lock(&ha->tgt.tgt_mutex);
+               sess = qlt_make_local_sess(vha, s_id);
+               /* sess has got an extra creation ref */
+               mutex_unlock(&ha->tgt.tgt_mutex);
+
+               spin_lock_irqsave(&ha->hardware_lock, flags);
+               if (!sess)
+                       goto out_term;
+       } else {
+               kref_get(&sess->se_sess->sess_kref);
+       }
+
+       if (tgt->tgt_stop)
+               goto out_term;
+
+       rc = __qlt_24xx_handle_abts(vha, &prm->abts, sess);
+       if (rc != 0)
+               goto out_term;
+       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+       ha->tgt.tgt_ops->put_sess(sess);
+       return;
+
+out_term:
+       qlt_24xx_send_abts_resp(vha, &prm->abts, FCP_TMF_REJECTED, false);
+       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+       if (sess)
+               ha->tgt.tgt_ops->put_sess(sess);
+}
+
+static void qlt_tmr_work(struct qla_tgt *tgt,
+       struct qla_tgt_sess_work_param *prm)
+{
+       struct atio_from_isp *a = &prm->tm_iocb2;
+       struct scsi_qla_host *vha = tgt->vha;
+       struct qla_hw_data *ha = vha->hw;
+       struct qla_tgt_sess *sess = NULL;
+       unsigned long flags;
+       uint8_t *s_id = NULL; /* to hide compiler warnings */
+       int rc;
+       uint32_t lun, unpacked_lun;
+       int lun_size, fn;
+       void *iocb;
+
+       spin_lock_irqsave(&ha->hardware_lock, flags);
+
+       if (tgt->tgt_stop)
+               goto out_term;
+
+       s_id = prm->tm_iocb2.u.isp24.fcp_hdr.s_id;
+       sess = ha->tgt.tgt_ops->find_sess_by_s_id(vha, s_id);
+       if (!sess) {
+               spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+               mutex_lock(&ha->tgt.tgt_mutex);
+               sess = qlt_make_local_sess(vha, s_id);
+               /* sess has got an extra creation ref */
+               mutex_unlock(&ha->tgt.tgt_mutex);
+
+               spin_lock_irqsave(&ha->hardware_lock, flags);
+               if (!sess)
+                       goto out_term;
+       } else {
+               kref_get(&sess->se_sess->sess_kref);
+       }
+
+       iocb = a;
+       lun = a->u.isp24.fcp_cmnd.lun;
+       lun_size = sizeof(lun);
+       fn = a->u.isp24.fcp_cmnd.task_mgmt_flags;
+       unpacked_lun = scsilun_to_int((struct scsi_lun *)&lun);
+
+       rc = qlt_issue_task_mgmt(sess, unpacked_lun, fn, iocb, 0);
+       if (rc != 0)
+               goto out_term;
+       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+       ha->tgt.tgt_ops->put_sess(sess);
+       return;
+
+out_term:
+       qlt_send_term_exchange(vha, NULL, &prm->tm_iocb2, 1);
+       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+       if (sess)
+               ha->tgt.tgt_ops->put_sess(sess);
+}
+
+static void qlt_sess_work_fn(struct work_struct *work)
+{
+       struct qla_tgt *tgt = container_of(work, struct qla_tgt, sess_work);
+       struct scsi_qla_host *vha = tgt->vha;
+       unsigned long flags;
+
+       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf000, "Sess work (tgt %p)", tgt);
+
+       spin_lock_irqsave(&tgt->sess_work_lock, flags);
+       while (!list_empty(&tgt->sess_works_list)) {
+               struct qla_tgt_sess_work_param *prm = list_entry(
+                   tgt->sess_works_list.next, typeof(*prm),
+                   sess_works_list_entry);
+
+               /*
+                * This work can be scheduled on several CPUs at time, so we
+                * must delete the entry to eliminate double processing
+                */
+               list_del(&prm->sess_works_list_entry);
+
+               spin_unlock_irqrestore(&tgt->sess_work_lock, flags);
+
+               switch (prm->type) {
+               case QLA_TGT_SESS_WORK_ABORT:
+                       qlt_abort_work(tgt, prm);
+                       break;
+               case QLA_TGT_SESS_WORK_TM:
+                       qlt_tmr_work(tgt, prm);
+                       break;
+               default:
+                       BUG_ON(1);
+                       break;
+               }
+
+               spin_lock_irqsave(&tgt->sess_work_lock, flags);
+
+               kfree(prm);
+       }
+       spin_unlock_irqrestore(&tgt->sess_work_lock, flags);
+}
+
+/* Must be called under tgt_host_action_mutex */
+int qlt_add_target(struct qla_hw_data *ha, struct scsi_qla_host *base_vha)
+{
+       struct qla_tgt *tgt;
+
+       if (!QLA_TGT_MODE_ENABLED())
+               return 0;
+
+       ql_dbg(ql_dbg_tgt, base_vha, 0xe03b,
+           "Registering target for host %ld(%p)", base_vha->host_no, ha);
+
+       BUG_ON((ha->tgt.qla_tgt != NULL) || (ha->tgt.tgt_ops != NULL));
+
+       tgt = kzalloc(sizeof(struct qla_tgt), GFP_KERNEL);
+       if (!tgt) {
+               ql_dbg(ql_dbg_tgt, base_vha, 0xe066,
+                   "Unable to allocate struct qla_tgt\n");
+               return -ENOMEM;
+       }
+
+       if (!(base_vha->host->hostt->supported_mode & MODE_TARGET))
+               base_vha->host->hostt->supported_mode |= MODE_TARGET;
+
+       tgt->ha = ha;
+       tgt->vha = base_vha;
+       init_waitqueue_head(&tgt->waitQ);
+       INIT_LIST_HEAD(&tgt->sess_list);
+       INIT_LIST_HEAD(&tgt->del_sess_list);
+       INIT_DELAYED_WORK(&tgt->sess_del_work,
+               (void (*)(struct work_struct *))qlt_del_sess_work_fn);
+       spin_lock_init(&tgt->sess_work_lock);
+       INIT_WORK(&tgt->sess_work, qlt_sess_work_fn);
+       INIT_LIST_HEAD(&tgt->sess_works_list);
+       spin_lock_init(&tgt->srr_lock);
+       INIT_LIST_HEAD(&tgt->srr_ctio_list);
+       INIT_LIST_HEAD(&tgt->srr_imm_list);
+       INIT_WORK(&tgt->srr_work, qlt_handle_srr_work);
+       atomic_set(&tgt->tgt_global_resets_count, 0);
+
+       ha->tgt.qla_tgt = tgt;
+
+       ql_dbg(ql_dbg_tgt, base_vha, 0xe067,
+               "qla_target(%d): using 64 Bit PCI addressing",
+               base_vha->vp_idx);
+       tgt->tgt_enable_64bit_addr = 1;
+       /* 3 is reserved */
+       tgt->sg_tablesize = QLA_TGT_MAX_SG_24XX(base_vha->req->length - 3);
+       tgt->datasegs_per_cmd = QLA_TGT_DATASEGS_PER_CMD_24XX;
+       tgt->datasegs_per_cont = QLA_TGT_DATASEGS_PER_CONT_24XX;
+
+       mutex_lock(&qla_tgt_mutex);
+       list_add_tail(&tgt->tgt_list_entry, &qla_tgt_glist);
+       mutex_unlock(&qla_tgt_mutex);
+
+       return 0;
+}
+
+/* Must be called under tgt_host_action_mutex */
+int qlt_remove_target(struct qla_hw_data *ha, struct scsi_qla_host *vha)
+{
+       if (!ha->tgt.qla_tgt)
+               return 0;
+
+       mutex_lock(&qla_tgt_mutex);
+       list_del(&ha->tgt.qla_tgt->tgt_list_entry);
+       mutex_unlock(&qla_tgt_mutex);
+
+       ql_dbg(ql_dbg_tgt, vha, 0xe03c, "Unregistering target for host %ld(%p)",
+           vha->host_no, ha);
+       qlt_release(ha->tgt.qla_tgt);
+
+       return 0;
+}
+
+static void qlt_lport_dump(struct scsi_qla_host *vha, u64 wwpn,
+       unsigned char *b)
+{
+       int i;
+
+       pr_debug("qla2xxx HW vha->node_name: ");
+       for (i = 0; i < WWN_SIZE; i++)
+               pr_debug("%02x ", vha->node_name[i]);
+       pr_debug("\n");
+       pr_debug("qla2xxx HW vha->port_name: ");
+       for (i = 0; i < WWN_SIZE; i++)
+               pr_debug("%02x ", vha->port_name[i]);
+       pr_debug("\n");
+
+       pr_debug("qla2xxx passed configfs WWPN: ");
+       put_unaligned_be64(wwpn, b);
+       for (i = 0; i < WWN_SIZE; i++)
+               pr_debug("%02x ", b[i]);
+       pr_debug("\n");
+}
+
+/**
+ * qla_tgt_lport_register - register lport with external module
+ *
+ * @qla_tgt_ops: Pointer for tcm_qla2xxx qla_tgt_ops
+ * @wwpn: Passwd FC target WWPN
+ * @callback:  lport initialization callback for tcm_qla2xxx code
+ * @target_lport_ptr: pointer for tcm_qla2xxx specific lport data
+ */
+int qlt_lport_register(struct qla_tgt_func_tmpl *qla_tgt_ops, u64 wwpn,
+       int (*callback)(struct scsi_qla_host *), void *target_lport_ptr)
+{
+       struct qla_tgt *tgt;
+       struct scsi_qla_host *vha;
+       struct qla_hw_data *ha;
+       struct Scsi_Host *host;
+       unsigned long flags;
+       int rc;
+       u8 b[WWN_SIZE];
+
+       mutex_lock(&qla_tgt_mutex);
+       list_for_each_entry(tgt, &qla_tgt_glist, tgt_list_entry) {
+               vha = tgt->vha;
+               ha = vha->hw;
+
+               host = vha->host;
+               if (!host)
+                       continue;
+
+               if (ha->tgt.tgt_ops != NULL)
+                       continue;
+
+               if (!(host->hostt->supported_mode & MODE_TARGET))
+                       continue;
+
+               spin_lock_irqsave(&ha->hardware_lock, flags);
+               if (host->active_mode & MODE_TARGET) {
+                       pr_debug("MODE_TARGET already active on qla2xxx(%d)\n",
+                           host->host_no);
+                       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+                       continue;
+               }
+               spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+               if (!scsi_host_get(host)) {
+                       ql_dbg(ql_dbg_tgt, vha, 0xe068,
+                           "Unable to scsi_host_get() for"
+                           " qla2xxx scsi_host\n");
+                       continue;
+               }
+               qlt_lport_dump(vha, wwpn, b);
+
+               if (memcmp(vha->port_name, b, WWN_SIZE)) {
+                       scsi_host_put(host);
+                       continue;
+               }
+               /*
+                * Setup passed parameters ahead of invoking callback
+                */
+               ha->tgt.tgt_ops = qla_tgt_ops;
+               ha->tgt.target_lport_ptr = target_lport_ptr;
+               rc = (*callback)(vha);
+               if (rc != 0) {
+                       ha->tgt.tgt_ops = NULL;
+                       ha->tgt.target_lport_ptr = NULL;
+               }
+               mutex_unlock(&qla_tgt_mutex);
+               return rc;
+       }
+       mutex_unlock(&qla_tgt_mutex);
+
+       return -ENODEV;
+}
+EXPORT_SYMBOL(qlt_lport_register);
+
+/**
+ * qla_tgt_lport_deregister - Degister lport
+ *
+ * @vha:  Registered scsi_qla_host pointer
+ */
+void qlt_lport_deregister(struct scsi_qla_host *vha)
+{
+       struct qla_hw_data *ha = vha->hw;
+       struct Scsi_Host *sh = vha->host;
+       /*
+        * Clear the target_lport_ptr qla_target_template pointer in qla_hw_data
+        */
+       ha->tgt.target_lport_ptr = NULL;
+       ha->tgt.tgt_ops = NULL;
+       /*
+        * Release the Scsi_Host reference for the underlying qla2xxx host
+        */
+       scsi_host_put(sh);
+}
+EXPORT_SYMBOL(qlt_lport_deregister);
+
+/* Must be called under HW lock */
+void qlt_set_mode(struct scsi_qla_host *vha)
+{
+       struct qla_hw_data *ha = vha->hw;
+
+       switch (ql2x_ini_mode) {
+       case QLA2XXX_INI_MODE_DISABLED:
+       case QLA2XXX_INI_MODE_EXCLUSIVE:
+               vha->host->active_mode = MODE_TARGET;
+               break;
+       case QLA2XXX_INI_MODE_ENABLED:
+               vha->host->active_mode |= MODE_TARGET;
+               break;
+       default:
+               break;
+       }
+
+       if (ha->tgt.ini_mode_force_reverse)
+               qla_reverse_ini_mode(vha);
+}
+
+/* Must be called under HW lock */
+void qlt_clear_mode(struct scsi_qla_host *vha)
+{
+       struct qla_hw_data *ha = vha->hw;
+
+       switch (ql2x_ini_mode) {
+       case QLA2XXX_INI_MODE_DISABLED:
+               vha->host->active_mode = MODE_UNKNOWN;
+               break;
+       case QLA2XXX_INI_MODE_EXCLUSIVE:
+               vha->host->active_mode = MODE_INITIATOR;
+               break;
+       case QLA2XXX_INI_MODE_ENABLED:
+               vha->host->active_mode &= ~MODE_TARGET;
+               break;
+       default:
+               break;
+       }
+
+       if (ha->tgt.ini_mode_force_reverse)
+               qla_reverse_ini_mode(vha);
+}
+
+/*
+ * qla_tgt_enable_vha - NO LOCK HELD
+ *
+ * host_reset, bring up w/ Target Mode Enabled
+ */
+void
+qlt_enable_vha(struct scsi_qla_host *vha)
+{
+       struct qla_hw_data *ha = vha->hw;
+       struct qla_tgt *tgt = ha->tgt.qla_tgt;
+       unsigned long flags;
+
+       if (!tgt) {
+               ql_dbg(ql_dbg_tgt, vha, 0xe069,
+                   "Unable to locate qla_tgt pointer from"
+                   " struct qla_hw_data\n");
+               dump_stack();
+               return;
+       }
+
+       spin_lock_irqsave(&ha->hardware_lock, flags);
+       tgt->tgt_stopped = 0;
+       qlt_set_mode(vha);
+       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+       set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
+       qla2xxx_wake_dpc(vha);
+       qla2x00_wait_for_hba_online(vha);
+}
+EXPORT_SYMBOL(qlt_enable_vha);
+
+/*
+ * qla_tgt_disable_vha - NO LOCK HELD
+ *
+ * Disable Target Mode and reset the adapter
+ */
+void
+qlt_disable_vha(struct scsi_qla_host *vha)
+{
+       struct qla_hw_data *ha = vha->hw;
+       struct qla_tgt *tgt = ha->tgt.qla_tgt;
+       unsigned long flags;
+
+       if (!tgt) {
+               ql_dbg(ql_dbg_tgt, vha, 0xe06a,
+                   "Unable to locate qla_tgt pointer from"
+                   " struct qla_hw_data\n");
+               dump_stack();
+               return;
+       }
+
+       spin_lock_irqsave(&ha->hardware_lock, flags);
+       qlt_clear_mode(vha);
+       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+       set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
+       qla2xxx_wake_dpc(vha);
+       qla2x00_wait_for_hba_online(vha);
+}
+
+/*
+ * Called from qla_init.c:qla24xx_vport_create() contex to setup
+ * the target mode specific struct scsi_qla_host and struct qla_hw_data
+ * members.
+ */
+void
+qlt_vport_create(struct scsi_qla_host *vha, struct qla_hw_data *ha)
+{
+       if (!qla_tgt_mode_enabled(vha))
+               return;
+
+       mutex_init(&ha->tgt.tgt_mutex);
+       mutex_init(&ha->tgt.tgt_host_action_mutex);
+
+       qlt_clear_mode(vha);
+
+       /*
+        * NOTE: Currently the value is kept the same for <24xx and
+        * >=24xx ISPs. If it is necessary to change it,
+        * the check should be added for specific ISPs,
+        * assigning the value appropriately.
+        */
+       ha->tgt.atio_q_length = ATIO_ENTRY_CNT_24XX;
+}
+
+void
+qlt_rff_id(struct scsi_qla_host *vha, struct ct_sns_req *ct_req)
+{
+       /*
+        * FC-4 Feature bit 0 indicates target functionality to the name server.
+        */
+       if (qla_tgt_mode_enabled(vha)) {
+               if (qla_ini_mode_enabled(vha))
+                       ct_req->req.rff_id.fc4_feature = BIT_0 | BIT_1;
+               else
+                       ct_req->req.rff_id.fc4_feature = BIT_0;
+       } else if (qla_ini_mode_enabled(vha)) {
+               ct_req->req.rff_id.fc4_feature = BIT_1;
+       }
+}
+
+/*
+ * qlt_init_atio_q_entries() - Initializes ATIO queue entries.
+ * @ha: HA context
+ *
+ * Beginning of ATIO ring has initialization control block already built
+ * by nvram config routine.
+ *
+ * Returns 0 on success.
+ */
+void
+qlt_init_atio_q_entries(struct scsi_qla_host *vha)
+{
+       struct qla_hw_data *ha = vha->hw;
+       uint16_t cnt;
+       struct atio_from_isp *pkt = (struct atio_from_isp *)ha->tgt.atio_ring;
+
+       if (!qla_tgt_mode_enabled(vha))
+               return;
+
+       for (cnt = 0; cnt < ha->tgt.atio_q_length; cnt++) {
+               pkt->u.raw.signature = ATIO_PROCESSED;
+               pkt++;
+       }
+
+}
+
+/*
+ * qlt_24xx_process_atio_queue() - Process ATIO queue entries.
+ * @ha: SCSI driver HA context
+ */
+void
+qlt_24xx_process_atio_queue(struct scsi_qla_host *vha)
+{
+       struct qla_hw_data *ha = vha->hw;
+       struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
+       struct atio_from_isp *pkt;
+       int cnt, i;
+
+       if (!vha->flags.online)
+               return;
+
+       while (ha->tgt.atio_ring_ptr->signature != ATIO_PROCESSED) {
+               pkt = (struct atio_from_isp *)ha->tgt.atio_ring_ptr;
+               cnt = pkt->u.raw.entry_count;
+
+               qlt_24xx_atio_pkt_all_vps(vha, (struct atio_from_isp *)pkt);
+
+               for (i = 0; i < cnt; i++) {
+                       ha->tgt.atio_ring_index++;
+                       if (ha->tgt.atio_ring_index == ha->tgt.atio_q_length) {
+                               ha->tgt.atio_ring_index = 0;
+                               ha->tgt.atio_ring_ptr = ha->tgt.atio_ring;
+                       } else
+                               ha->tgt.atio_ring_ptr++;
+
+                       pkt->u.raw.signature = ATIO_PROCESSED;
+                       pkt = (struct atio_from_isp *)ha->tgt.atio_ring_ptr;
+               }
+               wmb();
+       }
+
+       /* Adjust ring index */
+       WRT_REG_DWORD(&reg->atio_q_out, ha->tgt.atio_ring_index);
+}
+
+void
+qlt_24xx_config_rings(struct scsi_qla_host *vha, device_reg_t __iomem *reg)
+{
+       struct qla_hw_data *ha = vha->hw;
+
+/* FIXME: atio_q in/out for ha->mqenable=1..? */
+       if (ha->mqenable) {
+#if 0
+               WRT_REG_DWORD(&reg->isp25mq.atio_q_in, 0);
+               WRT_REG_DWORD(&reg->isp25mq.atio_q_out, 0);
+               RD_REG_DWORD(&reg->isp25mq.atio_q_out);
+#endif
+       } else {
+               /* Setup APTIO registers for target mode */
+               WRT_REG_DWORD(&reg->isp24.atio_q_in, 0);
+               WRT_REG_DWORD(&reg->isp24.atio_q_out, 0);
+               RD_REG_DWORD(&reg->isp24.atio_q_out);
+       }
+}
+
+void
+qlt_24xx_config_nvram_stage1(struct scsi_qla_host *vha, struct nvram_24xx *nv)
+{
+       struct qla_hw_data *ha = vha->hw;
+
+       if (qla_tgt_mode_enabled(vha)) {
+               if (!ha->tgt.saved_set) {
+                       /* We save only once */
+                       ha->tgt.saved_exchange_count = nv->exchange_count;
+                       ha->tgt.saved_firmware_options_1 =
+                           nv->firmware_options_1;
+                       ha->tgt.saved_firmware_options_2 =
+                           nv->firmware_options_2;
+                       ha->tgt.saved_firmware_options_3 =
+                           nv->firmware_options_3;
+                       ha->tgt.saved_set = 1;
+               }
+
+               nv->exchange_count = __constant_cpu_to_le16(0xFFFF);
+
+               /* Enable target mode */
+               nv->firmware_options_1 |= __constant_cpu_to_le32(BIT_4);
+
+               /* Disable ini mode, if requested */
+               if (!qla_ini_mode_enabled(vha))
+                       nv->firmware_options_1 |= __constant_cpu_to_le32(BIT_5);
+
+               /* Disable Full Login after LIP */
+               nv->firmware_options_1 &= __constant_cpu_to_le32(~BIT_13);
+               /* Enable initial LIP */
+               nv->firmware_options_1 &= __constant_cpu_to_le32(~BIT_9);
+               /* Enable FC tapes support */
+               nv->firmware_options_2 |= __constant_cpu_to_le32(BIT_12);
+               /* Disable Full Login after LIP */
+               nv->host_p &= __constant_cpu_to_le32(~BIT_10);
+               /* Enable target PRLI control */
+               nv->firmware_options_2 |= __constant_cpu_to_le32(BIT_14);
+       } else {
+               if (ha->tgt.saved_set) {
+                       nv->exchange_count = ha->tgt.saved_exchange_count;
+                       nv->firmware_options_1 =
+                           ha->tgt.saved_firmware_options_1;
+                       nv->firmware_options_2 =
+                           ha->tgt.saved_firmware_options_2;
+                       nv->firmware_options_3 =
+                           ha->tgt.saved_firmware_options_3;
+               }
+               return;
+       }
+
+       /* out-of-order frames reassembly */
+       nv->firmware_options_3 |= BIT_6|BIT_9;
+
+       if (ha->tgt.enable_class_2) {
+               if (vha->flags.init_done)
+                       fc_host_supported_classes(vha->host) =
+                               FC_COS_CLASS2 | FC_COS_CLASS3;
+
+               nv->firmware_options_2 |= __constant_cpu_to_le32(BIT_8);
+       } else {
+               if (vha->flags.init_done)
+                       fc_host_supported_classes(vha->host) = FC_COS_CLASS3;
+
+               nv->firmware_options_2 &= ~__constant_cpu_to_le32(BIT_8);
+       }
+}
+
+void
+qlt_24xx_config_nvram_stage2(struct scsi_qla_host *vha,
+       struct init_cb_24xx *icb)
+{
+       struct qla_hw_data *ha = vha->hw;
+
+       if (ha->tgt.node_name_set) {
+               memcpy(icb->node_name, ha->tgt.tgt_node_name, WWN_SIZE);
+               icb->firmware_options_1 |= __constant_cpu_to_le32(BIT_14);
+       }
+}
+
+int
+qlt_24xx_process_response_error(struct scsi_qla_host *vha,
+       struct sts_entry_24xx *pkt)
+{
+       switch (pkt->entry_type) {
+       case ABTS_RECV_24XX:
+       case ABTS_RESP_24XX:
+       case CTIO_TYPE7:
+       case NOTIFY_ACK_TYPE:
+               return 1;
+       default:
+               return 0;
+       }
+}
+
+void
+qlt_modify_vp_config(struct scsi_qla_host *vha,
+       struct vp_config_entry_24xx *vpmod)
+{
+       if (qla_tgt_mode_enabled(vha))
+               vpmod->options_idx1 &= ~BIT_5;
+       /* Disable ini mode, if requested */
+       if (!qla_ini_mode_enabled(vha))
+               vpmod->options_idx1 &= ~BIT_4;
+}
+
+void
+qlt_probe_one_stage1(struct scsi_qla_host *base_vha, struct qla_hw_data *ha)
+{
+       if (!QLA_TGT_MODE_ENABLED())
+               return;
+
+       mutex_init(&ha->tgt.tgt_mutex);
+       mutex_init(&ha->tgt.tgt_host_action_mutex);
+       qlt_clear_mode(base_vha);
+}
+
+int
+qlt_mem_alloc(struct qla_hw_data *ha)
+{
+       if (!QLA_TGT_MODE_ENABLED())
+               return 0;
+
+       ha->tgt.tgt_vp_map = kzalloc(sizeof(struct qla_tgt_vp_map) *
+           MAX_MULTI_ID_FABRIC, GFP_KERNEL);
+       if (!ha->tgt.tgt_vp_map)
+               return -ENOMEM;
+
+       ha->tgt.atio_ring = dma_alloc_coherent(&ha->pdev->dev,
+           (ha->tgt.atio_q_length + 1) * sizeof(struct atio_from_isp),
+           &ha->tgt.atio_dma, GFP_KERNEL);
+       if (!ha->tgt.atio_ring) {
+               kfree(ha->tgt.tgt_vp_map);
+               return -ENOMEM;
+       }
+       return 0;
+}
+
+void
+qlt_mem_free(struct qla_hw_data *ha)
+{
+       if (!QLA_TGT_MODE_ENABLED())
+               return;
+
+       if (ha->tgt.atio_ring) {
+               dma_free_coherent(&ha->pdev->dev, (ha->tgt.atio_q_length + 1) *
+                   sizeof(struct atio_from_isp), ha->tgt.atio_ring,
+                   ha->tgt.atio_dma);
+       }
+       kfree(ha->tgt.tgt_vp_map);
+}
+
+/* vport_slock to be held by the caller */
+void
+qlt_update_vp_map(struct scsi_qla_host *vha, int cmd)
+{
+       if (!QLA_TGT_MODE_ENABLED())
+               return;
+
+       switch (cmd) {
+       case SET_VP_IDX:
+               vha->hw->tgt.tgt_vp_map[vha->vp_idx].vha = vha;
+               break;
+       case SET_AL_PA:
+               vha->hw->tgt.tgt_vp_map[vha->d_id.b.al_pa].idx = vha->vp_idx;
+               break;
+       case RESET_VP_IDX:
+               vha->hw->tgt.tgt_vp_map[vha->vp_idx].vha = NULL;
+               break;
+       case RESET_AL_PA:
+               vha->hw->tgt.tgt_vp_map[vha->d_id.b.al_pa].idx = 0;
+               break;
+       }
+}
+
+static int __init qlt_parse_ini_mode(void)
+{
+       if (strcasecmp(qlini_mode, QLA2XXX_INI_MODE_STR_EXCLUSIVE) == 0)
+               ql2x_ini_mode = QLA2XXX_INI_MODE_EXCLUSIVE;
+       else if (strcasecmp(qlini_mode, QLA2XXX_INI_MODE_STR_DISABLED) == 0)
+               ql2x_ini_mode = QLA2XXX_INI_MODE_DISABLED;
+       else if (strcasecmp(qlini_mode, QLA2XXX_INI_MODE_STR_ENABLED) == 0)
+               ql2x_ini_mode = QLA2XXX_INI_MODE_ENABLED;
+       else
+               return false;
+
+       return true;
+}
+
+int __init qlt_init(void)
+{
+       int ret;
+
+       if (!qlt_parse_ini_mode()) {
+               ql_log(ql_log_fatal, NULL, 0xe06b,
+                   "qlt_parse_ini_mode() failed\n");
+               return -EINVAL;
+       }
+
+       if (!QLA_TGT_MODE_ENABLED())
+               return 0;
+
+       qla_tgt_cmd_cachep = kmem_cache_create("qla_tgt_cmd_cachep",
+           sizeof(struct qla_tgt_cmd), __alignof__(struct qla_tgt_cmd), 0,
+           NULL);
+       if (!qla_tgt_cmd_cachep) {
+               ql_log(ql_log_fatal, NULL, 0xe06c,
+                   "kmem_cache_create for qla_tgt_cmd_cachep failed\n");
+               return -ENOMEM;
+       }
+
+       qla_tgt_mgmt_cmd_cachep = kmem_cache_create("qla_tgt_mgmt_cmd_cachep",
+           sizeof(struct qla_tgt_mgmt_cmd), __alignof__(struct
+           qla_tgt_mgmt_cmd), 0, NULL);
+       if (!qla_tgt_mgmt_cmd_cachep) {
+               ql_log(ql_log_fatal, NULL, 0xe06d,
+                   "kmem_cache_create for qla_tgt_mgmt_cmd_cachep failed\n");
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       qla_tgt_mgmt_cmd_mempool = mempool_create(25, mempool_alloc_slab,
+           mempool_free_slab, qla_tgt_mgmt_cmd_cachep);
+       if (!qla_tgt_mgmt_cmd_mempool) {
+               ql_log(ql_log_fatal, NULL, 0xe06e,
+                   "mempool_create for qla_tgt_mgmt_cmd_mempool failed\n");
+               ret = -ENOMEM;
+               goto out_mgmt_cmd_cachep;
+       }
+
+       qla_tgt_wq = alloc_workqueue("qla_tgt_wq", 0, 0);
+       if (!qla_tgt_wq) {
+               ql_log(ql_log_fatal, NULL, 0xe06f,
+                   "alloc_workqueue for qla_tgt_wq failed\n");
+               ret = -ENOMEM;
+               goto out_cmd_mempool;
+       }
+       /*
+        * Return 1 to signal that initiator-mode is being disabled
+        */
+       return (ql2x_ini_mode == QLA2XXX_INI_MODE_DISABLED) ? 1 : 0;
+
+out_cmd_mempool:
+       mempool_destroy(qla_tgt_mgmt_cmd_mempool);
+out_mgmt_cmd_cachep:
+       kmem_cache_destroy(qla_tgt_mgmt_cmd_cachep);
+out:
+       kmem_cache_destroy(qla_tgt_cmd_cachep);
+       return ret;
+}
+
+void qlt_exit(void)
+{
+       if (!QLA_TGT_MODE_ENABLED())
+               return;
+
+       destroy_workqueue(qla_tgt_wq);
+       mempool_destroy(qla_tgt_mgmt_cmd_mempool);
+       kmem_cache_destroy(qla_tgt_mgmt_cmd_cachep);
+       kmem_cache_destroy(qla_tgt_cmd_cachep);
+}
diff --git a/drivers/scsi/qla2xxx/qla_target.h b/drivers/scsi/qla2xxx/qla_target.h
new file mode 100644 (file)
index 0000000..9ec19bc
--- /dev/null
@@ -0,0 +1,1005 @@
+/*
+ *  Copyright (C) 2004 - 2010 Vladislav Bolkhovitin <vst@vlnb.net>
+ *  Copyright (C) 2004 - 2005 Leonid Stoljar
+ *  Copyright (C) 2006 Nathaniel Clark <nate@misrule.us>
+ *  Copyright (C) 2007 - 2010 ID7 Ltd.
+ *
+ *  Forward port and refactoring to modern qla2xxx and target/configfs
+ *
+ *  Copyright (C) 2010-2011 Nicholas A. Bellinger <nab@kernel.org>
+ *
+ *  Additional file for the target driver support.
+ *
+ *  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.
+ */
+/*
+ * This is the global def file that is useful for including from the
+ * target portion.
+ */
+
+#ifndef __QLA_TARGET_H
+#define __QLA_TARGET_H
+
+#include "qla_def.h"
+
+/*
+ * Must be changed on any change in any initiator visible interfaces or
+ * data in the target add-on
+ */
+#define QLA2XXX_TARGET_MAGIC   269
+
+/*
+ * Must be changed on any change in any target visible interfaces or
+ * data in the initiator
+ */
+#define QLA2XXX_INITIATOR_MAGIC   57222
+
+#define QLA2XXX_INI_MODE_STR_EXCLUSIVE "exclusive"
+#define QLA2XXX_INI_MODE_STR_DISABLED  "disabled"
+#define QLA2XXX_INI_MODE_STR_ENABLED   "enabled"
+
+#define QLA2XXX_INI_MODE_EXCLUSIVE     0
+#define QLA2XXX_INI_MODE_DISABLED      1
+#define QLA2XXX_INI_MODE_ENABLED       2
+
+#define QLA2XXX_COMMAND_COUNT_INIT     250
+#define QLA2XXX_IMMED_NOTIFY_COUNT_INIT 250
+
+/*
+ * Used to mark which completion handles (for RIO Status's) are for CTIO's
+ * vs. regular (non-target) info. This is checked for in
+ * qla2x00_process_response_queue() to see if a handle coming back in a
+ * multi-complete should come to the tgt driver or be handled there by qla2xxx
+ */
+#define CTIO_COMPLETION_HANDLE_MARK    BIT_29
+#if (CTIO_COMPLETION_HANDLE_MARK <= MAX_OUTSTANDING_COMMANDS)
+#error "CTIO_COMPLETION_HANDLE_MARK not larger than MAX_OUTSTANDING_COMMANDS"
+#endif
+#define HANDLE_IS_CTIO_COMP(h) (h & CTIO_COMPLETION_HANDLE_MARK)
+
+/* Used to mark CTIO as intermediate */
+#define CTIO_INTERMEDIATE_HANDLE_MARK  BIT_30
+
+#ifndef OF_SS_MODE_0
+/*
+ * ISP target entries - Flags bit definitions.
+ */
+#define OF_SS_MODE_0        0
+#define OF_SS_MODE_1        1
+#define OF_SS_MODE_2        2
+#define OF_SS_MODE_3        3
+
+#define OF_EXPL_CONF        BIT_5       /* Explicit Confirmation Requested */
+#define OF_DATA_IN          BIT_6       /* Data in to initiator */
+                                       /*  (data from target to initiator) */
+#define OF_DATA_OUT         BIT_7       /* Data out from initiator */
+                                       /*  (data from initiator to target) */
+#define OF_NO_DATA          (BIT_7 | BIT_6)
+#define OF_INC_RC           BIT_8       /* Increment command resource count */
+#define OF_FAST_POST        BIT_9       /* Enable mailbox fast posting. */
+#define OF_CONF_REQ         BIT_13      /* Confirmation Requested */
+#define OF_TERM_EXCH        BIT_14      /* Terminate exchange */
+#define OF_SSTS             BIT_15      /* Send SCSI status */
+#endif
+
+#ifndef QLA_TGT_DATASEGS_PER_CMD32
+#define QLA_TGT_DATASEGS_PER_CMD32     3
+#define QLA_TGT_DATASEGS_PER_CONT32    7
+#define QLA_TGT_MAX_SG32(ql) \
+       (((ql) > 0) ? (QLA_TGT_DATASEGS_PER_CMD32 + \
+               QLA_TGT_DATASEGS_PER_CONT32*((ql) - 1)) : 0)
+
+#define QLA_TGT_DATASEGS_PER_CMD64     2
+#define QLA_TGT_DATASEGS_PER_CONT64    5
+#define QLA_TGT_MAX_SG64(ql) \
+       (((ql) > 0) ? (QLA_TGT_DATASEGS_PER_CMD64 + \
+               QLA_TGT_DATASEGS_PER_CONT64*((ql) - 1)) : 0)
+#endif
+
+#ifndef QLA_TGT_DATASEGS_PER_CMD_24XX
+#define QLA_TGT_DATASEGS_PER_CMD_24XX  1
+#define QLA_TGT_DATASEGS_PER_CONT_24XX 5
+#define QLA_TGT_MAX_SG_24XX(ql) \
+       (min(1270, ((ql) > 0) ? (QLA_TGT_DATASEGS_PER_CMD_24XX + \
+               QLA_TGT_DATASEGS_PER_CONT_24XX*((ql) - 1)) : 0))
+#endif
+#endif
+
+#define GET_TARGET_ID(ha, iocb) ((HAS_EXTENDED_IDS(ha))                        \
+                        ? le16_to_cpu((iocb)->u.isp2x.target.extended) \
+                        : (uint16_t)(iocb)->u.isp2x.target.id.standard)
+
+#ifndef IMMED_NOTIFY_TYPE
+#define IMMED_NOTIFY_TYPE 0x0D         /* Immediate notify entry. */
+/*
+ * ISP queue - immediate notify entry structure definition.
+ *             This is sent by the ISP to the Target driver.
+ *             This IOCB would have report of events sent by the
+ *             initiator, that needs to be handled by the target
+ *             driver immediately.
+ */
+struct imm_ntfy_from_isp {
+       uint8_t  entry_type;                /* Entry type. */
+       uint8_t  entry_count;               /* Entry count. */
+       uint8_t  sys_define;                /* System defined. */
+       uint8_t  entry_status;              /* Entry Status. */
+       union {
+               struct {
+                       uint32_t sys_define_2; /* System defined. */
+                       target_id_t target;
+                       uint16_t lun;
+                       uint8_t  target_id;
+                       uint8_t  reserved_1;
+                       uint16_t status_modifier;
+                       uint16_t status;
+                       uint16_t task_flags;
+                       uint16_t seq_id;
+                       uint16_t srr_rx_id;
+                       uint32_t srr_rel_offs;
+                       uint16_t srr_ui;
+#define SRR_IU_DATA_IN 0x1
+#define SRR_IU_DATA_OUT        0x5
+#define SRR_IU_STATUS  0x7
+                       uint16_t srr_ox_id;
+                       uint8_t reserved_2[28];
+               } isp2x;
+               struct {
+                       uint32_t reserved;
+                       uint16_t nport_handle;
+                       uint16_t reserved_2;
+                       uint16_t flags;
+#define NOTIFY24XX_FLAGS_GLOBAL_TPRLO   BIT_1
+#define NOTIFY24XX_FLAGS_PUREX_IOCB     BIT_0
+                       uint16_t srr_rx_id;
+                       uint16_t status;
+                       uint8_t  status_subcode;
+                       uint8_t  reserved_3;
+                       uint32_t exchange_address;
+                       uint32_t srr_rel_offs;
+                       uint16_t srr_ui;
+                       uint16_t srr_ox_id;
+                       uint8_t  reserved_4[19];
+                       uint8_t  vp_index;
+                       uint32_t reserved_5;
+                       uint8_t  port_id[3];
+                       uint8_t  reserved_6;
+               } isp24;
+       } u;
+       uint16_t reserved_7;
+       uint16_t ox_id;
+} __packed;
+#endif
+
+#ifndef NOTIFY_ACK_TYPE
+#define NOTIFY_ACK_TYPE 0x0E     /* Notify acknowledge entry. */
+/*
+ * ISP queue - notify acknowledge entry structure definition.
+ *             This is sent to the ISP from the target driver.
+ */
+struct nack_to_isp {
+       uint8_t  entry_type;                /* Entry type. */
+       uint8_t  entry_count;               /* Entry count. */
+       uint8_t  sys_define;                /* System defined. */
+       uint8_t  entry_status;              /* Entry Status. */
+       union {
+               struct {
+                       uint32_t sys_define_2; /* System defined. */
+                       target_id_t target;
+                       uint8_t  target_id;
+                       uint8_t  reserved_1;
+                       uint16_t flags;
+                       uint16_t resp_code;
+                       uint16_t status;
+                       uint16_t task_flags;
+                       uint16_t seq_id;
+                       uint16_t srr_rx_id;
+                       uint32_t srr_rel_offs;
+                       uint16_t srr_ui;
+                       uint16_t srr_flags;
+                       uint16_t srr_reject_code;
+                       uint8_t  srr_reject_vendor_uniq;
+                       uint8_t  srr_reject_code_expl;
+                       uint8_t  reserved_2[24];
+               } isp2x;
+               struct {
+                       uint32_t handle;
+                       uint16_t nport_handle;
+                       uint16_t reserved_1;
+                       uint16_t flags;
+                       uint16_t srr_rx_id;
+                       uint16_t status;
+                       uint8_t  status_subcode;
+                       uint8_t  reserved_3;
+                       uint32_t exchange_address;
+                       uint32_t srr_rel_offs;
+                       uint16_t srr_ui;
+                       uint16_t srr_flags;
+                       uint8_t  reserved_4[19];
+                       uint8_t  vp_index;
+                       uint8_t  srr_reject_vendor_uniq;
+                       uint8_t  srr_reject_code_expl;
+                       uint8_t  srr_reject_code;
+                       uint8_t  reserved_5[5];
+               } isp24;
+       } u;
+       uint8_t  reserved[2];
+       uint16_t ox_id;
+} __packed;
+#define NOTIFY_ACK_SRR_FLAGS_ACCEPT    0
+#define NOTIFY_ACK_SRR_FLAGS_REJECT    1
+
+#define NOTIFY_ACK_SRR_REJECT_REASON_UNABLE_TO_PERFORM 0x9
+
+#define NOTIFY_ACK_SRR_FLAGS_REJECT_EXPL_NO_EXPL               0
+#define NOTIFY_ACK_SRR_FLAGS_REJECT_EXPL_UNABLE_TO_SUPPLY_DATA 0x2a
+
+#define NOTIFY_ACK_SUCCESS      0x01
+#endif
+
+#ifndef ACCEPT_TGT_IO_TYPE
+#define ACCEPT_TGT_IO_TYPE 0x16 /* Accept target I/O entry. */
+#endif
+
+#ifndef CONTINUE_TGT_IO_TYPE
+#define CONTINUE_TGT_IO_TYPE 0x17
+/*
+ * ISP queue - Continue Target I/O (CTIO) entry for status mode 0 structure.
+ *             This structure is sent to the ISP 2xxx from target driver.
+ */
+struct ctio_to_2xxx {
+       uint8_t  entry_type;            /* Entry type. */
+       uint8_t  entry_count;           /* Entry count. */
+       uint8_t  sys_define;            /* System defined. */
+       uint8_t  entry_status;          /* Entry Status. */
+       uint32_t handle;                /* System defined handle */
+       target_id_t target;
+       uint16_t rx_id;
+       uint16_t flags;
+       uint16_t status;
+       uint16_t timeout;               /* 0 = 30 seconds, 0xFFFF = disable */
+       uint16_t dseg_count;            /* Data segment count. */
+       uint32_t relative_offset;
+       uint32_t residual;
+       uint16_t reserved_1[3];
+       uint16_t scsi_status;
+       uint32_t transfer_length;
+       uint32_t dseg_0_address;        /* Data segment 0 address. */
+       uint32_t dseg_0_length;         /* Data segment 0 length. */
+       uint32_t dseg_1_address;        /* Data segment 1 address. */
+       uint32_t dseg_1_length;         /* Data segment 1 length. */
+       uint32_t dseg_2_address;        /* Data segment 2 address. */
+       uint32_t dseg_2_length;         /* Data segment 2 length. */
+} __packed;
+#define ATIO_PATH_INVALID       0x07
+#define ATIO_CANT_PROV_CAP      0x16
+#define ATIO_CDB_VALID          0x3D
+
+#define ATIO_EXEC_READ          BIT_1
+#define ATIO_EXEC_WRITE         BIT_0
+#endif
+
+#ifndef CTIO_A64_TYPE
+#define CTIO_A64_TYPE 0x1F
+#define CTIO_SUCCESS                   0x01
+#define CTIO_ABORTED                   0x02
+#define CTIO_INVALID_RX_ID             0x08
+#define CTIO_TIMEOUT                   0x0B
+#define CTIO_LIP_RESET                 0x0E
+#define CTIO_TARGET_RESET              0x17
+#define CTIO_PORT_UNAVAILABLE          0x28
+#define CTIO_PORT_LOGGED_OUT           0x29
+#define CTIO_PORT_CONF_CHANGED         0x2A
+#define CTIO_SRR_RECEIVED              0x45
+#endif
+
+#ifndef CTIO_RET_TYPE
+#define CTIO_RET_TYPE  0x17            /* CTIO return entry */
+#define ATIO_TYPE7 0x06 /* Accept target I/O entry for 24xx */
+
+struct fcp_hdr {
+       uint8_t  r_ctl;
+       uint8_t  d_id[3];
+       uint8_t  cs_ctl;
+       uint8_t  s_id[3];
+       uint8_t  type;
+       uint8_t  f_ctl[3];
+       uint8_t  seq_id;
+       uint8_t  df_ctl;
+       uint16_t seq_cnt;
+       uint16_t ox_id;
+       uint16_t rx_id;
+       uint32_t parameter;
+} __packed;
+
+struct fcp_hdr_le {
+       uint8_t  d_id[3];
+       uint8_t  r_ctl;
+       uint8_t  s_id[3];
+       uint8_t  cs_ctl;
+       uint8_t  f_ctl[3];
+       uint8_t  type;
+       uint16_t seq_cnt;
+       uint8_t  df_ctl;
+       uint8_t  seq_id;
+       uint16_t rx_id;
+       uint16_t ox_id;
+       uint32_t parameter;
+} __packed;
+
+#define F_CTL_EXCH_CONTEXT_RESP        BIT_23
+#define F_CTL_SEQ_CONTEXT_RESIP        BIT_22
+#define F_CTL_LAST_SEQ         BIT_20
+#define F_CTL_END_SEQ          BIT_19
+#define F_CTL_SEQ_INITIATIVE   BIT_16
+
+#define R_CTL_BASIC_LINK_SERV  0x80
+#define R_CTL_B_ACC            0x4
+#define R_CTL_B_RJT            0x5
+
+struct atio7_fcp_cmnd {
+       uint64_t lun;
+       uint8_t  cmnd_ref;
+       uint8_t  task_attr:3;
+       uint8_t  reserved:5;
+       uint8_t  task_mgmt_flags;
+#define FCP_CMND_TASK_MGMT_CLEAR_ACA           6
+#define FCP_CMND_TASK_MGMT_TARGET_RESET                5
+#define FCP_CMND_TASK_MGMT_LU_RESET            4
+#define FCP_CMND_TASK_MGMT_CLEAR_TASK_SET      2
+#define FCP_CMND_TASK_MGMT_ABORT_TASK_SET      1
+       uint8_t  wrdata:1;
+       uint8_t  rddata:1;
+       uint8_t  add_cdb_len:6;
+       uint8_t  cdb[16];
+       /*
+        * add_cdb is optional and can absent from struct atio7_fcp_cmnd. Size 4
+        * only to make sizeof(struct atio7_fcp_cmnd) be as expected by
+        * BUILD_BUG_ON in qlt_init().
+        */
+       uint8_t  add_cdb[4];
+       /* uint32_t data_length; */
+} __packed;
+
+/*
+ * ISP queue - Accept Target I/O (ATIO) type entry IOCB structure.
+ *             This is sent from the ISP to the target driver.
+ */
+struct atio_from_isp {
+       union {
+               struct {
+                       uint16_t entry_hdr;
+                       uint8_t  sys_define;   /* System defined. */
+                       uint8_t  entry_status; /* Entry Status.   */
+                       uint32_t sys_define_2; /* System defined. */
+                       target_id_t target;
+                       uint16_t rx_id;
+                       uint16_t flags;
+                       uint16_t status;
+                       uint8_t  command_ref;
+                       uint8_t  task_codes;
+                       uint8_t  task_flags;
+                       uint8_t  execution_codes;
+                       uint8_t  cdb[MAX_CMDSZ];
+                       uint32_t data_length;
+                       uint16_t lun;
+                       uint8_t  initiator_port_name[WWN_SIZE]; /* on qla23xx */
+                       uint16_t reserved_32[6];
+                       uint16_t ox_id;
+               } isp2x;
+               struct {
+                       uint16_t entry_hdr;
+                       uint8_t  fcp_cmnd_len_low;
+                       uint8_t  fcp_cmnd_len_high:4;
+                       uint8_t  attr:4;
+                       uint32_t exchange_addr;
+#define ATIO_EXCHANGE_ADDRESS_UNKNOWN  0xFFFFFFFF
+                       struct fcp_hdr fcp_hdr;
+                       struct atio7_fcp_cmnd fcp_cmnd;
+               } isp24;
+               struct {
+                       uint8_t  entry_type;    /* Entry type. */
+                       uint8_t  entry_count;   /* Entry count. */
+                       uint8_t  data[58];
+                       uint32_t signature;
+#define ATIO_PROCESSED 0xDEADDEAD              /* Signature */
+               } raw;
+       } u;
+} __packed;
+
+#define CTIO_TYPE7 0x12 /* Continue target I/O entry (for 24xx) */
+
+/*
+ * ISP queue - Continue Target I/O (ATIO) type 7 entry (for 24xx) structure.
+ *             This structure is sent to the ISP 24xx from the target driver.
+ */
+
+struct ctio7_to_24xx {
+       uint8_t  entry_type;                /* Entry type. */
+       uint8_t  entry_count;               /* Entry count. */
+       uint8_t  sys_define;                /* System defined. */
+       uint8_t  entry_status;              /* Entry Status. */
+       uint32_t handle;                    /* System defined handle */
+       uint16_t nport_handle;
+#define CTIO7_NHANDLE_UNRECOGNIZED     0xFFFF
+       uint16_t timeout;
+       uint16_t dseg_count;                /* Data segment count. */
+       uint8_t  vp_index;
+       uint8_t  add_flags;
+       uint8_t  initiator_id[3];
+       uint8_t  reserved;
+       uint32_t exchange_addr;
+       union {
+               struct {
+                       uint16_t reserved1;
+                       uint16_t flags;
+                       uint32_t residual;
+                       uint16_t ox_id;
+                       uint16_t scsi_status;
+                       uint32_t relative_offset;
+                       uint32_t reserved2;
+                       uint32_t transfer_length;
+                       uint32_t reserved3;
+                       /* Data segment 0 address. */
+                       uint32_t dseg_0_address[2];
+                       /* Data segment 0 length. */
+                       uint32_t dseg_0_length;
+               } status0;
+               struct {
+                       uint16_t sense_length;
+                       uint16_t flags;
+                       uint32_t residual;
+                       uint16_t ox_id;
+                       uint16_t scsi_status;
+                       uint16_t response_len;
+                       uint16_t reserved;
+                       uint8_t sense_data[24];
+               } status1;
+       } u;
+} __packed;
+
+/*
+ * ISP queue - CTIO type 7 from ISP 24xx to target driver
+ * returned entry structure.
+ */
+struct ctio7_from_24xx {
+       uint8_t  entry_type;                /* Entry type. */
+       uint8_t  entry_count;               /* Entry count. */
+       uint8_t  sys_define;                /* System defined. */
+       uint8_t  entry_status;              /* Entry Status. */
+       uint32_t handle;                    /* System defined handle */
+       uint16_t status;
+       uint16_t timeout;
+       uint16_t dseg_count;                /* Data segment count. */
+       uint8_t  vp_index;
+       uint8_t  reserved1[5];
+       uint32_t exchange_address;
+       uint16_t reserved2;
+       uint16_t flags;
+       uint32_t residual;
+       uint16_t ox_id;
+       uint16_t reserved3;
+       uint32_t relative_offset;
+       uint8_t  reserved4[24];
+} __packed;
+
+/* CTIO7 flags values */
+#define CTIO7_FLAGS_SEND_STATUS                BIT_15
+#define CTIO7_FLAGS_TERMINATE          BIT_14
+#define CTIO7_FLAGS_CONFORM_REQ                BIT_13
+#define CTIO7_FLAGS_DONT_RET_CTIO      BIT_8
+#define CTIO7_FLAGS_STATUS_MODE_0      0
+#define CTIO7_FLAGS_STATUS_MODE_1      BIT_6
+#define CTIO7_FLAGS_EXPLICIT_CONFORM   BIT_5
+#define CTIO7_FLAGS_CONFIRM_SATISF     BIT_4
+#define CTIO7_FLAGS_DSD_PTR            BIT_2
+#define CTIO7_FLAGS_DATA_IN            BIT_1
+#define CTIO7_FLAGS_DATA_OUT           BIT_0
+
+#define ELS_PLOGI                      0x3
+#define ELS_FLOGI                      0x4
+#define ELS_LOGO                       0x5
+#define ELS_PRLI                       0x20
+#define ELS_PRLO                       0x21
+#define ELS_TPRLO                      0x24
+#define ELS_PDISC                      0x50
+#define ELS_ADISC                      0x52
+
+/*
+ * ISP queue - ABTS received/response entries structure definition for 24xx.
+ */
+#define ABTS_RECV_24XX         0x54 /* ABTS received (for 24xx) */
+#define ABTS_RESP_24XX         0x55 /* ABTS responce (for 24xx) */
+
+/*
+ * ISP queue - ABTS received IOCB entry structure definition for 24xx.
+ *             The ABTS BLS received from the wire is sent to the
+ *             target driver by the ISP 24xx.
+ *             The IOCB is placed on the response queue.
+ */
+struct abts_recv_from_24xx {
+       uint8_t  entry_type;                /* Entry type. */
+       uint8_t  entry_count;               /* Entry count. */
+       uint8_t  sys_define;                /* System defined. */
+       uint8_t  entry_status;              /* Entry Status. */
+       uint8_t  reserved_1[6];
+       uint16_t nport_handle;
+       uint8_t  reserved_2[2];
+       uint8_t  vp_index;
+       uint8_t  reserved_3:4;
+       uint8_t  sof_type:4;
+       uint32_t exchange_address;
+       struct fcp_hdr_le fcp_hdr_le;
+       uint8_t  reserved_4[16];
+       uint32_t exchange_addr_to_abort;
+} __packed;
+
+#define ABTS_PARAM_ABORT_SEQ           BIT_0
+
+struct ba_acc_le {
+       uint16_t reserved;
+       uint8_t  seq_id_last;
+       uint8_t  seq_id_valid;
+#define SEQ_ID_VALID   0x80
+#define SEQ_ID_INVALID 0x00
+       uint16_t rx_id;
+       uint16_t ox_id;
+       uint16_t high_seq_cnt;
+       uint16_t low_seq_cnt;
+} __packed;
+
+struct ba_rjt_le {
+       uint8_t vendor_uniq;
+       uint8_t reason_expl;
+       uint8_t reason_code;
+#define BA_RJT_REASON_CODE_INVALID_COMMAND     0x1
+#define BA_RJT_REASON_CODE_UNABLE_TO_PERFORM   0x9
+       uint8_t reserved;
+} __packed;
+
+/*
+ * ISP queue - ABTS Response IOCB entry structure definition for 24xx.
+ *             The ABTS response to the ABTS received is sent by the
+ *             target driver to the ISP 24xx.
+ *             The IOCB is placed on the request queue.
+ */
+struct abts_resp_to_24xx {
+       uint8_t  entry_type;                /* Entry type. */
+       uint8_t  entry_count;               /* Entry count. */
+       uint8_t  sys_define;                /* System defined. */
+       uint8_t  entry_status;              /* Entry Status. */
+       uint32_t handle;
+       uint16_t reserved_1;
+       uint16_t nport_handle;
+       uint16_t control_flags;
+#define ABTS_CONTR_FLG_TERM_EXCHG      BIT_0
+       uint8_t  vp_index;
+       uint8_t  reserved_3:4;
+       uint8_t  sof_type:4;
+       uint32_t exchange_address;
+       struct fcp_hdr_le fcp_hdr_le;
+       union {
+               struct ba_acc_le ba_acct;
+               struct ba_rjt_le ba_rjt;
+       } __packed payload;
+       uint32_t reserved_4;
+       uint32_t exchange_addr_to_abort;
+} __packed;
+
+/*
+ * ISP queue - ABTS Response IOCB from ISP24xx Firmware entry structure.
+ *             The ABTS response with completion status to the ABTS response
+ *             (sent by the target driver to the ISP 24xx) is sent by the
+ *             ISP24xx firmware to the target driver.
+ *             The IOCB is placed on the response queue.
+ */
+struct abts_resp_from_24xx_fw {
+       uint8_t  entry_type;                /* Entry type. */
+       uint8_t  entry_count;               /* Entry count. */
+       uint8_t  sys_define;                /* System defined. */
+       uint8_t  entry_status;              /* Entry Status. */
+       uint32_t handle;
+       uint16_t compl_status;
+#define ABTS_RESP_COMPL_SUCCESS                0
+#define ABTS_RESP_COMPL_SUBCODE_ERROR  0x31
+       uint16_t nport_handle;
+       uint16_t reserved_1;
+       uint8_t  reserved_2;
+       uint8_t  reserved_3:4;
+       uint8_t  sof_type:4;
+       uint32_t exchange_address;
+       struct fcp_hdr_le fcp_hdr_le;
+       uint8_t reserved_4[8];
+       uint32_t error_subcode1;
+#define ABTS_RESP_SUBCODE_ERR_ABORTED_EXCH_NOT_TERM    0x1E
+       uint32_t error_subcode2;
+       uint32_t exchange_addr_to_abort;
+} __packed;
+
+/********************************************************************\
+ * Type Definitions used by initiator & target halves
+\********************************************************************/
+
+struct qla_tgt_mgmt_cmd;
+struct qla_tgt_sess;
+
+/*
+ * This structure provides a template of function calls that the
+ * target driver (from within qla_target.c) can issue to the
+ * target module (tcm_qla2xxx).
+ */
+struct qla_tgt_func_tmpl {
+
+       int (*handle_cmd)(struct scsi_qla_host *, struct qla_tgt_cmd *,
+                       unsigned char *, uint32_t, int, int, int);
+       int (*handle_data)(struct qla_tgt_cmd *);
+       int (*handle_tmr)(struct qla_tgt_mgmt_cmd *, uint32_t, uint8_t,
+                       uint32_t);
+       void (*free_cmd)(struct qla_tgt_cmd *);
+       void (*free_mcmd)(struct qla_tgt_mgmt_cmd *);
+       void (*free_session)(struct qla_tgt_sess *);
+
+       int (*check_initiator_node_acl)(struct scsi_qla_host *, unsigned char *,
+                                       void *, uint8_t *, uint16_t);
+       struct qla_tgt_sess *(*find_sess_by_loop_id)(struct scsi_qla_host *,
+                                               const uint16_t);
+       struct qla_tgt_sess *(*find_sess_by_s_id)(struct scsi_qla_host *,
+                                               const uint8_t *);
+       void (*clear_nacl_from_fcport_map)(struct qla_tgt_sess *);
+       void (*put_sess)(struct qla_tgt_sess *);
+       void (*shutdown_sess)(struct qla_tgt_sess *);
+};
+
+int qla2x00_wait_for_hba_online(struct scsi_qla_host *);
+
+#include <target/target_core_base.h>
+
+#define QLA_TGT_TIMEOUT                        10      /* in seconds */
+
+#define QLA_TGT_MAX_HW_PENDING_TIME    60 /* in seconds */
+
+/* Immediate notify status constants */
+#define IMM_NTFY_LIP_RESET          0x000E
+#define IMM_NTFY_LIP_LINK_REINIT    0x000F
+#define IMM_NTFY_IOCB_OVERFLOW      0x0016
+#define IMM_NTFY_ABORT_TASK         0x0020
+#define IMM_NTFY_PORT_LOGOUT        0x0029
+#define IMM_NTFY_PORT_CONFIG        0x002A
+#define IMM_NTFY_GLBL_TPRLO         0x002D
+#define IMM_NTFY_GLBL_LOGO          0x002E
+#define IMM_NTFY_RESOURCE           0x0034
+#define IMM_NTFY_MSG_RX             0x0036
+#define IMM_NTFY_SRR                0x0045
+#define IMM_NTFY_ELS                0x0046
+
+/* Immediate notify task flags */
+#define IMM_NTFY_TASK_MGMT_SHIFT    8
+
+#define QLA_TGT_CLEAR_ACA               0x40
+#define QLA_TGT_TARGET_RESET            0x20
+#define QLA_TGT_LUN_RESET               0x10
+#define QLA_TGT_CLEAR_TS                0x04
+#define QLA_TGT_ABORT_TS                0x02
+#define QLA_TGT_ABORT_ALL_SESS          0xFFFF
+#define QLA_TGT_ABORT_ALL               0xFFFE
+#define QLA_TGT_NEXUS_LOSS_SESS         0xFFFD
+#define QLA_TGT_NEXUS_LOSS              0xFFFC
+
+/* Notify Acknowledge flags */
+#define NOTIFY_ACK_RES_COUNT        BIT_8
+#define NOTIFY_ACK_CLEAR_LIP_RESET  BIT_5
+#define NOTIFY_ACK_TM_RESP_CODE_VALID BIT_4
+
+/* Command's states */
+#define QLA_TGT_STATE_NEW              0 /* New command + target processing */
+#define QLA_TGT_STATE_NEED_DATA                1 /* target needs data to continue */
+#define QLA_TGT_STATE_DATA_IN          2 /* Data arrived + target processing */
+#define QLA_TGT_STATE_PROCESSED                3 /* target done processing */
+#define QLA_TGT_STATE_ABORTED          4 /* Command aborted */
+
+/* Special handles */
+#define QLA_TGT_NULL_HANDLE    0
+#define QLA_TGT_SKIP_HANDLE    (0xFFFFFFFF & ~CTIO_COMPLETION_HANDLE_MARK)
+
+/* ATIO task_codes field */
+#define ATIO_SIMPLE_QUEUE           0
+#define ATIO_HEAD_OF_QUEUE          1
+#define ATIO_ORDERED_QUEUE          2
+#define ATIO_ACA_QUEUE              4
+#define ATIO_UNTAGGED               5
+
+/* TM failed response codes, see FCP (9.4.11 FCP_RSP_INFO) */
+#define        FC_TM_SUCCESS               0
+#define        FC_TM_BAD_FCP_DATA          1
+#define        FC_TM_BAD_CMD               2
+#define        FC_TM_FCP_DATA_MISMATCH     3
+#define        FC_TM_REJECT                4
+#define FC_TM_FAILED                5
+
+/*
+ * Error code of qlt_pre_xmit_response() meaning that cmd's exchange was
+ * terminated, so no more actions is needed and success should be returned
+ * to target.
+ */
+#define QLA_TGT_PRE_XMIT_RESP_CMD_ABORTED      0x1717
+
+#if (BITS_PER_LONG > 32) || defined(CONFIG_HIGHMEM64G)
+#define pci_dma_lo32(a) (a & 0xffffffff)
+#define pci_dma_hi32(a) ((((a) >> 16)>>16) & 0xffffffff)
+#else
+#define pci_dma_lo32(a) (a & 0xffffffff)
+#define pci_dma_hi32(a) 0
+#endif
+
+#define QLA_TGT_SENSE_VALID(sense)  ((sense != NULL) && \
+                               (((const uint8_t *)(sense))[0] & 0x70) == 0x70)
+
+struct qla_port_24xx_data {
+       uint8_t port_name[WWN_SIZE];
+       uint16_t loop_id;
+       uint16_t reserved;
+};
+
+struct qla_tgt {
+       struct scsi_qla_host *vha;
+       struct qla_hw_data *ha;
+
+       /*
+        * To sync between IRQ handlers and qlt_target_release(). Needed,
+        * because req_pkt() can drop/reaquire HW lock inside. Protected by
+        * HW lock.
+        */
+       int irq_cmd_count;
+
+       int datasegs_per_cmd, datasegs_per_cont, sg_tablesize;
+
+       /* Target's flags, serialized by pha->hardware_lock */
+       unsigned int tgt_enable_64bit_addr:1; /* 64-bits PCI addr enabled */
+       unsigned int link_reinit_iocb_pending:1;
+
+       /*
+        * Protected by tgt_mutex AND hardware_lock for writing and tgt_mutex
+        * OR hardware_lock for reading.
+        */
+       int tgt_stop; /* the target mode driver is being stopped */
+       int tgt_stopped; /* the target mode driver has been stopped */
+
+       /* Count of sessions refering qla_tgt. Protected by hardware_lock. */
+       int sess_count;
+
+       /* Protected by hardware_lock. Addition also protected by tgt_mutex. */
+       struct list_head sess_list;
+
+       /* Protected by hardware_lock */
+       struct list_head del_sess_list;
+       struct delayed_work sess_del_work;
+
+       spinlock_t sess_work_lock;
+       struct list_head sess_works_list;
+       struct work_struct sess_work;
+
+       struct imm_ntfy_from_isp link_reinit_iocb;
+       wait_queue_head_t waitQ;
+       int notify_ack_expected;
+       int abts_resp_expected;
+       int modify_lun_expected;
+
+       int ctio_srr_id;
+       int imm_srr_id;
+       spinlock_t srr_lock;
+       struct list_head srr_ctio_list;
+       struct list_head srr_imm_list;
+       struct work_struct srr_work;
+
+       atomic_t tgt_global_resets_count;
+
+       struct list_head tgt_list_entry;
+};
+
+/*
+ * Equivilant to IT Nexus (Initiator-Target)
+ */
+struct qla_tgt_sess {
+       uint16_t loop_id;
+       port_id_t s_id;
+
+       unsigned int conf_compl_supported:1;
+       unsigned int deleted:1;
+       unsigned int local:1;
+       unsigned int tearing_down:1;
+
+       struct se_session *se_sess;
+       struct scsi_qla_host *vha;
+       struct qla_tgt *tgt;
+
+       struct list_head sess_list_entry;
+       unsigned long expires;
+       struct list_head del_list_entry;
+
+       uint8_t port_name[WWN_SIZE];
+       struct work_struct free_work;
+};
+
+struct qla_tgt_cmd {
+       struct qla_tgt_sess *sess;
+       int state;
+       struct se_cmd se_cmd;
+       struct work_struct free_work;
+       struct work_struct work;
+       /* Sense buffer that will be mapped into outgoing status */
+       unsigned char sense_buffer[TRANSPORT_SENSE_BUFFER];
+
+       /* to save extra sess dereferences */
+       unsigned int conf_compl_supported:1;
+       unsigned int sg_mapped:1;
+       unsigned int free_sg:1;
+       unsigned int aborted:1; /* Needed in case of SRR */
+       unsigned int write_data_transferred:1;
+
+       struct scatterlist *sg; /* cmd data buffer SG vector */
+       int sg_cnt;             /* SG segments count */
+       int bufflen;            /* cmd buffer length */
+       int offset;
+       uint32_t tag;
+       uint32_t unpacked_lun;
+       enum dma_data_direction dma_data_direction;
+
+       uint16_t loop_id;       /* to save extra sess dereferences */
+       struct qla_tgt *tgt;    /* to save extra sess dereferences */
+       struct scsi_qla_host *vha;
+       struct list_head cmd_list;
+
+       struct atio_from_isp atio;
+};
+
+struct qla_tgt_sess_work_param {
+       struct list_head sess_works_list_entry;
+
+#define QLA_TGT_SESS_WORK_ABORT        1
+#define QLA_TGT_SESS_WORK_TM   2
+       int type;
+
+       union {
+               struct abts_recv_from_24xx abts;
+               struct imm_ntfy_from_isp tm_iocb;
+               struct atio_from_isp tm_iocb2;
+       };
+};
+
+struct qla_tgt_mgmt_cmd {
+       uint8_t tmr_func;
+       uint8_t fc_tm_rsp;
+       struct qla_tgt_sess *sess;
+       struct se_cmd se_cmd;
+       struct work_struct free_work;
+       unsigned int flags;
+#define QLA24XX_MGMT_SEND_NACK 1
+       union {
+               struct atio_from_isp atio;
+               struct imm_ntfy_from_isp imm_ntfy;
+               struct abts_recv_from_24xx abts;
+       } __packed orig_iocb;
+};
+
+struct qla_tgt_prm {
+       struct qla_tgt_cmd *cmd;
+       struct qla_tgt *tgt;
+       void *pkt;
+       struct scatterlist *sg; /* cmd data buffer SG vector */
+       int seg_cnt;
+       int req_cnt;
+       uint16_t rq_result;
+       uint16_t scsi_status;
+       unsigned char *sense_buffer;
+       int sense_buffer_len;
+       int residual;
+       int add_status_pkt;
+};
+
+struct qla_tgt_srr_imm {
+       struct list_head srr_list_entry;
+       int srr_id;
+       struct imm_ntfy_from_isp imm_ntfy;
+};
+
+struct qla_tgt_srr_ctio {
+       struct list_head srr_list_entry;
+       int srr_id;
+       struct qla_tgt_cmd *cmd;
+};
+
+#define QLA_TGT_XMIT_DATA              1
+#define QLA_TGT_XMIT_STATUS            2
+#define QLA_TGT_XMIT_ALL               (QLA_TGT_XMIT_STATUS|QLA_TGT_XMIT_DATA)
+
+#include <linux/version.h>
+
+extern struct qla_tgt_data qla_target;
+/*
+ * Internal function prototypes
+ */
+void qlt_disable_vha(struct scsi_qla_host *);
+
+/*
+ * Function prototypes for qla_target.c logic used by qla2xxx LLD code.
+ */
+extern int qlt_add_target(struct qla_hw_data *, struct scsi_qla_host *);
+extern int qlt_remove_target(struct qla_hw_data *, struct scsi_qla_host *);
+extern int qlt_lport_register(struct qla_tgt_func_tmpl *, u64,
+                       int (*callback)(struct scsi_qla_host *), void *);
+extern void qlt_lport_deregister(struct scsi_qla_host *);
+extern void qlt_unreg_sess(struct qla_tgt_sess *);
+extern void qlt_fc_port_added(struct scsi_qla_host *, fc_port_t *);
+extern void qlt_fc_port_deleted(struct scsi_qla_host *, fc_port_t *);
+extern void qlt_set_mode(struct scsi_qla_host *ha);
+extern void qlt_clear_mode(struct scsi_qla_host *ha);
+extern int __init qlt_init(void);
+extern void qlt_exit(void);
+extern void qlt_update_vp_map(struct scsi_qla_host *, int);
+
+/*
+ * This macro is used during early initializations when host->active_mode
+ * is not set. Right now, ha value is ignored.
+ */
+#define QLA_TGT_MODE_ENABLED() (ql2x_ini_mode != QLA2XXX_INI_MODE_ENABLED)
+
+static inline bool qla_tgt_mode_enabled(struct scsi_qla_host *ha)
+{
+       return ha->host->active_mode & MODE_TARGET;
+}
+
+static inline bool qla_ini_mode_enabled(struct scsi_qla_host *ha)
+{
+       return ha->host->active_mode & MODE_INITIATOR;
+}
+
+static inline void qla_reverse_ini_mode(struct scsi_qla_host *ha)
+{
+       if (ha->host->active_mode & MODE_INITIATOR)
+               ha->host->active_mode &= ~MODE_INITIATOR;
+       else
+               ha->host->active_mode |= MODE_INITIATOR;
+}
+
+/*
+ * Exported symbols from qla_target.c LLD logic used by qla2xxx code..
+ */
+extern void qlt_24xx_atio_pkt_all_vps(struct scsi_qla_host *,
+       struct atio_from_isp *);
+extern void qlt_response_pkt_all_vps(struct scsi_qla_host *, response_t *);
+extern int qlt_rdy_to_xfer(struct qla_tgt_cmd *);
+extern int qlt_xmit_response(struct qla_tgt_cmd *, int, uint8_t);
+extern void qlt_xmit_tm_rsp(struct qla_tgt_mgmt_cmd *);
+extern void qlt_free_mcmd(struct qla_tgt_mgmt_cmd *);
+extern void qlt_free_cmd(struct qla_tgt_cmd *cmd);
+extern void qlt_ctio_completion(struct scsi_qla_host *, uint32_t);
+extern void qlt_async_event(uint16_t, struct scsi_qla_host *, uint16_t *);
+extern void qlt_enable_vha(struct scsi_qla_host *);
+extern void qlt_vport_create(struct scsi_qla_host *, struct qla_hw_data *);
+extern void qlt_rff_id(struct scsi_qla_host *, struct ct_sns_req *);
+extern void qlt_init_atio_q_entries(struct scsi_qla_host *);
+extern void qlt_24xx_process_atio_queue(struct scsi_qla_host *);
+extern void qlt_24xx_config_rings(struct scsi_qla_host *,
+       device_reg_t __iomem *);
+extern void qlt_24xx_config_nvram_stage1(struct scsi_qla_host *,
+       struct nvram_24xx *);
+extern void qlt_24xx_config_nvram_stage2(struct scsi_qla_host *,
+       struct init_cb_24xx *);
+extern int qlt_24xx_process_response_error(struct scsi_qla_host *,
+       struct sts_entry_24xx *);
+extern void qlt_modify_vp_config(struct scsi_qla_host *,
+       struct vp_config_entry_24xx *);
+extern void qlt_probe_one_stage1(struct scsi_qla_host *, struct qla_hw_data *);
+extern int qlt_mem_alloc(struct qla_hw_data *);
+extern void qlt_mem_free(struct qla_hw_data *);
+extern void qlt_stop_phase1(struct qla_tgt *);
+extern void qlt_stop_phase2(struct qla_tgt *);
+
+#endif /* __QLA_TARGET_H */
diff --git a/drivers/scsi/qla2xxx/tcm_qla2xxx.c b/drivers/scsi/qla2xxx/tcm_qla2xxx.c
new file mode 100644 (file)
index 0000000..436598f
--- /dev/null
@@ -0,0 +1,1955 @@
+/*******************************************************************************
+ * This file contains tcm implementation using v4 configfs fabric infrastructure
+ * for QLogic target mode HBAs
+ *
+ * ?? Copyright 2010-2011 RisingTide Systems LLC.
+ *
+ * Licensed to the Linux Foundation under the General Public License (GPL)
+ * version 2.
+ *
+ * Author: Nicholas A. Bellinger <nab@risingtidesystems.com>
+ *
+ * tcm_qla2xxx_parse_wwn() and tcm_qla2xxx_format_wwn() contains code from
+ * the TCM_FC / Open-FCoE.org fabric module.
+ *
+ * Copyright (c) 2010 Cisco Systems, 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 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.
+ ****************************************************************************/
+
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <generated/utsrelease.h>
+#include <linux/utsname.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/kthread.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/configfs.h>
+#include <linux/ctype.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <asm/unaligned.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_cmnd.h>
+#include <target/target_core_base.h>
+#include <target/target_core_fabric.h>
+#include <target/target_core_fabric_configfs.h>
+#include <target/target_core_configfs.h>
+#include <target/configfs_macros.h>
+
+#include "qla_def.h"
+#include "qla_target.h"
+#include "tcm_qla2xxx.h"
+
+struct workqueue_struct *tcm_qla2xxx_free_wq;
+struct workqueue_struct *tcm_qla2xxx_cmd_wq;
+
+static int tcm_qla2xxx_check_true(struct se_portal_group *se_tpg)
+{
+       return 1;
+}
+
+static int tcm_qla2xxx_check_false(struct se_portal_group *se_tpg)
+{
+       return 0;
+}
+
+/*
+ * Parse WWN.
+ * If strict, we require lower-case hex and colon separators to be sure
+ * the name is the same as what would be generated by ft_format_wwn()
+ * so the name and wwn are mapped one-to-one.
+ */
+static ssize_t tcm_qla2xxx_parse_wwn(const char *name, u64 *wwn, int strict)
+{
+       const char *cp;
+       char c;
+       u32 nibble;
+       u32 byte = 0;
+       u32 pos = 0;
+       u32 err;
+
+       *wwn = 0;
+       for (cp = name; cp < &name[TCM_QLA2XXX_NAMELEN - 1]; cp++) {
+               c = *cp;
+               if (c == '\n' && cp[1] == '\0')
+                       continue;
+               if (strict && pos++ == 2 && byte++ < 7) {
+                       pos = 0;
+                       if (c == ':')
+                               continue;
+                       err = 1;
+                       goto fail;
+               }
+               if (c == '\0') {
+                       err = 2;
+                       if (strict && byte != 8)
+                               goto fail;
+                       return cp - name;
+               }
+               err = 3;
+               if (isdigit(c))
+                       nibble = c - '0';
+               else if (isxdigit(c) && (islower(c) || !strict))
+                       nibble = tolower(c) - 'a' + 10;
+               else
+                       goto fail;
+               *wwn = (*wwn << 4) | nibble;
+       }
+       err = 4;
+fail:
+       pr_debug("err %u len %zu pos %u byte %u\n",
+                       err, cp - name, pos, byte);
+       return -1;
+}
+
+static ssize_t tcm_qla2xxx_format_wwn(char *buf, size_t len, u64 wwn)
+{
+       u8 b[8];
+
+       put_unaligned_be64(wwn, b);
+       return snprintf(buf, len,
+               "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x",
+               b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]);
+}
+
+static char *tcm_qla2xxx_get_fabric_name(void)
+{
+       return "qla2xxx";
+}
+
+/*
+ * From drivers/scsi/scsi_transport_fc.c:fc_parse_wwn
+ */
+static int tcm_qla2xxx_npiv_extract_wwn(const char *ns, u64 *nm)
+{
+       unsigned int i, j, value;
+       u8 wwn[8];
+
+       memset(wwn, 0, sizeof(wwn));
+
+       /* Validate and store the new name */
+       for (i = 0, j = 0; i < 16; i++) {
+               value = hex_to_bin(*ns++);
+               if (value >= 0)
+                       j = (j << 4) | value;
+               else
+                       return -EINVAL;
+
+               if (i % 2) {
+                       wwn[i/2] = j & 0xff;
+                       j = 0;
+               }
+       }
+
+       *nm = wwn_to_u64(wwn);
+       return 0;
+}
+
+/*
+ * This parsing logic follows drivers/scsi/scsi_transport_fc.c:
+ * store_fc_host_vport_create()
+ */
+static int tcm_qla2xxx_npiv_parse_wwn(
+       const char *name,
+       size_t count,
+       u64 *wwpn,
+       u64 *wwnn)
+{
+       unsigned int cnt = count;
+       int rc;
+
+       *wwpn = 0;
+       *wwnn = 0;
+
+       /* count may include a LF at end of string */
+       if (name[cnt-1] == '\n')
+               cnt--;
+
+       /* validate we have enough characters for WWPN */
+       if ((cnt != (16+1+16)) || (name[16] != ':'))
+               return -EINVAL;
+
+       rc = tcm_qla2xxx_npiv_extract_wwn(&name[0], wwpn);
+       if (rc != 0)
+               return rc;
+
+       rc = tcm_qla2xxx_npiv_extract_wwn(&name[17], wwnn);
+       if (rc != 0)
+               return rc;
+
+       return 0;
+}
+
+static ssize_t tcm_qla2xxx_npiv_format_wwn(char *buf, size_t len,
+                                       u64 wwpn, u64 wwnn)
+{
+       u8 b[8], b2[8];
+
+       put_unaligned_be64(wwpn, b);
+       put_unaligned_be64(wwnn, b2);
+       return snprintf(buf, len,
+               "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x,"
+               "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x",
+               b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7],
+               b2[0], b2[1], b2[2], b2[3], b2[4], b2[5], b2[6], b2[7]);
+}
+
+static char *tcm_qla2xxx_npiv_get_fabric_name(void)
+{
+       return "qla2xxx_npiv";
+}
+
+static u8 tcm_qla2xxx_get_fabric_proto_ident(struct se_portal_group *se_tpg)
+{
+       struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg,
+                               struct tcm_qla2xxx_tpg, se_tpg);
+       struct tcm_qla2xxx_lport *lport = tpg->lport;
+       u8 proto_id;
+
+       switch (lport->lport_proto_id) {
+       case SCSI_PROTOCOL_FCP:
+       default:
+               proto_id = fc_get_fabric_proto_ident(se_tpg);
+               break;
+       }
+
+       return proto_id;
+}
+
+static char *tcm_qla2xxx_get_fabric_wwn(struct se_portal_group *se_tpg)
+{
+       struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg,
+                               struct tcm_qla2xxx_tpg, se_tpg);
+       struct tcm_qla2xxx_lport *lport = tpg->lport;
+
+       return &lport->lport_name[0];
+}
+
+static char *tcm_qla2xxx_npiv_get_fabric_wwn(struct se_portal_group *se_tpg)
+{
+       struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg,
+                               struct tcm_qla2xxx_tpg, se_tpg);
+       struct tcm_qla2xxx_lport *lport = tpg->lport;
+
+       return &lport->lport_npiv_name[0];
+}
+
+static u16 tcm_qla2xxx_get_tag(struct se_portal_group *se_tpg)
+{
+       struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg,
+                               struct tcm_qla2xxx_tpg, se_tpg);
+       return tpg->lport_tpgt;
+}
+
+static u32 tcm_qla2xxx_get_default_depth(struct se_portal_group *se_tpg)
+{
+       return 1;
+}
+
+static u32 tcm_qla2xxx_get_pr_transport_id(
+       struct se_portal_group *se_tpg,
+       struct se_node_acl *se_nacl,
+       struct t10_pr_registration *pr_reg,
+       int *format_code,
+       unsigned char *buf)
+{
+       struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg,
+                               struct tcm_qla2xxx_tpg, se_tpg);
+       struct tcm_qla2xxx_lport *lport = tpg->lport;
+       int ret = 0;
+
+       switch (lport->lport_proto_id) {
+       case SCSI_PROTOCOL_FCP:
+       default:
+               ret = fc_get_pr_transport_id(se_tpg, se_nacl, pr_reg,
+                                       format_code, buf);
+               break;
+       }
+
+       return ret;
+}
+
+static u32 tcm_qla2xxx_get_pr_transport_id_len(
+       struct se_portal_group *se_tpg,
+       struct se_node_acl *se_nacl,
+       struct t10_pr_registration *pr_reg,
+       int *format_code)
+{
+       struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg,
+                               struct tcm_qla2xxx_tpg, se_tpg);
+       struct tcm_qla2xxx_lport *lport = tpg->lport;
+       int ret = 0;
+
+       switch (lport->lport_proto_id) {
+       case SCSI_PROTOCOL_FCP:
+       default:
+               ret = fc_get_pr_transport_id_len(se_tpg, se_nacl, pr_reg,
+                                       format_code);
+               break;
+       }
+
+       return ret;
+}
+
+static char *tcm_qla2xxx_parse_pr_out_transport_id(
+       struct se_portal_group *se_tpg,
+       const char *buf,
+       u32 *out_tid_len,
+       char **port_nexus_ptr)
+{
+       struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg,
+                               struct tcm_qla2xxx_tpg, se_tpg);
+       struct tcm_qla2xxx_lport *lport = tpg->lport;
+       char *tid = NULL;
+
+       switch (lport->lport_proto_id) {
+       case SCSI_PROTOCOL_FCP:
+       default:
+               tid = fc_parse_pr_out_transport_id(se_tpg, buf, out_tid_len,
+                                       port_nexus_ptr);
+               break;
+       }
+
+       return tid;
+}
+
+static int tcm_qla2xxx_check_demo_mode(struct se_portal_group *se_tpg)
+{
+       struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg,
+                               struct tcm_qla2xxx_tpg, se_tpg);
+
+       return QLA_TPG_ATTRIB(tpg)->generate_node_acls;
+}
+
+static int tcm_qla2xxx_check_demo_mode_cache(struct se_portal_group *se_tpg)
+{
+       struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg,
+                               struct tcm_qla2xxx_tpg, se_tpg);
+
+       return QLA_TPG_ATTRIB(tpg)->cache_dynamic_acls;
+}
+
+static int tcm_qla2xxx_check_demo_write_protect(struct se_portal_group *se_tpg)
+{
+       struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg,
+                               struct tcm_qla2xxx_tpg, se_tpg);
+
+       return QLA_TPG_ATTRIB(tpg)->demo_mode_write_protect;
+}
+
+static int tcm_qla2xxx_check_prod_write_protect(struct se_portal_group *se_tpg)
+{
+       struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg,
+                               struct tcm_qla2xxx_tpg, se_tpg);
+
+       return QLA_TPG_ATTRIB(tpg)->prod_mode_write_protect;
+}
+
+static struct se_node_acl *tcm_qla2xxx_alloc_fabric_acl(
+       struct se_portal_group *se_tpg)
+{
+       struct tcm_qla2xxx_nacl *nacl;
+
+       nacl = kzalloc(sizeof(struct tcm_qla2xxx_nacl), GFP_KERNEL);
+       if (!nacl) {
+               pr_err("Unable to alocate struct tcm_qla2xxx_nacl\n");
+               return NULL;
+       }
+
+       return &nacl->se_node_acl;
+}
+
+static void tcm_qla2xxx_release_fabric_acl(
+       struct se_portal_group *se_tpg,
+       struct se_node_acl *se_nacl)
+{
+       struct tcm_qla2xxx_nacl *nacl = container_of(se_nacl,
+                       struct tcm_qla2xxx_nacl, se_node_acl);
+       kfree(nacl);
+}
+
+static u32 tcm_qla2xxx_tpg_get_inst_index(struct se_portal_group *se_tpg)
+{
+       struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg,
+                               struct tcm_qla2xxx_tpg, se_tpg);
+
+       return tpg->lport_tpgt;
+}
+
+static void tcm_qla2xxx_complete_mcmd(struct work_struct *work)
+{
+       struct qla_tgt_mgmt_cmd *mcmd = container_of(work,
+                       struct qla_tgt_mgmt_cmd, free_work);
+
+       transport_generic_free_cmd(&mcmd->se_cmd, 0);
+}
+
+/*
+ * Called from qla_target_template->free_mcmd(), and will call
+ * tcm_qla2xxx_release_cmd() via normal struct target_core_fabric_ops
+ * release callback.  qla_hw_data->hardware_lock is expected to be held
+ */
+static void tcm_qla2xxx_free_mcmd(struct qla_tgt_mgmt_cmd *mcmd)
+{
+       INIT_WORK(&mcmd->free_work, tcm_qla2xxx_complete_mcmd);
+       queue_work(tcm_qla2xxx_free_wq, &mcmd->free_work);
+}
+
+static void tcm_qla2xxx_complete_free(struct work_struct *work)
+{
+       struct qla_tgt_cmd *cmd = container_of(work, struct qla_tgt_cmd, work);
+
+       transport_generic_free_cmd(&cmd->se_cmd, 0);
+}
+
+/*
+ * Called from qla_target_template->free_cmd(), and will call
+ * tcm_qla2xxx_release_cmd via normal struct target_core_fabric_ops
+ * release callback.  qla_hw_data->hardware_lock is expected to be held
+ */
+static void tcm_qla2xxx_free_cmd(struct qla_tgt_cmd *cmd)
+{
+       INIT_WORK(&cmd->work, tcm_qla2xxx_complete_free);
+       queue_work(tcm_qla2xxx_free_wq, &cmd->work);
+}
+
+/*
+ * Called from struct target_core_fabric_ops->check_stop_free() context
+ */
+static int tcm_qla2xxx_check_stop_free(struct se_cmd *se_cmd)
+{
+       return target_put_sess_cmd(se_cmd->se_sess, se_cmd);
+}
+
+/* tcm_qla2xxx_release_cmd - Callback from TCM Core to release underlying
+ * fabric descriptor @se_cmd command to release
+ */
+static void tcm_qla2xxx_release_cmd(struct se_cmd *se_cmd)
+{
+       struct qla_tgt_cmd *cmd;
+
+       if (se_cmd->se_cmd_flags & SCF_SCSI_TMR_CDB) {
+               struct qla_tgt_mgmt_cmd *mcmd = container_of(se_cmd,
+                               struct qla_tgt_mgmt_cmd, se_cmd);
+               qlt_free_mcmd(mcmd);
+               return;
+       }
+
+       cmd = container_of(se_cmd, struct qla_tgt_cmd, se_cmd);
+       qlt_free_cmd(cmd);
+}
+
+static int tcm_qla2xxx_shutdown_session(struct se_session *se_sess)
+{
+       struct qla_tgt_sess *sess = se_sess->fabric_sess_ptr;
+       struct scsi_qla_host *vha;
+       unsigned long flags;
+
+       BUG_ON(!sess);
+       vha = sess->vha;
+
+       spin_lock_irqsave(&vha->hw->hardware_lock, flags);
+       sess->tearing_down = 1;
+       target_splice_sess_cmd_list(se_sess);
+       spin_unlock_irqrestore(&vha->hw->hardware_lock, flags);
+
+       return 1;
+}
+
+static void tcm_qla2xxx_close_session(struct se_session *se_sess)
+{
+       struct qla_tgt_sess *sess = se_sess->fabric_sess_ptr;
+       struct scsi_qla_host *vha;
+       unsigned long flags;
+
+       BUG_ON(!sess);
+       vha = sess->vha;
+
+       spin_lock_irqsave(&vha->hw->hardware_lock, flags);
+       qlt_unreg_sess(sess);
+       spin_unlock_irqrestore(&vha->hw->hardware_lock, flags);
+}
+
+static u32 tcm_qla2xxx_sess_get_index(struct se_session *se_sess)
+{
+       return 0;
+}
+
+/*
+ * The LIO target core uses DMA_TO_DEVICE to mean that data is going
+ * to the target (eg handling a WRITE) and DMA_FROM_DEVICE to mean
+ * that data is coming from the target (eg handling a READ).  However,
+ * this is just the opposite of what we have to tell the DMA mapping
+ * layer -- eg when handling a READ, the HBA will have to DMA the data
+ * out of memory so it can send it to the initiator, which means we
+ * need to use DMA_TO_DEVICE when we map the data.
+ */
+static enum dma_data_direction tcm_qla2xxx_mapping_dir(struct se_cmd *se_cmd)
+{
+       if (se_cmd->se_cmd_flags & SCF_BIDI)
+               return DMA_BIDIRECTIONAL;
+
+       switch (se_cmd->data_direction) {
+       case DMA_TO_DEVICE:
+               return DMA_FROM_DEVICE;
+       case DMA_FROM_DEVICE:
+               return DMA_TO_DEVICE;
+       case DMA_NONE:
+       default:
+               return DMA_NONE;
+       }
+}
+
+static int tcm_qla2xxx_write_pending(struct se_cmd *se_cmd)
+{
+       struct qla_tgt_cmd *cmd = container_of(se_cmd,
+                               struct qla_tgt_cmd, se_cmd);
+
+       cmd->bufflen = se_cmd->data_length;
+       cmd->dma_data_direction = tcm_qla2xxx_mapping_dir(se_cmd);
+
+       cmd->sg_cnt = se_cmd->t_data_nents;
+       cmd->sg = se_cmd->t_data_sg;
+
+       /*
+        * qla_target.c:qlt_rdy_to_xfer() will call pci_map_sg() to setup
+        * the SGL mappings into PCIe memory for incoming FCP WRITE data.
+        */
+       return qlt_rdy_to_xfer(cmd);
+}
+
+static int tcm_qla2xxx_write_pending_status(struct se_cmd *se_cmd)
+{
+       unsigned long flags;
+       /*
+        * Check for WRITE_PENDING status to determine if we need to wait for
+        * CTIO aborts to be posted via hardware in tcm_qla2xxx_handle_data().
+        */
+       spin_lock_irqsave(&se_cmd->t_state_lock, flags);
+       if (se_cmd->t_state == TRANSPORT_WRITE_PENDING ||
+           se_cmd->t_state == TRANSPORT_COMPLETE_QF_WP) {
+               spin_unlock_irqrestore(&se_cmd->t_state_lock, flags);
+               wait_for_completion_timeout(&se_cmd->t_transport_stop_comp,
+                                               3000);
+               return 0;
+       }
+       spin_unlock_irqrestore(&se_cmd->t_state_lock, flags);
+
+       return 0;
+}
+
+static void tcm_qla2xxx_set_default_node_attrs(struct se_node_acl *nacl)
+{
+       return;
+}
+
+static u32 tcm_qla2xxx_get_task_tag(struct se_cmd *se_cmd)
+{
+       struct qla_tgt_cmd *cmd = container_of(se_cmd,
+                               struct qla_tgt_cmd, se_cmd);
+
+       return cmd->tag;
+}
+
+static int tcm_qla2xxx_get_cmd_state(struct se_cmd *se_cmd)
+{
+       return 0;
+}
+
+/*
+ * Called from process context in qla_target.c:qlt_do_work() code
+ */
+static int tcm_qla2xxx_handle_cmd(scsi_qla_host_t *vha, struct qla_tgt_cmd *cmd,
+       unsigned char *cdb, uint32_t data_length, int fcp_task_attr,
+       int data_dir, int bidi)
+{
+       struct se_cmd *se_cmd = &cmd->se_cmd;
+       struct se_session *se_sess;
+       struct qla_tgt_sess *sess;
+       int flags = TARGET_SCF_ACK_KREF;
+
+       if (bidi)
+               flags |= TARGET_SCF_BIDI_OP;
+
+       sess = cmd->sess;
+       if (!sess) {
+               pr_err("Unable to locate struct qla_tgt_sess from qla_tgt_cmd\n");
+               return -EINVAL;
+       }
+
+       se_sess = sess->se_sess;
+       if (!se_sess) {
+               pr_err("Unable to locate active struct se_session\n");
+               return -EINVAL;
+       }
+
+       target_submit_cmd(se_cmd, se_sess, cdb, &cmd->sense_buffer[0],
+                               cmd->unpacked_lun, data_length, fcp_task_attr,
+                               data_dir, flags);
+       return 0;
+}
+
+static void tcm_qla2xxx_do_rsp(struct work_struct *work)
+{
+       struct qla_tgt_cmd *cmd = container_of(work, struct qla_tgt_cmd, work);
+       /*
+        * Dispatch ->queue_status from workqueue process context
+        */
+       transport_generic_request_failure(&cmd->se_cmd);
+}
+
+/*
+ * Called from qla_target.c:qlt_do_ctio_completion()
+ */
+static int tcm_qla2xxx_handle_data(struct qla_tgt_cmd *cmd)
+{
+       struct se_cmd *se_cmd = &cmd->se_cmd;
+       unsigned long flags;
+       /*
+        * Ensure that the complete FCP WRITE payload has been received.
+        * Otherwise return an exception via CHECK_CONDITION status.
+        */
+       if (!cmd->write_data_transferred) {
+               /*
+                * Check if se_cmd has already been aborted via LUN_RESET, and
+                * waiting upon completion in tcm_qla2xxx_write_pending_status()
+                */
+               spin_lock_irqsave(&se_cmd->t_state_lock, flags);
+               if (se_cmd->transport_state & CMD_T_ABORTED) {
+                       spin_unlock_irqrestore(&se_cmd->t_state_lock, flags);
+                       complete(&se_cmd->t_transport_stop_comp);
+                       return 0;
+               }
+               spin_unlock_irqrestore(&se_cmd->t_state_lock, flags);
+
+               se_cmd->scsi_sense_reason = TCM_CHECK_CONDITION_ABORT_CMD;
+               INIT_WORK(&cmd->work, tcm_qla2xxx_do_rsp);
+               queue_work(tcm_qla2xxx_free_wq, &cmd->work);
+               return 0;
+       }
+       /*
+        * We now tell TCM to queue this WRITE CDB with TRANSPORT_PROCESS_WRITE
+        * status to the backstore processing thread.
+        */
+       return transport_generic_handle_data(&cmd->se_cmd);
+}
+
+/*
+ * Called from qla_target.c:qlt_issue_task_mgmt()
+ */
+int tcm_qla2xxx_handle_tmr(struct qla_tgt_mgmt_cmd *mcmd, uint32_t lun,
+                       uint8_t tmr_func, uint32_t tag)
+{
+       struct qla_tgt_sess *sess = mcmd->sess;
+       struct se_cmd *se_cmd = &mcmd->se_cmd;
+
+       return target_submit_tmr(se_cmd, sess->se_sess, NULL, lun, mcmd,
+                       tmr_func, GFP_ATOMIC, tag, TARGET_SCF_ACK_KREF);
+}
+
+static int tcm_qla2xxx_queue_data_in(struct se_cmd *se_cmd)
+{
+       struct qla_tgt_cmd *cmd = container_of(se_cmd,
+                               struct qla_tgt_cmd, se_cmd);
+
+       cmd->bufflen = se_cmd->data_length;
+       cmd->dma_data_direction = tcm_qla2xxx_mapping_dir(se_cmd);
+       cmd->aborted = (se_cmd->transport_state & CMD_T_ABORTED);
+
+       cmd->sg_cnt = se_cmd->t_data_nents;
+       cmd->sg = se_cmd->t_data_sg;
+       cmd->offset = 0;
+
+       /*
+        * Now queue completed DATA_IN the qla2xxx LLD and response ring
+        */
+       return qlt_xmit_response(cmd, QLA_TGT_XMIT_DATA|QLA_TGT_XMIT_STATUS,
+                               se_cmd->scsi_status);
+}
+
+static int tcm_qla2xxx_queue_status(struct se_cmd *se_cmd)
+{
+       struct qla_tgt_cmd *cmd = container_of(se_cmd,
+                               struct qla_tgt_cmd, se_cmd);
+       int xmit_type = QLA_TGT_XMIT_STATUS;
+
+       cmd->bufflen = se_cmd->data_length;
+       cmd->sg = NULL;
+       cmd->sg_cnt = 0;
+       cmd->offset = 0;
+       cmd->dma_data_direction = tcm_qla2xxx_mapping_dir(se_cmd);
+       cmd->aborted = (se_cmd->transport_state & CMD_T_ABORTED);
+
+       if (se_cmd->data_direction == DMA_FROM_DEVICE) {
+               /*
+                * For FCP_READ with CHECK_CONDITION status, clear cmd->bufflen
+                * for qla_tgt_xmit_response LLD code
+                */
+               se_cmd->se_cmd_flags |= SCF_UNDERFLOW_BIT;
+               se_cmd->residual_count = se_cmd->data_length;
+
+               cmd->bufflen = 0;
+       }
+       /*
+        * Now queue status response to qla2xxx LLD code and response ring
+        */
+       return qlt_xmit_response(cmd, xmit_type, se_cmd->scsi_status);
+}
+
+static int tcm_qla2xxx_queue_tm_rsp(struct se_cmd *se_cmd)
+{
+       struct se_tmr_req *se_tmr = se_cmd->se_tmr_req;
+       struct qla_tgt_mgmt_cmd *mcmd = container_of(se_cmd,
+                               struct qla_tgt_mgmt_cmd, se_cmd);
+
+       pr_debug("queue_tm_rsp: mcmd: %p func: 0x%02x response: 0x%02x\n",
+                       mcmd, se_tmr->function, se_tmr->response);
+       /*
+        * Do translation between TCM TM response codes and
+        * QLA2xxx FC TM response codes.
+        */
+       switch (se_tmr->response) {
+       case TMR_FUNCTION_COMPLETE:
+               mcmd->fc_tm_rsp = FC_TM_SUCCESS;
+               break;
+       case TMR_TASK_DOES_NOT_EXIST:
+               mcmd->fc_tm_rsp = FC_TM_BAD_CMD;
+               break;
+       case TMR_FUNCTION_REJECTED:
+               mcmd->fc_tm_rsp = FC_TM_REJECT;
+               break;
+       case TMR_LUN_DOES_NOT_EXIST:
+       default:
+               mcmd->fc_tm_rsp = FC_TM_FAILED;
+               break;
+       }
+       /*
+        * Queue the TM response to QLA2xxx LLD to build a
+        * CTIO response packet.
+        */
+       qlt_xmit_tm_rsp(mcmd);
+
+       return 0;
+}
+
+static u16 tcm_qla2xxx_get_fabric_sense_len(void)
+{
+       return 0;
+}
+
+static u16 tcm_qla2xxx_set_fabric_sense_len(struct se_cmd *se_cmd,
+                                       u32 sense_length)
+{
+       return 0;
+}
+
+/* Local pointer to allocated TCM configfs fabric module */
+struct target_fabric_configfs *tcm_qla2xxx_fabric_configfs;
+struct target_fabric_configfs *tcm_qla2xxx_npiv_fabric_configfs;
+
+static int tcm_qla2xxx_setup_nacl_from_rport(
+       struct se_portal_group *se_tpg,
+       struct se_node_acl *se_nacl,
+       struct tcm_qla2xxx_lport *lport,
+       struct tcm_qla2xxx_nacl *nacl,
+       u64 rport_wwnn)
+{
+       struct scsi_qla_host *vha = lport->qla_vha;
+       struct Scsi_Host *sh = vha->host;
+       struct fc_host_attrs *fc_host = shost_to_fc_host(sh);
+       struct fc_rport *rport;
+       unsigned long flags;
+       void *node;
+       int rc;
+
+       /*
+        * Scan the existing rports, and create a session for the
+        * explict NodeACL is an matching rport->node_name already
+        * exists.
+        */
+       spin_lock_irqsave(sh->host_lock, flags);
+       list_for_each_entry(rport, &fc_host->rports, peers) {
+               if (rport_wwnn != rport->node_name)
+                       continue;
+
+               pr_debug("Located existing rport_wwpn and rport->node_name: 0x%016LX, port_id: 0x%04x\n",
+                   rport->node_name, rport->port_id);
+               nacl->nport_id = rport->port_id;
+
+               spin_unlock_irqrestore(sh->host_lock, flags);
+
+               spin_lock_irqsave(&vha->hw->hardware_lock, flags);
+               node = btree_lookup32(&lport->lport_fcport_map, rport->port_id);
+               if (node) {
+                       rc = btree_update32(&lport->lport_fcport_map,
+                                           rport->port_id, se_nacl);
+               } else {
+                       rc = btree_insert32(&lport->lport_fcport_map,
+                                           rport->port_id, se_nacl,
+                                           GFP_ATOMIC);
+               }
+               spin_unlock_irqrestore(&vha->hw->hardware_lock, flags);
+
+               if (rc) {
+                       pr_err("Unable to insert se_nacl into fcport_map");
+                       WARN_ON(rc > 0);
+                       return rc;
+               }
+
+               pr_debug("Inserted into fcport_map: %p for WWNN: 0x%016LX, port_id: 0x%08x\n",
+                   se_nacl, rport_wwnn, nacl->nport_id);
+
+               return 1;
+       }
+       spin_unlock_irqrestore(sh->host_lock, flags);
+
+       return 0;
+}
+
+/*
+ * Expected to be called with struct qla_hw_data->hardware_lock held
+ */
+static void tcm_qla2xxx_clear_nacl_from_fcport_map(struct qla_tgt_sess *sess)
+{
+       struct se_node_acl *se_nacl = sess->se_sess->se_node_acl;
+       struct se_portal_group *se_tpg = se_nacl->se_tpg;
+       struct se_wwn *se_wwn = se_tpg->se_tpg_wwn;
+       struct tcm_qla2xxx_lport *lport = container_of(se_wwn,
+                               struct tcm_qla2xxx_lport, lport_wwn);
+       struct tcm_qla2xxx_nacl *nacl = container_of(se_nacl,
+                               struct tcm_qla2xxx_nacl, se_node_acl);
+       void *node;
+
+       pr_debug("fc_rport domain: port_id 0x%06x\n", nacl->nport_id);
+
+       node = btree_remove32(&lport->lport_fcport_map, nacl->nport_id);
+       WARN_ON(node && (node != se_nacl));
+
+       pr_debug("Removed from fcport_map: %p for WWNN: 0x%016LX, port_id: 0x%06x\n",
+           se_nacl, nacl->nport_wwnn, nacl->nport_id);
+}
+
+static void tcm_qla2xxx_put_sess(struct qla_tgt_sess *sess)
+{
+       target_put_session(sess->se_sess);
+}
+
+static void tcm_qla2xxx_shutdown_sess(struct qla_tgt_sess *sess)
+{
+       tcm_qla2xxx_shutdown_session(sess->se_sess);
+}
+
+static struct se_node_acl *tcm_qla2xxx_make_nodeacl(
+       struct se_portal_group *se_tpg,
+       struct config_group *group,
+       const char *name)
+{
+       struct se_wwn *se_wwn = se_tpg->se_tpg_wwn;
+       struct tcm_qla2xxx_lport *lport = container_of(se_wwn,
+                               struct tcm_qla2xxx_lport, lport_wwn);
+       struct se_node_acl *se_nacl, *se_nacl_new;
+       struct tcm_qla2xxx_nacl *nacl;
+       u64 wwnn;
+       u32 qla2xxx_nexus_depth;
+       int rc;
+
+       if (tcm_qla2xxx_parse_wwn(name, &wwnn, 1) < 0)
+               return ERR_PTR(-EINVAL);
+
+       se_nacl_new = tcm_qla2xxx_alloc_fabric_acl(se_tpg);
+       if (!se_nacl_new)
+               return ERR_PTR(-ENOMEM);
+/* #warning FIXME: Hardcoded qla2xxx_nexus depth in tcm_qla2xxx_make_nodeacl */
+       qla2xxx_nexus_depth = 1;
+
+       /*
+        * se_nacl_new may be released by core_tpg_add_initiator_node_acl()
+        * when converting a NodeACL from demo mode -> explict
+        */
+       se_nacl = core_tpg_add_initiator_node_acl(se_tpg, se_nacl_new,
+                               name, qla2xxx_nexus_depth);
+       if (IS_ERR(se_nacl)) {
+               tcm_qla2xxx_release_fabric_acl(se_tpg, se_nacl_new);
+               return se_nacl;
+       }
+       /*
+        * Locate our struct tcm_qla2xxx_nacl and set the FC Nport WWPN
+        */
+       nacl = container_of(se_nacl, struct tcm_qla2xxx_nacl, se_node_acl);
+       nacl->nport_wwnn = wwnn;
+       tcm_qla2xxx_format_wwn(&nacl->nport_name[0], TCM_QLA2XXX_NAMELEN, wwnn);
+       /*
+        * Setup a se_nacl handle based on an a matching struct fc_rport setup
+        * via drivers/scsi/qla2xxx/qla_init.c:qla2x00_reg_remote_port()
+        */
+       rc = tcm_qla2xxx_setup_nacl_from_rport(se_tpg, se_nacl, lport,
+                                       nacl, wwnn);
+       if (rc < 0) {
+               tcm_qla2xxx_release_fabric_acl(se_tpg, se_nacl_new);
+               return ERR_PTR(rc);
+       }
+
+       return se_nacl;
+}
+
+static void tcm_qla2xxx_drop_nodeacl(struct se_node_acl *se_acl)
+{
+       struct se_portal_group *se_tpg = se_acl->se_tpg;
+       struct tcm_qla2xxx_nacl *nacl = container_of(se_acl,
+                               struct tcm_qla2xxx_nacl, se_node_acl);
+
+       core_tpg_del_initiator_node_acl(se_tpg, se_acl, 1);
+       kfree(nacl);
+}
+
+/* Start items for tcm_qla2xxx_tpg_attrib_cit */
+
+#define DEF_QLA_TPG_ATTRIB(name)                                       \
+                                                                       \
+static ssize_t tcm_qla2xxx_tpg_attrib_show_##name(                     \
+       struct se_portal_group *se_tpg,                                 \
+       char *page)                                                     \
+{                                                                      \
+       struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg,              \
+                       struct tcm_qla2xxx_tpg, se_tpg);                \
+                                                                       \
+       return sprintf(page, "%u\n", QLA_TPG_ATTRIB(tpg)->name);        \
+}                                                                      \
+                                                                       \
+static ssize_t tcm_qla2xxx_tpg_attrib_store_##name(                    \
+       struct se_portal_group *se_tpg,                                 \
+       const char *page,                                               \
+       size_t count)                                                   \
+{                                                                      \
+       struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg,              \
+                       struct tcm_qla2xxx_tpg, se_tpg);                \
+       unsigned long val;                                              \
+       int ret;                                                        \
+                                                                       \
+       ret = kstrtoul(page, 0, &val);                                  \
+       if (ret < 0) {                                                  \
+               pr_err("kstrtoul() failed with"                         \
+                               " ret: %d\n", ret);                     \
+               return -EINVAL;                                         \
+       }                                                               \
+       ret = tcm_qla2xxx_set_attrib_##name(tpg, val);                  \
+                                                                       \
+       return (!ret) ? count : -EINVAL;                                \
+}
+
+#define DEF_QLA_TPG_ATTR_BOOL(_name)                                   \
+                                                                       \
+static int tcm_qla2xxx_set_attrib_##_name(                             \
+       struct tcm_qla2xxx_tpg *tpg,                                    \
+       unsigned long val)                                              \
+{                                                                      \
+       struct tcm_qla2xxx_tpg_attrib *a = &tpg->tpg_attrib;            \
+                                                                       \
+       if ((val != 0) && (val != 1)) {                                 \
+               pr_err("Illegal boolean value %lu\n", val);             \
+               return -EINVAL;                                         \
+       }                                                               \
+                                                                       \
+       a->_name = val;                                                 \
+       return 0;                                                       \
+}
+
+#define QLA_TPG_ATTR(_name, _mode) \
+       TF_TPG_ATTRIB_ATTR(tcm_qla2xxx, _name, _mode);
+
+/*
+ * Define tcm_qla2xxx_tpg_attrib_s_generate_node_acls
+ */
+DEF_QLA_TPG_ATTR_BOOL(generate_node_acls);
+DEF_QLA_TPG_ATTRIB(generate_node_acls);
+QLA_TPG_ATTR(generate_node_acls, S_IRUGO | S_IWUSR);
+
+/*
+ Define tcm_qla2xxx_attrib_s_cache_dynamic_acls
+ */
+DEF_QLA_TPG_ATTR_BOOL(cache_dynamic_acls);
+DEF_QLA_TPG_ATTRIB(cache_dynamic_acls);
+QLA_TPG_ATTR(cache_dynamic_acls, S_IRUGO | S_IWUSR);
+
+/*
+ * Define tcm_qla2xxx_tpg_attrib_s_demo_mode_write_protect
+ */
+DEF_QLA_TPG_ATTR_BOOL(demo_mode_write_protect);
+DEF_QLA_TPG_ATTRIB(demo_mode_write_protect);
+QLA_TPG_ATTR(demo_mode_write_protect, S_IRUGO | S_IWUSR);
+
+/*
+ * Define tcm_qla2xxx_tpg_attrib_s_prod_mode_write_protect
+ */
+DEF_QLA_TPG_ATTR_BOOL(prod_mode_write_protect);
+DEF_QLA_TPG_ATTRIB(prod_mode_write_protect);
+QLA_TPG_ATTR(prod_mode_write_protect, S_IRUGO | S_IWUSR);
+
+static struct configfs_attribute *tcm_qla2xxx_tpg_attrib_attrs[] = {
+       &tcm_qla2xxx_tpg_attrib_generate_node_acls.attr,
+       &tcm_qla2xxx_tpg_attrib_cache_dynamic_acls.attr,
+       &tcm_qla2xxx_tpg_attrib_demo_mode_write_protect.attr,
+       &tcm_qla2xxx_tpg_attrib_prod_mode_write_protect.attr,
+       NULL,
+};
+
+/* End items for tcm_qla2xxx_tpg_attrib_cit */
+
+static ssize_t tcm_qla2xxx_tpg_show_enable(
+       struct se_portal_group *se_tpg,
+       char *page)
+{
+       struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg,
+                       struct tcm_qla2xxx_tpg, se_tpg);
+
+       return snprintf(page, PAGE_SIZE, "%d\n",
+                       atomic_read(&tpg->lport_tpg_enabled));
+}
+
+static ssize_t tcm_qla2xxx_tpg_store_enable(
+       struct se_portal_group *se_tpg,
+       const char *page,
+       size_t count)
+{
+       struct se_wwn *se_wwn = se_tpg->se_tpg_wwn;
+       struct tcm_qla2xxx_lport *lport = container_of(se_wwn,
+                       struct tcm_qla2xxx_lport, lport_wwn);
+       struct scsi_qla_host *vha = lport->qla_vha;
+       struct qla_hw_data *ha = vha->hw;
+       struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg,
+                       struct tcm_qla2xxx_tpg, se_tpg);
+       unsigned long op;
+       int rc;
+
+       rc = kstrtoul(page, 0, &op);
+       if (rc < 0) {
+               pr_err("kstrtoul() returned %d\n", rc);
+               return -EINVAL;
+       }
+       if ((op != 1) && (op != 0)) {
+               pr_err("Illegal value for tpg_enable: %lu\n", op);
+               return -EINVAL;
+       }
+
+       if (op) {
+               atomic_set(&tpg->lport_tpg_enabled, 1);
+               qlt_enable_vha(vha);
+       } else {
+               if (!ha->tgt.qla_tgt) {
+                       pr_err("truct qla_hw_data *ha->tgt.qla_tgt is NULL\n");
+                       return -ENODEV;
+               }
+               atomic_set(&tpg->lport_tpg_enabled, 0);
+               qlt_stop_phase1(ha->tgt.qla_tgt);
+       }
+
+       return count;
+}
+
+TF_TPG_BASE_ATTR(tcm_qla2xxx, enable, S_IRUGO | S_IWUSR);
+
+static struct configfs_attribute *tcm_qla2xxx_tpg_attrs[] = {
+       &tcm_qla2xxx_tpg_enable.attr,
+       NULL,
+};
+
+static struct se_portal_group *tcm_qla2xxx_make_tpg(
+       struct se_wwn *wwn,
+       struct config_group *group,
+       const char *name)
+{
+       struct tcm_qla2xxx_lport *lport = container_of(wwn,
+                       struct tcm_qla2xxx_lport, lport_wwn);
+       struct tcm_qla2xxx_tpg *tpg;
+       unsigned long tpgt;
+       int ret;
+
+       if (strstr(name, "tpgt_") != name)
+               return ERR_PTR(-EINVAL);
+       if (kstrtoul(name + 5, 10, &tpgt) || tpgt > USHRT_MAX)
+               return ERR_PTR(-EINVAL);
+
+       if (!lport->qla_npiv_vp && (tpgt != 1)) {
+               pr_err("In non NPIV mode, a single TPG=1 is used for HW port mappings\n");
+               return ERR_PTR(-ENOSYS);
+       }
+
+       tpg = kzalloc(sizeof(struct tcm_qla2xxx_tpg), GFP_KERNEL);
+       if (!tpg) {
+               pr_err("Unable to allocate struct tcm_qla2xxx_tpg\n");
+               return ERR_PTR(-ENOMEM);
+       }
+       tpg->lport = lport;
+       tpg->lport_tpgt = tpgt;
+       /*
+        * By default allow READ-ONLY TPG demo-mode access w/ cached dynamic
+        * NodeACLs
+        */
+       QLA_TPG_ATTRIB(tpg)->generate_node_acls = 1;
+       QLA_TPG_ATTRIB(tpg)->demo_mode_write_protect = 1;
+       QLA_TPG_ATTRIB(tpg)->cache_dynamic_acls = 1;
+
+       ret = core_tpg_register(&tcm_qla2xxx_fabric_configfs->tf_ops, wwn,
+                               &tpg->se_tpg, tpg, TRANSPORT_TPG_TYPE_NORMAL);
+       if (ret < 0) {
+               kfree(tpg);
+               return NULL;
+       }
+       /*
+        * Setup local TPG=1 pointer for non NPIV mode.
+        */
+       if (lport->qla_npiv_vp == NULL)
+               lport->tpg_1 = tpg;
+
+       return &tpg->se_tpg;
+}
+
+static void tcm_qla2xxx_drop_tpg(struct se_portal_group *se_tpg)
+{
+       struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg,
+                       struct tcm_qla2xxx_tpg, se_tpg);
+       struct tcm_qla2xxx_lport *lport = tpg->lport;
+       struct scsi_qla_host *vha = lport->qla_vha;
+       struct qla_hw_data *ha = vha->hw;
+       /*
+        * Call into qla2x_target.c LLD logic to shutdown the active
+        * FC Nexuses and disable target mode operation for this qla_hw_data
+        */
+       if (ha->tgt.qla_tgt && !ha->tgt.qla_tgt->tgt_stop)
+               qlt_stop_phase1(ha->tgt.qla_tgt);
+
+       core_tpg_deregister(se_tpg);
+       /*
+        * Clear local TPG=1 pointer for non NPIV mode.
+        */
+       if (lport->qla_npiv_vp == NULL)
+               lport->tpg_1 = NULL;
+
+       kfree(tpg);
+}
+
+static struct se_portal_group *tcm_qla2xxx_npiv_make_tpg(
+       struct se_wwn *wwn,
+       struct config_group *group,
+       const char *name)
+{
+       struct tcm_qla2xxx_lport *lport = container_of(wwn,
+                       struct tcm_qla2xxx_lport, lport_wwn);
+       struct tcm_qla2xxx_tpg *tpg;
+       unsigned long tpgt;
+       int ret;
+
+       if (strstr(name, "tpgt_") != name)
+               return ERR_PTR(-EINVAL);
+       if (kstrtoul(name + 5, 10, &tpgt) || tpgt > USHRT_MAX)
+               return ERR_PTR(-EINVAL);
+
+       tpg = kzalloc(sizeof(struct tcm_qla2xxx_tpg), GFP_KERNEL);
+       if (!tpg) {
+               pr_err("Unable to allocate struct tcm_qla2xxx_tpg\n");
+               return ERR_PTR(-ENOMEM);
+       }
+       tpg->lport = lport;
+       tpg->lport_tpgt = tpgt;
+
+       ret = core_tpg_register(&tcm_qla2xxx_npiv_fabric_configfs->tf_ops, wwn,
+                               &tpg->se_tpg, tpg, TRANSPORT_TPG_TYPE_NORMAL);
+       if (ret < 0) {
+               kfree(tpg);
+               return NULL;
+       }
+       return &tpg->se_tpg;
+}
+
+/*
+ * Expected to be called with struct qla_hw_data->hardware_lock held
+ */
+static struct qla_tgt_sess *tcm_qla2xxx_find_sess_by_s_id(
+       scsi_qla_host_t *vha,
+       const uint8_t *s_id)
+{
+       struct qla_hw_data *ha = vha->hw;
+       struct tcm_qla2xxx_lport *lport;
+       struct se_node_acl *se_nacl;
+       struct tcm_qla2xxx_nacl *nacl;
+       u32 key;
+
+       lport = ha->tgt.target_lport_ptr;
+       if (!lport) {
+               pr_err("Unable to locate struct tcm_qla2xxx_lport\n");
+               dump_stack();
+               return NULL;
+       }
+
+       key = (((unsigned long)s_id[0] << 16) |
+              ((unsigned long)s_id[1] << 8) |
+              (unsigned long)s_id[2]);
+       pr_debug("find_sess_by_s_id: 0x%06x\n", key);
+
+       se_nacl = btree_lookup32(&lport->lport_fcport_map, key);
+       if (!se_nacl) {
+               pr_debug("Unable to locate s_id: 0x%06x\n", key);
+               return NULL;
+       }
+       pr_debug("find_sess_by_s_id: located se_nacl: %p, initiatorname: %s\n",
+           se_nacl, se_nacl->initiatorname);
+
+       nacl = container_of(se_nacl, struct tcm_qla2xxx_nacl, se_node_acl);
+       if (!nacl->qla_tgt_sess) {
+               pr_err("Unable to locate struct qla_tgt_sess\n");
+               return NULL;
+       }
+
+       return nacl->qla_tgt_sess;
+}
+
+/*
+ * Expected to be called with struct qla_hw_data->hardware_lock held
+ */
+static void tcm_qla2xxx_set_sess_by_s_id(
+       struct tcm_qla2xxx_lport *lport,
+       struct se_node_acl *new_se_nacl,
+       struct tcm_qla2xxx_nacl *nacl,
+       struct se_session *se_sess,
+       struct qla_tgt_sess *qla_tgt_sess,
+       uint8_t *s_id)
+{
+       u32 key;
+       void *slot;
+       int rc;
+
+       key = (((unsigned long)s_id[0] << 16) |
+              ((unsigned long)s_id[1] << 8) |
+              (unsigned long)s_id[2]);
+       pr_debug("set_sess_by_s_id: %06x\n", key);
+
+       slot = btree_lookup32(&lport->lport_fcport_map, key);
+       if (!slot) {
+               if (new_se_nacl) {
+                       pr_debug("Setting up new fc_port entry to new_se_nacl\n");
+                       nacl->nport_id = key;
+                       rc = btree_insert32(&lport->lport_fcport_map, key,
+                                       new_se_nacl, GFP_ATOMIC);
+                       if (rc)
+                               printk(KERN_ERR "Unable to insert s_id into fcport_map: %06x\n",
+                                   (int)key);
+               } else {
+                       pr_debug("Wiping nonexisting fc_port entry\n");
+               }
+
+               qla_tgt_sess->se_sess = se_sess;
+               nacl->qla_tgt_sess = qla_tgt_sess;
+               return;
+       }
+
+       if (nacl->qla_tgt_sess) {
+               if (new_se_nacl == NULL) {
+                       pr_debug("Clearing existing nacl->qla_tgt_sess and fc_port entry\n");
+                       btree_remove32(&lport->lport_fcport_map, key);
+                       nacl->qla_tgt_sess = NULL;
+                       return;
+               }
+               pr_debug("Replacing existing nacl->qla_tgt_sess and fc_port entry\n");
+               btree_update32(&lport->lport_fcport_map, key, new_se_nacl);
+               qla_tgt_sess->se_sess = se_sess;
+               nacl->qla_tgt_sess = qla_tgt_sess;
+               return;
+       }
+
+       if (new_se_nacl == NULL) {
+               pr_debug("Clearing existing fc_port entry\n");
+               btree_remove32(&lport->lport_fcport_map, key);
+               return;
+       }
+
+       pr_debug("Replacing existing fc_port entry w/o active nacl->qla_tgt_sess\n");
+       btree_update32(&lport->lport_fcport_map, key, new_se_nacl);
+       qla_tgt_sess->se_sess = se_sess;
+       nacl->qla_tgt_sess = qla_tgt_sess;
+
+       pr_debug("Setup nacl->qla_tgt_sess %p by s_id for se_nacl: %p, initiatorname: %s\n",
+           nacl->qla_tgt_sess, new_se_nacl, new_se_nacl->initiatorname);
+}
+
+/*
+ * Expected to be called with struct qla_hw_data->hardware_lock held
+ */
+static struct qla_tgt_sess *tcm_qla2xxx_find_sess_by_loop_id(
+       scsi_qla_host_t *vha,
+       const uint16_t loop_id)
+{
+       struct qla_hw_data *ha = vha->hw;
+       struct tcm_qla2xxx_lport *lport;
+       struct se_node_acl *se_nacl;
+       struct tcm_qla2xxx_nacl *nacl;
+       struct tcm_qla2xxx_fc_loopid *fc_loopid;
+
+       lport = ha->tgt.target_lport_ptr;
+       if (!lport) {
+               pr_err("Unable to locate struct tcm_qla2xxx_lport\n");
+               dump_stack();
+               return NULL;
+       }
+
+       pr_debug("find_sess_by_loop_id: Using loop_id: 0x%04x\n", loop_id);
+
+       fc_loopid = lport->lport_loopid_map + loop_id;
+       se_nacl = fc_loopid->se_nacl;
+       if (!se_nacl) {
+               pr_debug("Unable to locate se_nacl by loop_id: 0x%04x\n",
+                   loop_id);
+               return NULL;
+       }
+
+       nacl = container_of(se_nacl, struct tcm_qla2xxx_nacl, se_node_acl);
+
+       if (!nacl->qla_tgt_sess) {
+               pr_err("Unable to locate struct qla_tgt_sess\n");
+               return NULL;
+       }
+
+       return nacl->qla_tgt_sess;
+}
+
+/*
+ * Expected to be called with struct qla_hw_data->hardware_lock held
+ */
+static void tcm_qla2xxx_set_sess_by_loop_id(
+       struct tcm_qla2xxx_lport *lport,
+       struct se_node_acl *new_se_nacl,
+       struct tcm_qla2xxx_nacl *nacl,
+       struct se_session *se_sess,
+       struct qla_tgt_sess *qla_tgt_sess,
+       uint16_t loop_id)
+{
+       struct se_node_acl *saved_nacl;
+       struct tcm_qla2xxx_fc_loopid *fc_loopid;
+
+       pr_debug("set_sess_by_loop_id: Using loop_id: 0x%04x\n", loop_id);
+
+       fc_loopid = &((struct tcm_qla2xxx_fc_loopid *)
+                       lport->lport_loopid_map)[loop_id];
+
+       saved_nacl = fc_loopid->se_nacl;
+       if (!saved_nacl) {
+               pr_debug("Setting up new fc_loopid->se_nacl to new_se_nacl\n");
+               fc_loopid->se_nacl = new_se_nacl;
+               if (qla_tgt_sess->se_sess != se_sess)
+                       qla_tgt_sess->se_sess = se_sess;
+               if (nacl->qla_tgt_sess != qla_tgt_sess)
+                       nacl->qla_tgt_sess = qla_tgt_sess;
+               return;
+       }
+
+       if (nacl->qla_tgt_sess) {
+               if (new_se_nacl == NULL) {
+                       pr_debug("Clearing nacl->qla_tgt_sess and fc_loopid->se_nacl\n");
+                       fc_loopid->se_nacl = NULL;
+                       nacl->qla_tgt_sess = NULL;
+                       return;
+               }
+
+               pr_debug("Replacing existing nacl->qla_tgt_sess and fc_loopid->se_nacl\n");
+               fc_loopid->se_nacl = new_se_nacl;
+               if (qla_tgt_sess->se_sess != se_sess)
+                       qla_tgt_sess->se_sess = se_sess;
+               if (nacl->qla_tgt_sess != qla_tgt_sess)
+                       nacl->qla_tgt_sess = qla_tgt_sess;
+               return;
+       }
+
+       if (new_se_nacl == NULL) {
+               pr_debug("Clearing fc_loopid->se_nacl\n");
+               fc_loopid->se_nacl = NULL;
+               return;
+       }
+
+       pr_debug("Replacing existing fc_loopid->se_nacl w/o active nacl->qla_tgt_sess\n");
+       fc_loopid->se_nacl = new_se_nacl;
+       if (qla_tgt_sess->se_sess != se_sess)
+               qla_tgt_sess->se_sess = se_sess;
+       if (nacl->qla_tgt_sess != qla_tgt_sess)
+               nacl->qla_tgt_sess = qla_tgt_sess;
+
+       pr_debug("Setup nacl->qla_tgt_sess %p by loop_id for se_nacl: %p, initiatorname: %s\n",
+           nacl->qla_tgt_sess, new_se_nacl, new_se_nacl->initiatorname);
+}
+
+static void tcm_qla2xxx_free_session(struct qla_tgt_sess *sess)
+{
+       struct qla_tgt *tgt = sess->tgt;
+       struct qla_hw_data *ha = tgt->ha;
+       struct se_session *se_sess;
+       struct se_node_acl *se_nacl;
+       struct tcm_qla2xxx_lport *lport;
+       struct tcm_qla2xxx_nacl *nacl;
+       unsigned char be_sid[3];
+       unsigned long flags;
+
+       BUG_ON(in_interrupt());
+
+       se_sess = sess->se_sess;
+       if (!se_sess) {
+               pr_err("struct qla_tgt_sess->se_sess is NULL\n");
+               dump_stack();
+               return;
+       }
+       se_nacl = se_sess->se_node_acl;
+       nacl = container_of(se_nacl, struct tcm_qla2xxx_nacl, se_node_acl);
+
+       lport = ha->tgt.target_lport_ptr;
+       if (!lport) {
+               pr_err("Unable to locate struct tcm_qla2xxx_lport\n");
+               dump_stack();
+               return;
+       }
+       target_wait_for_sess_cmds(se_sess, 0);
+       /*
+        * And now clear the se_nacl and session pointers from our HW lport
+        * mappings for fabric S_ID and LOOP_ID.
+        */
+       memset(&be_sid, 0, 3);
+       be_sid[0] = sess->s_id.b.domain;
+       be_sid[1] = sess->s_id.b.area;
+       be_sid[2] = sess->s_id.b.al_pa;
+
+       spin_lock_irqsave(&ha->hardware_lock, flags);
+       tcm_qla2xxx_set_sess_by_s_id(lport, NULL, nacl, se_sess,
+                       sess, be_sid);
+       tcm_qla2xxx_set_sess_by_loop_id(lport, NULL, nacl, se_sess,
+                       sess, sess->loop_id);
+       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+       transport_deregister_session_configfs(sess->se_sess);
+       transport_deregister_session(sess->se_sess);
+}
+
+/*
+ * Called via qlt_create_sess():ha->qla2x_tmpl->check_initiator_node_acl()
+ * to locate struct se_node_acl
+ */
+static int tcm_qla2xxx_check_initiator_node_acl(
+       scsi_qla_host_t *vha,
+       unsigned char *fc_wwpn,
+       void *qla_tgt_sess,
+       uint8_t *s_id,
+       uint16_t loop_id)
+{
+       struct qla_hw_data *ha = vha->hw;
+       struct tcm_qla2xxx_lport *lport;
+       struct tcm_qla2xxx_tpg *tpg;
+       struct tcm_qla2xxx_nacl *nacl;
+       struct se_portal_group *se_tpg;
+       struct se_node_acl *se_nacl;
+       struct se_session *se_sess;
+       struct qla_tgt_sess *sess = qla_tgt_sess;
+       unsigned char port_name[36];
+       unsigned long flags;
+
+       lport = ha->tgt.target_lport_ptr;
+       if (!lport) {
+               pr_err("Unable to locate struct tcm_qla2xxx_lport\n");
+               dump_stack();
+               return -EINVAL;
+       }
+       /*
+        * Locate the TPG=1 reference..
+        */
+       tpg = lport->tpg_1;
+       if (!tpg) {
+               pr_err("Unable to lcoate struct tcm_qla2xxx_lport->tpg_1\n");
+               return -EINVAL;
+       }
+       se_tpg = &tpg->se_tpg;
+
+       se_sess = transport_init_session();
+       if (IS_ERR(se_sess)) {
+               pr_err("Unable to initialize struct se_session\n");
+               return PTR_ERR(se_sess);
+       }
+       /*
+        * Format the FCP Initiator port_name into colon seperated values to
+        * match the format by tcm_qla2xxx explict ConfigFS NodeACLs.
+        */
+       memset(&port_name, 0, 36);
+       snprintf(port_name, 36, "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
+               fc_wwpn[0], fc_wwpn[1], fc_wwpn[2], fc_wwpn[3], fc_wwpn[4],
+               fc_wwpn[5], fc_wwpn[6], fc_wwpn[7]);
+       /*
+        * Locate our struct se_node_acl either from an explict NodeACL created
+        * via ConfigFS, or via running in TPG demo mode.
+        */
+       se_sess->se_node_acl = core_tpg_check_initiator_node_acl(se_tpg,
+                                       port_name);
+       if (!se_sess->se_node_acl) {
+               transport_free_session(se_sess);
+               return -EINVAL;
+       }
+       se_nacl = se_sess->se_node_acl;
+       nacl = container_of(se_nacl, struct tcm_qla2xxx_nacl, se_node_acl);
+       /*
+        * And now setup the new se_nacl and session pointers into our HW lport
+        * mappings for fabric S_ID and LOOP_ID.
+        */
+       spin_lock_irqsave(&ha->hardware_lock, flags);
+       tcm_qla2xxx_set_sess_by_s_id(lport, se_nacl, nacl, se_sess,
+                       qla_tgt_sess, s_id);
+       tcm_qla2xxx_set_sess_by_loop_id(lport, se_nacl, nacl, se_sess,
+                       qla_tgt_sess, loop_id);
+       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+       /*
+        * Finally register the new FC Nexus with TCM
+        */
+       __transport_register_session(se_nacl->se_tpg, se_nacl, se_sess, sess);
+
+       return 0;
+}
+
+/*
+ * Calls into tcm_qla2xxx used by qla2xxx LLD I/O path.
+ */
+static struct qla_tgt_func_tmpl tcm_qla2xxx_template = {
+       .handle_cmd             = tcm_qla2xxx_handle_cmd,
+       .handle_data            = tcm_qla2xxx_handle_data,
+       .handle_tmr             = tcm_qla2xxx_handle_tmr,
+       .free_cmd               = tcm_qla2xxx_free_cmd,
+       .free_mcmd              = tcm_qla2xxx_free_mcmd,
+       .free_session           = tcm_qla2xxx_free_session,
+       .check_initiator_node_acl = tcm_qla2xxx_check_initiator_node_acl,
+       .find_sess_by_s_id      = tcm_qla2xxx_find_sess_by_s_id,
+       .find_sess_by_loop_id   = tcm_qla2xxx_find_sess_by_loop_id,
+       .clear_nacl_from_fcport_map = tcm_qla2xxx_clear_nacl_from_fcport_map,
+       .put_sess               = tcm_qla2xxx_put_sess,
+       .shutdown_sess          = tcm_qla2xxx_shutdown_sess,
+};
+
+static int tcm_qla2xxx_init_lport(struct tcm_qla2xxx_lport *lport)
+{
+       int rc;
+
+       rc = btree_init32(&lport->lport_fcport_map);
+       if (rc) {
+               pr_err("Unable to initialize lport->lport_fcport_map btree\n");
+               return rc;
+       }
+
+       lport->lport_loopid_map = vmalloc(sizeof(struct tcm_qla2xxx_fc_loopid) *
+                               65536);
+       if (!lport->lport_loopid_map) {
+               pr_err("Unable to allocate lport->lport_loopid_map of %zu bytes\n",
+                   sizeof(struct tcm_qla2xxx_fc_loopid) * 65536);
+               btree_destroy32(&lport->lport_fcport_map);
+               return -ENOMEM;
+       }
+       memset(lport->lport_loopid_map, 0, sizeof(struct tcm_qla2xxx_fc_loopid)
+              * 65536);
+       pr_debug("qla2xxx: Allocated lport_loopid_map of %zu bytes\n",
+              sizeof(struct tcm_qla2xxx_fc_loopid) * 65536);
+       return 0;
+}
+
+static int tcm_qla2xxx_lport_register_cb(struct scsi_qla_host *vha)
+{
+       struct qla_hw_data *ha = vha->hw;
+       struct tcm_qla2xxx_lport *lport;
+       /*
+        * Setup local pointer to vha, NPIV VP pointer (if present) and
+        * vha->tcm_lport pointer
+        */
+       lport = (struct tcm_qla2xxx_lport *)ha->tgt.target_lport_ptr;
+       lport->qla_vha = vha;
+
+       return 0;
+}
+
+static struct se_wwn *tcm_qla2xxx_make_lport(
+       struct target_fabric_configfs *tf,
+       struct config_group *group,
+       const char *name)
+{
+       struct tcm_qla2xxx_lport *lport;
+       u64 wwpn;
+       int ret = -ENODEV;
+
+       if (tcm_qla2xxx_parse_wwn(name, &wwpn, 1) < 0)
+               return ERR_PTR(-EINVAL);
+
+       lport = kzalloc(sizeof(struct tcm_qla2xxx_lport), GFP_KERNEL);
+       if (!lport) {
+               pr_err("Unable to allocate struct tcm_qla2xxx_lport\n");
+               return ERR_PTR(-ENOMEM);
+       }
+       lport->lport_wwpn = wwpn;
+       tcm_qla2xxx_format_wwn(&lport->lport_name[0], TCM_QLA2XXX_NAMELEN,
+                               wwpn);
+
+       ret = tcm_qla2xxx_init_lport(lport);
+       if (ret != 0)
+               goto out;
+
+       ret = qlt_lport_register(&tcm_qla2xxx_template, wwpn,
+                               tcm_qla2xxx_lport_register_cb, lport);
+       if (ret != 0)
+               goto out_lport;
+
+       return &lport->lport_wwn;
+out_lport:
+       vfree(lport->lport_loopid_map);
+       btree_destroy32(&lport->lport_fcport_map);
+out:
+       kfree(lport);
+       return ERR_PTR(ret);
+}
+
+static void tcm_qla2xxx_drop_lport(struct se_wwn *wwn)
+{
+       struct tcm_qla2xxx_lport *lport = container_of(wwn,
+                       struct tcm_qla2xxx_lport, lport_wwn);
+       struct scsi_qla_host *vha = lport->qla_vha;
+       struct qla_hw_data *ha = vha->hw;
+       struct se_node_acl *node;
+       u32 key = 0;
+
+       /*
+        * Call into qla2x_target.c LLD logic to complete the
+        * shutdown of struct qla_tgt after the call to
+        * qlt_stop_phase1() from tcm_qla2xxx_drop_tpg() above..
+        */
+       if (ha->tgt.qla_tgt && !ha->tgt.qla_tgt->tgt_stopped)
+               qlt_stop_phase2(ha->tgt.qla_tgt);
+
+       qlt_lport_deregister(vha);
+
+       vfree(lport->lport_loopid_map);
+       btree_for_each_safe32(&lport->lport_fcport_map, key, node)
+               btree_remove32(&lport->lport_fcport_map, key);
+       btree_destroy32(&lport->lport_fcport_map);
+       kfree(lport);
+}
+
+static struct se_wwn *tcm_qla2xxx_npiv_make_lport(
+       struct target_fabric_configfs *tf,
+       struct config_group *group,
+       const char *name)
+{
+       struct tcm_qla2xxx_lport *lport;
+       u64 npiv_wwpn, npiv_wwnn;
+       int ret;
+
+       if (tcm_qla2xxx_npiv_parse_wwn(name, strlen(name)+1,
+                               &npiv_wwpn, &npiv_wwnn) < 0)
+               return ERR_PTR(-EINVAL);
+
+       lport = kzalloc(sizeof(struct tcm_qla2xxx_lport), GFP_KERNEL);
+       if (!lport) {
+               pr_err("Unable to allocate struct tcm_qla2xxx_lport for NPIV\n");
+               return ERR_PTR(-ENOMEM);
+       }
+       lport->lport_npiv_wwpn = npiv_wwpn;
+       lport->lport_npiv_wwnn = npiv_wwnn;
+       tcm_qla2xxx_npiv_format_wwn(&lport->lport_npiv_name[0],
+                       TCM_QLA2XXX_NAMELEN, npiv_wwpn, npiv_wwnn);
+
+/* FIXME: tcm_qla2xxx_npiv_make_lport */
+       ret = -ENOSYS;
+       if (ret != 0)
+               goto out;
+
+       return &lport->lport_wwn;
+out:
+       kfree(lport);
+       return ERR_PTR(ret);
+}
+
+static void tcm_qla2xxx_npiv_drop_lport(struct se_wwn *wwn)
+{
+       struct tcm_qla2xxx_lport *lport = container_of(wwn,
+                       struct tcm_qla2xxx_lport, lport_wwn);
+       struct scsi_qla_host *vha = lport->qla_vha;
+       struct Scsi_Host *sh = vha->host;
+       /*
+        * Notify libfc that we want to release the lport->npiv_vport
+        */
+       fc_vport_terminate(lport->npiv_vport);
+
+       scsi_host_put(sh);
+       kfree(lport);
+}
+
+
+static ssize_t tcm_qla2xxx_wwn_show_attr_version(
+       struct target_fabric_configfs *tf,
+       char *page)
+{
+       return sprintf(page,
+           "TCM QLOGIC QLA2XXX NPIV capable fabric module %s on %s/%s on "
+           UTS_RELEASE"\n", TCM_QLA2XXX_VERSION, utsname()->sysname,
+           utsname()->machine);
+}
+
+TF_WWN_ATTR_RO(tcm_qla2xxx, version);
+
+static struct configfs_attribute *tcm_qla2xxx_wwn_attrs[] = {
+       &tcm_qla2xxx_wwn_version.attr,
+       NULL,
+};
+
+static struct target_core_fabric_ops tcm_qla2xxx_ops = {
+       .get_fabric_name                = tcm_qla2xxx_get_fabric_name,
+       .get_fabric_proto_ident         = tcm_qla2xxx_get_fabric_proto_ident,
+       .tpg_get_wwn                    = tcm_qla2xxx_get_fabric_wwn,
+       .tpg_get_tag                    = tcm_qla2xxx_get_tag,
+       .tpg_get_default_depth          = tcm_qla2xxx_get_default_depth,
+       .tpg_get_pr_transport_id        = tcm_qla2xxx_get_pr_transport_id,
+       .tpg_get_pr_transport_id_len    = tcm_qla2xxx_get_pr_transport_id_len,
+       .tpg_parse_pr_out_transport_id  = tcm_qla2xxx_parse_pr_out_transport_id,
+       .tpg_check_demo_mode            = tcm_qla2xxx_check_demo_mode,
+       .tpg_check_demo_mode_cache      = tcm_qla2xxx_check_demo_mode_cache,
+       .tpg_check_demo_mode_write_protect =
+                                       tcm_qla2xxx_check_demo_write_protect,
+       .tpg_check_prod_mode_write_protect =
+                                       tcm_qla2xxx_check_prod_write_protect,
+       .tpg_check_demo_mode_login_only = tcm_qla2xxx_check_true,
+       .tpg_alloc_fabric_acl           = tcm_qla2xxx_alloc_fabric_acl,
+       .tpg_release_fabric_acl         = tcm_qla2xxx_release_fabric_acl,
+       .tpg_get_inst_index             = tcm_qla2xxx_tpg_get_inst_index,
+       .new_cmd_map                    = NULL,
+       .check_stop_free                = tcm_qla2xxx_check_stop_free,
+       .release_cmd                    = tcm_qla2xxx_release_cmd,
+       .shutdown_session               = tcm_qla2xxx_shutdown_session,
+       .close_session                  = tcm_qla2xxx_close_session,
+       .sess_get_index                 = tcm_qla2xxx_sess_get_index,
+       .sess_get_initiator_sid         = NULL,
+       .write_pending                  = tcm_qla2xxx_write_pending,
+       .write_pending_status           = tcm_qla2xxx_write_pending_status,
+       .set_default_node_attributes    = tcm_qla2xxx_set_default_node_attrs,
+       .get_task_tag                   = tcm_qla2xxx_get_task_tag,
+       .get_cmd_state                  = tcm_qla2xxx_get_cmd_state,
+       .queue_data_in                  = tcm_qla2xxx_queue_data_in,
+       .queue_status                   = tcm_qla2xxx_queue_status,
+       .queue_tm_rsp                   = tcm_qla2xxx_queue_tm_rsp,
+       .get_fabric_sense_len           = tcm_qla2xxx_get_fabric_sense_len,
+       .set_fabric_sense_len           = tcm_qla2xxx_set_fabric_sense_len,
+       /*
+        * Setup function pointers for generic logic in
+        * target_core_fabric_configfs.c
+        */
+       .fabric_make_wwn                = tcm_qla2xxx_make_lport,
+       .fabric_drop_wwn                = tcm_qla2xxx_drop_lport,
+       .fabric_make_tpg                = tcm_qla2xxx_make_tpg,
+       .fabric_drop_tpg                = tcm_qla2xxx_drop_tpg,
+       .fabric_post_link               = NULL,
+       .fabric_pre_unlink              = NULL,
+       .fabric_make_np                 = NULL,
+       .fabric_drop_np                 = NULL,
+       .fabric_make_nodeacl            = tcm_qla2xxx_make_nodeacl,
+       .fabric_drop_nodeacl            = tcm_qla2xxx_drop_nodeacl,
+};
+
+static struct target_core_fabric_ops tcm_qla2xxx_npiv_ops = {
+       .get_fabric_name                = tcm_qla2xxx_npiv_get_fabric_name,
+       .get_fabric_proto_ident         = tcm_qla2xxx_get_fabric_proto_ident,
+       .tpg_get_wwn                    = tcm_qla2xxx_npiv_get_fabric_wwn,
+       .tpg_get_tag                    = tcm_qla2xxx_get_tag,
+       .tpg_get_default_depth          = tcm_qla2xxx_get_default_depth,
+       .tpg_get_pr_transport_id        = tcm_qla2xxx_get_pr_transport_id,
+       .tpg_get_pr_transport_id_len    = tcm_qla2xxx_get_pr_transport_id_len,
+       .tpg_parse_pr_out_transport_id  = tcm_qla2xxx_parse_pr_out_transport_id,
+       .tpg_check_demo_mode            = tcm_qla2xxx_check_false,
+       .tpg_check_demo_mode_cache      = tcm_qla2xxx_check_true,
+       .tpg_check_demo_mode_write_protect = tcm_qla2xxx_check_true,
+       .tpg_check_prod_mode_write_protect = tcm_qla2xxx_check_false,
+       .tpg_check_demo_mode_login_only = tcm_qla2xxx_check_true,
+       .tpg_alloc_fabric_acl           = tcm_qla2xxx_alloc_fabric_acl,
+       .tpg_release_fabric_acl         = tcm_qla2xxx_release_fabric_acl,
+       .tpg_get_inst_index             = tcm_qla2xxx_tpg_get_inst_index,
+       .release_cmd                    = tcm_qla2xxx_release_cmd,
+       .shutdown_session               = tcm_qla2xxx_shutdown_session,
+       .close_session                  = tcm_qla2xxx_close_session,
+       .sess_get_index                 = tcm_qla2xxx_sess_get_index,
+       .sess_get_initiator_sid         = NULL,
+       .write_pending                  = tcm_qla2xxx_write_pending,
+       .write_pending_status           = tcm_qla2xxx_write_pending_status,
+       .set_default_node_attributes    = tcm_qla2xxx_set_default_node_attrs,
+       .get_task_tag                   = tcm_qla2xxx_get_task_tag,
+       .get_cmd_state                  = tcm_qla2xxx_get_cmd_state,
+       .queue_data_in                  = tcm_qla2xxx_queue_data_in,
+       .queue_status                   = tcm_qla2xxx_queue_status,
+       .queue_tm_rsp                   = tcm_qla2xxx_queue_tm_rsp,
+       .get_fabric_sense_len           = tcm_qla2xxx_get_fabric_sense_len,
+       .set_fabric_sense_len           = tcm_qla2xxx_set_fabric_sense_len,
+       /*
+        * Setup function pointers for generic logic in
+        * target_core_fabric_configfs.c
+        */
+       .fabric_make_wwn                = tcm_qla2xxx_npiv_make_lport,
+       .fabric_drop_wwn                = tcm_qla2xxx_npiv_drop_lport,
+       .fabric_make_tpg                = tcm_qla2xxx_npiv_make_tpg,
+       .fabric_drop_tpg                = tcm_qla2xxx_drop_tpg,
+       .fabric_post_link               = NULL,
+       .fabric_pre_unlink              = NULL,
+       .fabric_make_np                 = NULL,
+       .fabric_drop_np                 = NULL,
+       .fabric_make_nodeacl            = tcm_qla2xxx_make_nodeacl,
+       .fabric_drop_nodeacl            = tcm_qla2xxx_drop_nodeacl,
+};
+
+static int tcm_qla2xxx_register_configfs(void)
+{
+       struct target_fabric_configfs *fabric, *npiv_fabric;
+       int ret;
+
+       pr_debug("TCM QLOGIC QLA2XXX fabric module %s on %s/%s on "
+           UTS_RELEASE"\n", TCM_QLA2XXX_VERSION, utsname()->sysname,
+           utsname()->machine);
+       /*
+        * Register the top level struct config_item_type with TCM core
+        */
+       fabric = target_fabric_configfs_init(THIS_MODULE, "qla2xxx");
+       if (IS_ERR(fabric)) {
+               pr_err("target_fabric_configfs_init() failed\n");
+               return PTR_ERR(fabric);
+       }
+       /*
+        * Setup fabric->tf_ops from our local tcm_qla2xxx_ops
+        */
+       fabric->tf_ops = tcm_qla2xxx_ops;
+       /*
+        * Setup default attribute lists for various fabric->tf_cit_tmpl
+        */
+       TF_CIT_TMPL(fabric)->tfc_wwn_cit.ct_attrs = tcm_qla2xxx_wwn_attrs;
+       TF_CIT_TMPL(fabric)->tfc_tpg_base_cit.ct_attrs = tcm_qla2xxx_tpg_attrs;
+       TF_CIT_TMPL(fabric)->tfc_tpg_attrib_cit.ct_attrs =
+                                               tcm_qla2xxx_tpg_attrib_attrs;
+       TF_CIT_TMPL(fabric)->tfc_tpg_param_cit.ct_attrs = NULL;
+       TF_CIT_TMPL(fabric)->tfc_tpg_np_base_cit.ct_attrs = NULL;
+       TF_CIT_TMPL(fabric)->tfc_tpg_nacl_base_cit.ct_attrs = NULL;
+       TF_CIT_TMPL(fabric)->tfc_tpg_nacl_attrib_cit.ct_attrs = NULL;
+       TF_CIT_TMPL(fabric)->tfc_tpg_nacl_auth_cit.ct_attrs = NULL;
+       TF_CIT_TMPL(fabric)->tfc_tpg_nacl_param_cit.ct_attrs = NULL;
+       /*
+        * Register the fabric for use within TCM
+        */
+       ret = target_fabric_configfs_register(fabric);
+       if (ret < 0) {
+               pr_err("target_fabric_configfs_register() failed for TCM_QLA2XXX\n");
+               return ret;
+       }
+       /*
+        * Setup our local pointer to *fabric
+        */
+       tcm_qla2xxx_fabric_configfs = fabric;
+       pr_debug("TCM_QLA2XXX[0] - Set fabric -> tcm_qla2xxx_fabric_configfs\n");
+
+       /*
+        * Register the top level struct config_item_type for NPIV with TCM core
+        */
+       npiv_fabric = target_fabric_configfs_init(THIS_MODULE, "qla2xxx_npiv");
+       if (IS_ERR(npiv_fabric)) {
+               pr_err("target_fabric_configfs_init() failed\n");
+               ret = PTR_ERR(npiv_fabric);
+               goto out_fabric;
+       }
+       /*
+        * Setup fabric->tf_ops from our local tcm_qla2xxx_npiv_ops
+        */
+       npiv_fabric->tf_ops = tcm_qla2xxx_npiv_ops;
+       /*
+        * Setup default attribute lists for various npiv_fabric->tf_cit_tmpl
+        */
+       TF_CIT_TMPL(npiv_fabric)->tfc_wwn_cit.ct_attrs = tcm_qla2xxx_wwn_attrs;
+       TF_CIT_TMPL(npiv_fabric)->tfc_tpg_base_cit.ct_attrs = NULL;
+       TF_CIT_TMPL(npiv_fabric)->tfc_tpg_attrib_cit.ct_attrs = NULL;
+       TF_CIT_TMPL(npiv_fabric)->tfc_tpg_param_cit.ct_attrs = NULL;
+       TF_CIT_TMPL(npiv_fabric)->tfc_tpg_np_base_cit.ct_attrs = NULL;
+       TF_CIT_TMPL(npiv_fabric)->tfc_tpg_nacl_base_cit.ct_attrs = NULL;
+       TF_CIT_TMPL(npiv_fabric)->tfc_tpg_nacl_attrib_cit.ct_attrs = NULL;
+       TF_CIT_TMPL(npiv_fabric)->tfc_tpg_nacl_auth_cit.ct_attrs = NULL;
+       TF_CIT_TMPL(npiv_fabric)->tfc_tpg_nacl_param_cit.ct_attrs = NULL;
+       /*
+        * Register the npiv_fabric for use within TCM
+        */
+       ret = target_fabric_configfs_register(npiv_fabric);
+       if (ret < 0) {
+               pr_err("target_fabric_configfs_register() failed for TCM_QLA2XXX\n");
+               goto out_fabric;
+       }
+       /*
+        * Setup our local pointer to *npiv_fabric
+        */
+       tcm_qla2xxx_npiv_fabric_configfs = npiv_fabric;
+       pr_debug("TCM_QLA2XXX[0] - Set fabric -> tcm_qla2xxx_npiv_fabric_configfs\n");
+
+       tcm_qla2xxx_free_wq = alloc_workqueue("tcm_qla2xxx_free",
+                                               WQ_MEM_RECLAIM, 0);
+       if (!tcm_qla2xxx_free_wq) {
+               ret = -ENOMEM;
+               goto out_fabric_npiv;
+       }
+
+       tcm_qla2xxx_cmd_wq = alloc_workqueue("tcm_qla2xxx_cmd", 0, 0);
+       if (!tcm_qla2xxx_cmd_wq) {
+               ret = -ENOMEM;
+               goto out_free_wq;
+       }
+
+       return 0;
+
+out_free_wq:
+       destroy_workqueue(tcm_qla2xxx_free_wq);
+out_fabric_npiv:
+       target_fabric_configfs_deregister(tcm_qla2xxx_npiv_fabric_configfs);
+out_fabric:
+       target_fabric_configfs_deregister(tcm_qla2xxx_fabric_configfs);
+       return ret;
+}
+
+static void tcm_qla2xxx_deregister_configfs(void)
+{
+       destroy_workqueue(tcm_qla2xxx_cmd_wq);
+       destroy_workqueue(tcm_qla2xxx_free_wq);
+
+       target_fabric_configfs_deregister(tcm_qla2xxx_fabric_configfs);
+       tcm_qla2xxx_fabric_configfs = NULL;
+       pr_debug("TCM_QLA2XXX[0] - Cleared tcm_qla2xxx_fabric_configfs\n");
+
+       target_fabric_configfs_deregister(tcm_qla2xxx_npiv_fabric_configfs);
+       tcm_qla2xxx_npiv_fabric_configfs = NULL;
+       pr_debug("TCM_QLA2XXX[0] - Cleared tcm_qla2xxx_npiv_fabric_configfs\n");
+}
+
+static int __init tcm_qla2xxx_init(void)
+{
+       int ret;
+
+       ret = tcm_qla2xxx_register_configfs();
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+static void __exit tcm_qla2xxx_exit(void)
+{
+       tcm_qla2xxx_deregister_configfs();
+}
+
+MODULE_DESCRIPTION("TCM QLA2XXX series NPIV enabled fabric driver");
+MODULE_LICENSE("GPL");
+module_init(tcm_qla2xxx_init);
+module_exit(tcm_qla2xxx_exit);
diff --git a/drivers/scsi/qla2xxx/tcm_qla2xxx.h b/drivers/scsi/qla2xxx/tcm_qla2xxx.h
new file mode 100644 (file)
index 0000000..8254981
--- /dev/null
@@ -0,0 +1,82 @@
+#include <target/target_core_base.h>
+#include <linux/btree.h>
+
+#define TCM_QLA2XXX_VERSION    "v0.1"
+/* length of ASCII WWPNs including pad */
+#define TCM_QLA2XXX_NAMELEN    32
+/* lenth of ASCII NPIV 'WWPN+WWNN' including pad */
+#define TCM_QLA2XXX_NPIV_NAMELEN 66
+
+#include "qla_target.h"
+
+struct tcm_qla2xxx_nacl {
+       /* From libfc struct fc_rport->port_id */
+       u32 nport_id;
+       /* Binary World Wide unique Node Name for remote FC Initiator Nport */
+       u64 nport_wwnn;
+       /* ASCII formatted WWPN for FC Initiator Nport */
+       char nport_name[TCM_QLA2XXX_NAMELEN];
+       /* Pointer to qla_tgt_sess */
+       struct qla_tgt_sess *qla_tgt_sess;
+       /* Pointer to TCM FC nexus */
+       struct se_session *nport_nexus;
+       /* Returned by tcm_qla2xxx_make_nodeacl() */
+       struct se_node_acl se_node_acl;
+};
+
+struct tcm_qla2xxx_tpg_attrib {
+       int generate_node_acls;
+       int cache_dynamic_acls;
+       int demo_mode_write_protect;
+       int prod_mode_write_protect;
+};
+
+struct tcm_qla2xxx_tpg {
+       /* FC lport target portal group tag for TCM */
+       u16 lport_tpgt;
+       /* Atomic bit to determine TPG active status */
+       atomic_t lport_tpg_enabled;
+       /* Pointer back to tcm_qla2xxx_lport */
+       struct tcm_qla2xxx_lport *lport;
+       /* Used by tcm_qla2xxx_tpg_attrib_cit */
+       struct tcm_qla2xxx_tpg_attrib tpg_attrib;
+       /* Returned by tcm_qla2xxx_make_tpg() */
+       struct se_portal_group se_tpg;
+};
+
+#define QLA_TPG_ATTRIB(tpg)    (&(tpg)->tpg_attrib)
+
+struct tcm_qla2xxx_fc_loopid {
+       struct se_node_acl *se_nacl;
+};
+
+struct tcm_qla2xxx_lport {
+       /* SCSI protocol the lport is providing */
+       u8 lport_proto_id;
+       /* Binary World Wide unique Port Name for FC Target Lport */
+       u64 lport_wwpn;
+       /* Binary World Wide unique Port Name for FC NPIV Target Lport */
+       u64 lport_npiv_wwpn;
+       /* Binary World Wide unique Node Name for FC NPIV Target Lport */
+       u64 lport_npiv_wwnn;
+       /* ASCII formatted WWPN for FC Target Lport */
+       char lport_name[TCM_QLA2XXX_NAMELEN];
+       /* ASCII formatted WWPN+WWNN for NPIV FC Target Lport */
+       char lport_npiv_name[TCM_QLA2XXX_NPIV_NAMELEN];
+       /* map for fc_port pointers in 24-bit FC Port ID space */
+       struct btree_head32 lport_fcport_map;
+       /* vmalloc-ed memory for fc_port pointers for 16-bit FC loop ID */
+       struct tcm_qla2xxx_fc_loopid *lport_loopid_map;
+       /* Pointer to struct scsi_qla_host from qla2xxx LLD */
+       struct scsi_qla_host *qla_vha;
+       /* Pointer to struct scsi_qla_host for NPIV VP from qla2xxx LLD */
+       struct scsi_qla_host *qla_npiv_vp;
+       /* Pointer to struct qla_tgt pointer */
+       struct qla_tgt lport_qla_tgt;
+       /* Pointer to struct fc_vport for NPIV vport from libfc */
+       struct fc_vport *npiv_vport;
+       /* Pointer to TPG=1 for non NPIV mode */
+       struct tcm_qla2xxx_tpg *tpg_1;
+       /* Returned by tcm_qla2xxx_make_lport() */
+       struct se_wwn lport_wwn;
+};
index 0b0a7d42137d7b3fef646ec376175d9c7a97454e..c681b2a355e137a99edcfd39e1c18b8c7dcbda11 100644 (file)
@@ -9,6 +9,140 @@
 #include "ql4_glbl.h"
 #include "ql4_dbg.h"
 
+static ssize_t
+qla4_8xxx_sysfs_read_fw_dump(struct file *filep, struct kobject *kobj,
+                            struct bin_attribute *ba, char *buf, loff_t off,
+                            size_t count)
+{
+       struct scsi_qla_host *ha = to_qla_host(dev_to_shost(container_of(kobj,
+                                              struct device, kobj)));
+
+       if (!is_qla8022(ha))
+               return -EINVAL;
+
+       if (!test_bit(AF_82XX_DUMP_READING, &ha->flags))
+               return 0;
+
+       return memory_read_from_buffer(buf, count, &off, ha->fw_dump,
+                                      ha->fw_dump_size);
+}
+
+static ssize_t
+qla4_8xxx_sysfs_write_fw_dump(struct file *filep, struct kobject *kobj,
+                             struct bin_attribute *ba, char *buf, loff_t off,
+                             size_t count)
+{
+       struct scsi_qla_host *ha = to_qla_host(dev_to_shost(container_of(kobj,
+                                              struct device, kobj)));
+       uint32_t dev_state;
+       long reading;
+       int ret = 0;
+
+       if (!is_qla8022(ha))
+               return -EINVAL;
+
+       if (off != 0)
+               return ret;
+
+       buf[1] = 0;
+       ret = kstrtol(buf, 10, &reading);
+       if (ret) {
+               ql4_printk(KERN_ERR, ha, "%s: Invalid input. Return err %d\n",
+                          __func__, ret);
+               return ret;
+       }
+
+       switch (reading) {
+       case 0:
+               /* clear dump collection flags */
+               if (test_and_clear_bit(AF_82XX_DUMP_READING, &ha->flags)) {
+                       clear_bit(AF_82XX_FW_DUMPED, &ha->flags);
+                       /* Reload minidump template */
+                       qla4xxx_alloc_fw_dump(ha);
+                       DEBUG2(ql4_printk(KERN_INFO, ha,
+                                         "Firmware template reloaded\n"));
+               }
+               break;
+       case 1:
+               /* Set flag to read dump */
+               if (test_bit(AF_82XX_FW_DUMPED, &ha->flags) &&
+                   !test_bit(AF_82XX_DUMP_READING, &ha->flags)) {
+                       set_bit(AF_82XX_DUMP_READING, &ha->flags);
+                       DEBUG2(ql4_printk(KERN_INFO, ha,
+                                         "Raw firmware dump ready for read on (%ld).\n",
+                                         ha->host_no));
+               }
+               break;
+       case 2:
+               /* Reset HBA */
+               qla4_8xxx_idc_lock(ha);
+               dev_state = qla4_8xxx_rd_32(ha, QLA82XX_CRB_DEV_STATE);
+               if (dev_state == QLA82XX_DEV_READY) {
+                       ql4_printk(KERN_INFO, ha,
+                                  "%s: Setting Need reset, reset_owner is 0x%x.\n",
+                                  __func__, ha->func_num);
+                       qla4_8xxx_wr_32(ha, QLA82XX_CRB_DEV_STATE,
+                                       QLA82XX_DEV_NEED_RESET);
+                       set_bit(AF_82XX_RST_OWNER, &ha->flags);
+               } else
+                       ql4_printk(KERN_INFO, ha,
+                                  "%s: Reset not performed as device state is 0x%x\n",
+                                  __func__, dev_state);
+
+               qla4_8xxx_idc_unlock(ha);
+               break;
+       default:
+               /* do nothing */
+               break;
+       }
+
+       return count;
+}
+
+static struct bin_attribute sysfs_fw_dump_attr = {
+       .attr = {
+               .name = "fw_dump",
+               .mode = S_IRUSR | S_IWUSR,
+       },
+       .size = 0,
+       .read = qla4_8xxx_sysfs_read_fw_dump,
+       .write = qla4_8xxx_sysfs_write_fw_dump,
+};
+
+static struct sysfs_entry {
+       char *name;
+       struct bin_attribute *attr;
+} bin_file_entries[] = {
+       { "fw_dump", &sysfs_fw_dump_attr },
+       { NULL },
+};
+
+void qla4_8xxx_alloc_sysfs_attr(struct scsi_qla_host *ha)
+{
+       struct Scsi_Host *host = ha->host;
+       struct sysfs_entry *iter;
+       int ret;
+
+       for (iter = bin_file_entries; iter->name; iter++) {
+               ret = sysfs_create_bin_file(&host->shost_gendev.kobj,
+                                           iter->attr);
+               if (ret)
+                       ql4_printk(KERN_ERR, ha,
+                                  "Unable to create sysfs %s binary attribute (%d).\n",
+                                  iter->name, ret);
+       }
+}
+
+void qla4_8xxx_free_sysfs_attr(struct scsi_qla_host *ha)
+{
+       struct Scsi_Host *host = ha->host;
+       struct sysfs_entry *iter;
+
+       for (iter = bin_file_entries; iter->name; iter++)
+               sysfs_remove_bin_file(&host->shost_gendev.kobj,
+                                     iter->attr);
+}
+
 /* Scsi_Host attributes. */
 static ssize_t
 qla4xxx_fw_version_show(struct device *dev,
index 7f2492e88be72085325bbb58debacefe9aaf288c..96a5616a8fdaa6f7bbb35140befe25a0c88e7087 100644 (file)
@@ -398,6 +398,16 @@ struct isp_operations {
        int (*get_sys_info) (struct scsi_qla_host *);
 };
 
+struct ql4_mdump_size_table {
+       uint32_t size;
+       uint32_t size_cmask_02;
+       uint32_t size_cmask_04;
+       uint32_t size_cmask_08;
+       uint32_t size_cmask_10;
+       uint32_t size_cmask_FF;
+       uint32_t version;
+};
+
 /*qla4xxx ipaddress configuration details */
 struct ipaddress_config {
        uint16_t ipv4_options;
@@ -485,6 +495,10 @@ struct scsi_qla_host {
 #define AF_EEH_BUSY                    20 /* 0x00100000 */
 #define AF_PCI_CHANNEL_IO_PERM_FAILURE 21 /* 0x00200000 */
 #define AF_BUILD_DDB_LIST              22 /* 0x00400000 */
+#define AF_82XX_FW_DUMPED              24 /* 0x01000000 */
+#define AF_82XX_RST_OWNER              25 /* 0x02000000 */
+#define AF_82XX_DUMP_READING           26 /* 0x04000000 */
+
        unsigned long dpc_flags;
 
 #define DPC_RESET_HA                   1 /* 0x00000002 */
@@ -662,6 +676,11 @@ struct scsi_qla_host {
 
        uint32_t nx_dev_init_timeout;
        uint32_t nx_reset_timeout;
+       void *fw_dump;
+       uint32_t fw_dump_size;
+       uint32_t fw_dump_capture_mask;
+       void *fw_dump_tmplt_hdr;
+       uint32_t fw_dump_tmplt_size;
 
        struct completion mbx_intr_comp;
 
@@ -936,4 +955,7 @@ static inline int ql4xxx_reset_active(struct scsi_qla_host *ha)
 #define PROCESS_ALL_AENS        0
 #define FLUSH_DDB_CHANGED_AENS  1
 
+/* Defines for udev events */
+#define QL4_UEVENT_CODE_FW_DUMP                0
+
 #endif /*_QLA4XXX_H */
index 210cd1d64475b0b3cfc0128ee262565ec7533f2c..7240948fb929bcb557398ecd774fe9fc36c7fae3 100644 (file)
@@ -385,6 +385,11 @@ struct qla_flt_region {
 #define MBOX_CMD_GET_IP_ADDR_STATE             0x0091
 #define MBOX_CMD_SEND_IPV6_ROUTER_SOL          0x0092
 #define MBOX_CMD_GET_DB_ENTRY_CURRENT_IP_ADDR  0x0093
+#define MBOX_CMD_MINIDUMP                      0x0129
+
+/* Minidump subcommand */
+#define MINIDUMP_GET_SIZE_SUBCOMMAND           0x00
+#define MINIDUMP_GET_TMPLT_SUBCOMMAND          0x01
 
 /* Mailbox 1 */
 #define FW_STATE_READY                         0x0000
@@ -1190,4 +1195,27 @@ struct ql_iscsi_stats {
        uint8_t reserved2[264]; /* 0x0308 - 0x040F */
 };
 
+#define QLA82XX_DBG_STATE_ARRAY_LEN            16
+#define QLA82XX_DBG_CAP_SIZE_ARRAY_LEN         8
+#define QLA82XX_DBG_RSVD_ARRAY_LEN             8
+
+struct qla4_8xxx_minidump_template_hdr {
+       uint32_t entry_type;
+       uint32_t first_entry_offset;
+       uint32_t size_of_template;
+       uint32_t capture_debug_level;
+       uint32_t num_of_entries;
+       uint32_t version;
+       uint32_t driver_timestamp;
+       uint32_t checksum;
+
+       uint32_t driver_capture_mask;
+       uint32_t driver_info_word2;
+       uint32_t driver_info_word3;
+       uint32_t driver_info_word4;
+
+       uint32_t saved_state_array[QLA82XX_DBG_STATE_ARRAY_LEN];
+       uint32_t capture_size_array[QLA82XX_DBG_CAP_SIZE_ARRAY_LEN];
+};
+
 #endif /*  _QLA4X_FW_H */
index 910536667cf577e1c19d7db82803944730eefbca..20b49d019043d193c50aebd9aef311cfa9571271 100644 (file)
@@ -196,10 +196,18 @@ int qla4xxx_bsg_request(struct bsg_job *bsg_job);
 int qla4xxx_process_vendor_specific(struct bsg_job *bsg_job);
 
 void qla4xxx_arm_relogin_timer(struct ddb_entry *ddb_entry);
+int qla4xxx_get_minidump_template(struct scsi_qla_host *ha,
+                                 dma_addr_t phys_addr);
+int qla4xxx_req_template_size(struct scsi_qla_host *ha);
+void qla4_8xxx_alloc_sysfs_attr(struct scsi_qla_host *ha);
+void qla4_8xxx_free_sysfs_attr(struct scsi_qla_host *ha);
+void qla4xxx_alloc_fw_dump(struct scsi_qla_host *ha);
 
 extern int ql4xextended_error_logging;
 extern int ql4xdontresethba;
 extern int ql4xenablemsix;
+extern int ql4xmdcapmask;
+extern int ql4xenablemd;
 
 extern struct device_attribute *qla4xxx_host_attrs[];
 #endif /* _QLA4x_GBL_H */
index 90ee5d8fa7315eda08a8bdc9cd8b66aff2e82eb2..bf36723b84e10cff0a01a3925f5d5bcafaa27fb9 100644 (file)
@@ -277,6 +277,94 @@ qla4xxx_wait_for_ip_config(struct scsi_qla_host *ha)
        return ipv4_wait|ipv6_wait;
 }
 
+/**
+ * qla4xxx_alloc_fw_dump - Allocate memory for minidump data.
+ * @ha: pointer to host adapter structure.
+ **/
+void qla4xxx_alloc_fw_dump(struct scsi_qla_host *ha)
+{
+       int status;
+       uint32_t capture_debug_level;
+       int hdr_entry_bit, k;
+       void *md_tmp;
+       dma_addr_t md_tmp_dma;
+       struct qla4_8xxx_minidump_template_hdr *md_hdr;
+
+       if (ha->fw_dump) {
+               ql4_printk(KERN_WARNING, ha,
+                          "Firmware dump previously allocated.\n");
+               return;
+       }
+
+       status = qla4xxx_req_template_size(ha);
+       if (status != QLA_SUCCESS) {
+               ql4_printk(KERN_INFO, ha,
+                          "scsi%ld: Failed to get template size\n",
+                          ha->host_no);
+               return;
+       }
+
+       clear_bit(AF_82XX_FW_DUMPED, &ha->flags);
+
+       /* Allocate memory for saving the template */
+       md_tmp = dma_alloc_coherent(&ha->pdev->dev, ha->fw_dump_tmplt_size,
+                                   &md_tmp_dma, GFP_KERNEL);
+
+       /* Request template */
+       status =  qla4xxx_get_minidump_template(ha, md_tmp_dma);
+       if (status != QLA_SUCCESS) {
+               ql4_printk(KERN_INFO, ha,
+                          "scsi%ld: Failed to get minidump template\n",
+                          ha->host_no);
+               goto alloc_cleanup;
+       }
+
+       md_hdr = (struct qla4_8xxx_minidump_template_hdr *)md_tmp;
+
+       capture_debug_level = md_hdr->capture_debug_level;
+
+       /* Get capture mask based on module loadtime setting. */
+       if (ql4xmdcapmask >= 0x3 && ql4xmdcapmask <= 0x7F)
+               ha->fw_dump_capture_mask = ql4xmdcapmask;
+       else
+               ha->fw_dump_capture_mask = capture_debug_level;
+
+       md_hdr->driver_capture_mask = ha->fw_dump_capture_mask;
+
+       DEBUG2(ql4_printk(KERN_INFO, ha, "Minimum num of entries = %d\n",
+                         md_hdr->num_of_entries));
+       DEBUG2(ql4_printk(KERN_INFO, ha, "Dump template size  = %d\n",
+                         ha->fw_dump_tmplt_size));
+       DEBUG2(ql4_printk(KERN_INFO, ha, "Selected Capture mask =0x%x\n",
+                         ha->fw_dump_capture_mask));
+
+       /* Calculate fw_dump_size */
+       for (hdr_entry_bit = 0x2, k = 1; (hdr_entry_bit & 0xFF);
+            hdr_entry_bit <<= 1, k++) {
+               if (hdr_entry_bit & ha->fw_dump_capture_mask)
+                       ha->fw_dump_size += md_hdr->capture_size_array[k];
+       }
+
+       /* Total firmware dump size including command header */
+       ha->fw_dump_size += ha->fw_dump_tmplt_size;
+       ha->fw_dump = vmalloc(ha->fw_dump_size);
+       if (!ha->fw_dump)
+               goto alloc_cleanup;
+
+       DEBUG2(ql4_printk(KERN_INFO, ha,
+                         "Minidump Tempalate Size = 0x%x KB\n",
+                         ha->fw_dump_tmplt_size));
+       DEBUG2(ql4_printk(KERN_INFO, ha,
+                         "Total Minidump size = 0x%x KB\n", ha->fw_dump_size));
+
+       memcpy(ha->fw_dump, md_tmp, ha->fw_dump_tmplt_size);
+       ha->fw_dump_tmplt_hdr = ha->fw_dump;
+
+alloc_cleanup:
+       dma_free_coherent(&ha->pdev->dev, ha->fw_dump_tmplt_size,
+                         md_tmp, md_tmp_dma);
+}
+
 static int qla4xxx_fw_ready(struct scsi_qla_host *ha)
 {
        uint32_t timeout_count;
@@ -445,9 +533,13 @@ static int qla4xxx_init_firmware(struct scsi_qla_host *ha)
                              "control block\n", ha->host_no, __func__));
                return status;
        }
+
        if (!qla4xxx_fw_ready(ha))
                return status;
 
+       if (is_qla8022(ha) && !test_bit(AF_INIT_DONE, &ha->flags))
+               qla4xxx_alloc_fw_dump(ha);
+
        return qla4xxx_get_firmware_status(ha);
 }
 
@@ -884,8 +976,8 @@ int qla4xxx_ddb_change(struct scsi_qla_host *ha, uint32_t fw_ddb_index,
                switch (state) {
                case DDB_DS_SESSION_ACTIVE:
                case DDB_DS_DISCOVERY:
-                       ddb_entry->unblock_sess(ddb_entry->sess);
                        qla4xxx_update_session_conn_param(ha, ddb_entry);
+                       ddb_entry->unblock_sess(ddb_entry->sess);
                        status = QLA_SUCCESS;
                        break;
                case DDB_DS_SESSION_FAILED:
@@ -897,6 +989,7 @@ int qla4xxx_ddb_change(struct scsi_qla_host *ha, uint32_t fw_ddb_index,
                }
                break;
        case DDB_DS_SESSION_ACTIVE:
+       case DDB_DS_DISCOVERY:
                switch (state) {
                case DDB_DS_SESSION_FAILED:
                        /*
index 7ac21dabbf22fce08264314ba3a212a329a1c40e..cab8f665a41faca343dba8f404e01ba96075abaf 100644 (file)
@@ -51,25 +51,6 @@ int qla4xxx_mailbox_command(struct scsi_qla_host *ha, uint8_t inCount,
                }
        }
 
-       if (is_qla8022(ha)) {
-               if (test_bit(AF_FW_RECOVERY, &ha->flags)) {
-                       DEBUG2(ql4_printk(KERN_WARNING, ha, "scsi%ld: %s: "
-                           "prematurely completing mbx cmd as firmware "
-                           "recovery detected\n", ha->host_no, __func__));
-                       return status;
-               }
-               /* Do not send any mbx cmd if h/w is in failed state*/
-               qla4_8xxx_idc_lock(ha);
-               dev_state = qla4_8xxx_rd_32(ha, QLA82XX_CRB_DEV_STATE);
-               qla4_8xxx_idc_unlock(ha);
-               if (dev_state == QLA82XX_DEV_FAILED) {
-                       ql4_printk(KERN_WARNING, ha, "scsi%ld: %s: H/W is in "
-                           "failed state, do not send any mailbox commands\n",
-                           ha->host_no, __func__);
-                       return status;
-               }
-       }
-
        if ((is_aer_supported(ha)) &&
            (test_bit(AF_PCI_CHANNEL_IO_PERM_FAILURE, &ha->flags))) {
                DEBUG2(printk(KERN_WARNING "scsi%ld: %s: Perm failure on EEH, "
@@ -96,6 +77,25 @@ int qla4xxx_mailbox_command(struct scsi_qla_host *ha, uint8_t inCount,
                msleep(10);
        }
 
+       if (is_qla8022(ha)) {
+               if (test_bit(AF_FW_RECOVERY, &ha->flags)) {
+                       DEBUG2(ql4_printk(KERN_WARNING, ha,
+                                         "scsi%ld: %s: prematurely completing mbx cmd as firmware recovery detected\n",
+                                         ha->host_no, __func__));
+                       goto mbox_exit;
+               }
+               /* Do not send any mbx cmd if h/w is in failed state*/
+               qla4_8xxx_idc_lock(ha);
+               dev_state = qla4_8xxx_rd_32(ha, QLA82XX_CRB_DEV_STATE);
+               qla4_8xxx_idc_unlock(ha);
+               if (dev_state == QLA82XX_DEV_FAILED) {
+                       ql4_printk(KERN_WARNING, ha,
+                                  "scsi%ld: %s: H/W is in failed state, do not send any mailbox commands\n",
+                                  ha->host_no, __func__);
+                       goto mbox_exit;
+               }
+       }
+
        spin_lock_irqsave(&ha->hardware_lock, flags);
 
        ha->mbox_status_count = outCount;
@@ -270,6 +270,79 @@ mbox_exit:
        return status;
 }
 
+/**
+ * qla4xxx_get_minidump_template - Get the firmware template
+ * @ha: Pointer to host adapter structure.
+ * @phys_addr: dma address for template
+ *
+ * Obtain the minidump template from firmware during initialization
+ * as it may not be available when minidump is desired.
+ **/
+int qla4xxx_get_minidump_template(struct scsi_qla_host *ha,
+                                 dma_addr_t phys_addr)
+{
+       uint32_t mbox_cmd[MBOX_REG_COUNT];
+       uint32_t mbox_sts[MBOX_REG_COUNT];
+       int status;
+
+       memset(&mbox_cmd, 0, sizeof(mbox_cmd));
+       memset(&mbox_sts, 0, sizeof(mbox_sts));
+
+       mbox_cmd[0] = MBOX_CMD_MINIDUMP;
+       mbox_cmd[1] = MINIDUMP_GET_TMPLT_SUBCOMMAND;
+       mbox_cmd[2] = LSDW(phys_addr);
+       mbox_cmd[3] = MSDW(phys_addr);
+       mbox_cmd[4] = ha->fw_dump_tmplt_size;
+       mbox_cmd[5] = 0;
+
+       status = qla4xxx_mailbox_command(ha, MBOX_REG_COUNT, 2, &mbox_cmd[0],
+                                        &mbox_sts[0]);
+       if (status != QLA_SUCCESS) {
+               DEBUG2(ql4_printk(KERN_INFO, ha,
+                                 "scsi%ld: %s: Cmd = %08X, mbx[0] = 0x%04x, mbx[1] = 0x%04x\n",
+                                 ha->host_no, __func__, mbox_cmd[0],
+                                 mbox_sts[0], mbox_sts[1]));
+       }
+       return status;
+}
+
+/**
+ * qla4xxx_req_template_size - Get minidump template size from firmware.
+ * @ha: Pointer to host adapter structure.
+ **/
+int qla4xxx_req_template_size(struct scsi_qla_host *ha)
+{
+       uint32_t mbox_cmd[MBOX_REG_COUNT];
+       uint32_t mbox_sts[MBOX_REG_COUNT];
+       int status;
+
+       memset(&mbox_cmd, 0, sizeof(mbox_cmd));
+       memset(&mbox_sts, 0, sizeof(mbox_sts));
+
+       mbox_cmd[0] = MBOX_CMD_MINIDUMP;
+       mbox_cmd[1] = MINIDUMP_GET_SIZE_SUBCOMMAND;
+
+       status = qla4xxx_mailbox_command(ha, MBOX_REG_COUNT, 8, &mbox_cmd[0],
+                                        &mbox_sts[0]);
+       if (status == QLA_SUCCESS) {
+               ha->fw_dump_tmplt_size = mbox_sts[1];
+               DEBUG2(ql4_printk(KERN_INFO, ha,
+                                 "%s: sts[0]=0x%04x, template  size=0x%04x, size_cm_02=0x%04x, size_cm_04=0x%04x, size_cm_08=0x%04x, size_cm_10=0x%04x, size_cm_FF=0x%04x, version=0x%04x\n",
+                                 __func__, mbox_sts[0], mbox_sts[1],
+                                 mbox_sts[2], mbox_sts[3], mbox_sts[4],
+                                 mbox_sts[5], mbox_sts[6], mbox_sts[7]));
+               if (ha->fw_dump_tmplt_size == 0)
+                       status = QLA_ERROR;
+       } else {
+               ql4_printk(KERN_WARNING, ha,
+                          "%s: Error sts[0]=0x%04x, mbx[1]=0x%04x\n",
+                          __func__, mbox_sts[0], mbox_sts[1]);
+               status = QLA_ERROR;
+       }
+
+       return status;
+}
+
 void qla4xxx_mailbox_premature_completion(struct scsi_qla_host *ha)
 {
        set_bit(AF_FW_RECOVERY, &ha->flags);
index e1e46b6dac754e8bb10d9f6523b75e9ef38c2ea3..228b67020d2cde7549e9d962f0b1e95b0675155c 100644 (file)
@@ -7,6 +7,7 @@
 #include <linux/delay.h>
 #include <linux/io.h>
 #include <linux/pci.h>
+#include <linux/ratelimit.h>
 #include "ql4_def.h"
 #include "ql4_glbl.h"
 
@@ -420,6 +421,38 @@ qla4_8xxx_rd_32(struct scsi_qla_host *ha, ulong off)
        return data;
 }
 
+/* Minidump related functions */
+static int qla4_8xxx_md_rw_32(struct scsi_qla_host *ha, uint32_t off,
+                             u32 data, uint8_t flag)
+{
+       uint32_t win_read, off_value, rval = QLA_SUCCESS;
+
+       off_value  = off & 0xFFFF0000;
+       writel(off_value, (void __iomem *)(CRB_WINDOW_2M + ha->nx_pcibase));
+
+       /* Read back value to make sure write has gone through before trying
+        * to use it.
+        */
+       win_read = readl((void __iomem *)(CRB_WINDOW_2M + ha->nx_pcibase));
+       if (win_read != off_value) {
+               DEBUG2(ql4_printk(KERN_INFO, ha,
+                                 "%s: Written (0x%x) != Read (0x%x), off=0x%x\n",
+                                  __func__, off_value, win_read, off));
+               return QLA_ERROR;
+       }
+
+       off_value  = off & 0x0000FFFF;
+
+       if (flag)
+               writel(data, (void __iomem *)(off_value + CRB_INDIRECT_2M +
+                                             ha->nx_pcibase));
+       else
+               rval = readl((void __iomem *)(off_value + CRB_INDIRECT_2M +
+                                             ha->nx_pcibase));
+
+       return rval;
+}
+
 #define CRB_WIN_LOCK_TIMEOUT 100000000
 
 int qla4_8xxx_crb_win_lock(struct scsi_qla_host *ha)
@@ -1252,9 +1285,9 @@ qla4_8xxx_pci_mem_read_2M(struct scsi_qla_host *ha,
                }
 
                if (j >= MAX_CTL_CHECK) {
-                       if (printk_ratelimit())
-                               ql4_printk(KERN_ERR, ha,
-                                   "failed to read through agent\n");
+                       printk_ratelimited(KERN_ERR
+                                          "%s: failed to read through agent\n",
+                                          __func__);
                        break;
                }
 
@@ -1390,7 +1423,8 @@ qla4_8xxx_pci_mem_write_2M(struct scsi_qla_host *ha,
                if (j >= MAX_CTL_CHECK) {
                        if (printk_ratelimit())
                                ql4_printk(KERN_ERR, ha,
-                                   "failed to write through agent\n");
+                                          "%s: failed to read through agent\n",
+                                          __func__);
                        ret = -1;
                        break;
                }
@@ -1462,6 +1496,8 @@ qla4_8xxx_set_drv_active(struct scsi_qla_host *ha)
 
        drv_active = qla4_8xxx_rd_32(ha, QLA82XX_CRB_DRV_ACTIVE);
        drv_active |= (1 << (ha->func_num * 4));
+       ql4_printk(KERN_INFO, ha, "%s(%ld): drv_active: 0x%08x\n",
+                  __func__, ha->host_no, drv_active);
        qla4_8xxx_wr_32(ha, QLA82XX_CRB_DRV_ACTIVE, drv_active);
 }
 
@@ -1472,6 +1508,8 @@ qla4_8xxx_clear_drv_active(struct scsi_qla_host *ha)
 
        drv_active = qla4_8xxx_rd_32(ha, QLA82XX_CRB_DRV_ACTIVE);
        drv_active &= ~(1 << (ha->func_num * 4));
+       ql4_printk(KERN_INFO, ha, "%s(%ld): drv_active: 0x%08x\n",
+                  __func__, ha->host_no, drv_active);
        qla4_8xxx_wr_32(ha, QLA82XX_CRB_DRV_ACTIVE, drv_active);
 }
 
@@ -1497,6 +1535,8 @@ qla4_8xxx_set_rst_ready(struct scsi_qla_host *ha)
 
        drv_state = qla4_8xxx_rd_32(ha, QLA82XX_CRB_DRV_STATE);
        drv_state |= (1 << (ha->func_num * 4));
+       ql4_printk(KERN_INFO, ha, "%s(%ld): drv_state: 0x%08x\n",
+                  __func__, ha->host_no, drv_state);
        qla4_8xxx_wr_32(ha, QLA82XX_CRB_DRV_STATE, drv_state);
 }
 
@@ -1507,6 +1547,8 @@ qla4_8xxx_clear_rst_ready(struct scsi_qla_host *ha)
 
        drv_state = qla4_8xxx_rd_32(ha, QLA82XX_CRB_DRV_STATE);
        drv_state &= ~(1 << (ha->func_num * 4));
+       ql4_printk(KERN_INFO, ha, "%s(%ld): drv_state: 0x%08x\n",
+                  __func__, ha->host_no, drv_state);
        qla4_8xxx_wr_32(ha, QLA82XX_CRB_DRV_STATE, drv_state);
 }
 
@@ -1601,6 +1643,629 @@ static void qla4_8xxx_rom_lock_recovery(struct scsi_qla_host *ha)
        qla4_8xxx_rom_unlock(ha);
 }
 
+static void qla4_8xxx_minidump_process_rdcrb(struct scsi_qla_host *ha,
+                               struct qla82xx_minidump_entry_hdr *entry_hdr,
+                               uint32_t **d_ptr)
+{
+       uint32_t r_addr, r_stride, loop_cnt, i, r_value;
+       struct qla82xx_minidump_entry_crb *crb_hdr;
+       uint32_t *data_ptr = *d_ptr;
+
+       DEBUG2(ql4_printk(KERN_INFO, ha, "Entering fn: %s\n", __func__));
+       crb_hdr = (struct qla82xx_minidump_entry_crb *)entry_hdr;
+       r_addr = crb_hdr->addr;
+       r_stride = crb_hdr->crb_strd.addr_stride;
+       loop_cnt = crb_hdr->op_count;
+
+       for (i = 0; i < loop_cnt; i++) {
+               r_value = qla4_8xxx_md_rw_32(ha, r_addr, 0, 0);
+               *data_ptr++ = cpu_to_le32(r_addr);
+               *data_ptr++ = cpu_to_le32(r_value);
+               r_addr += r_stride;
+       }
+       *d_ptr = data_ptr;
+}
+
+static int qla4_8xxx_minidump_process_l2tag(struct scsi_qla_host *ha,
+                                struct qla82xx_minidump_entry_hdr *entry_hdr,
+                                uint32_t **d_ptr)
+{
+       uint32_t addr, r_addr, c_addr, t_r_addr;
+       uint32_t i, k, loop_count, t_value, r_cnt, r_value;
+       unsigned long p_wait, w_time, p_mask;
+       uint32_t c_value_w, c_value_r;
+       struct qla82xx_minidump_entry_cache *cache_hdr;
+       int rval = QLA_ERROR;
+       uint32_t *data_ptr = *d_ptr;
+
+       DEBUG2(ql4_printk(KERN_INFO, ha, "Entering fn: %s\n", __func__));
+       cache_hdr = (struct qla82xx_minidump_entry_cache *)entry_hdr;
+
+       loop_count = cache_hdr->op_count;
+       r_addr = cache_hdr->read_addr;
+       c_addr = cache_hdr->control_addr;
+       c_value_w = cache_hdr->cache_ctrl.write_value;
+
+       t_r_addr = cache_hdr->tag_reg_addr;
+       t_value = cache_hdr->addr_ctrl.init_tag_value;
+       r_cnt = cache_hdr->read_ctrl.read_addr_cnt;
+       p_wait = cache_hdr->cache_ctrl.poll_wait;
+       p_mask = cache_hdr->cache_ctrl.poll_mask;
+
+       for (i = 0; i < loop_count; i++) {
+               qla4_8xxx_md_rw_32(ha, t_r_addr, t_value, 1);
+
+               if (c_value_w)
+                       qla4_8xxx_md_rw_32(ha, c_addr, c_value_w, 1);
+
+               if (p_mask) {
+                       w_time = jiffies + p_wait;
+                       do {
+                               c_value_r = qla4_8xxx_md_rw_32(ha, c_addr,
+                                                               0, 0);
+                               if ((c_value_r & p_mask) == 0) {
+                                       break;
+                               } else if (time_after_eq(jiffies, w_time)) {
+                                       /* capturing dump failed */
+                                       return rval;
+                               }
+                       } while (1);
+               }
+
+               addr = r_addr;
+               for (k = 0; k < r_cnt; k++) {
+                       r_value = qla4_8xxx_md_rw_32(ha, addr, 0, 0);
+                       *data_ptr++ = cpu_to_le32(r_value);
+                       addr += cache_hdr->read_ctrl.read_addr_stride;
+               }
+
+               t_value += cache_hdr->addr_ctrl.tag_value_stride;
+       }
+       *d_ptr = data_ptr;
+       return QLA_SUCCESS;
+}
+
+static int qla4_8xxx_minidump_process_control(struct scsi_qla_host *ha,
+                               struct qla82xx_minidump_entry_hdr *entry_hdr)
+{
+       struct qla82xx_minidump_entry_crb *crb_entry;
+       uint32_t read_value, opcode, poll_time, addr, index, rval = QLA_SUCCESS;
+       uint32_t crb_addr;
+       unsigned long wtime;
+       struct qla4_8xxx_minidump_template_hdr *tmplt_hdr;
+       int i;
+
+       DEBUG2(ql4_printk(KERN_INFO, ha, "Entering fn: %s\n", __func__));
+       tmplt_hdr = (struct qla4_8xxx_minidump_template_hdr *)
+                                               ha->fw_dump_tmplt_hdr;
+       crb_entry = (struct qla82xx_minidump_entry_crb *)entry_hdr;
+
+       crb_addr = crb_entry->addr;
+       for (i = 0; i < crb_entry->op_count; i++) {
+               opcode = crb_entry->crb_ctrl.opcode;
+               if (opcode & QLA82XX_DBG_OPCODE_WR) {
+                       qla4_8xxx_md_rw_32(ha, crb_addr,
+                                          crb_entry->value_1, 1);
+                       opcode &= ~QLA82XX_DBG_OPCODE_WR;
+               }
+               if (opcode & QLA82XX_DBG_OPCODE_RW) {
+                       read_value = qla4_8xxx_md_rw_32(ha, crb_addr, 0, 0);
+                       qla4_8xxx_md_rw_32(ha, crb_addr, read_value, 1);
+                       opcode &= ~QLA82XX_DBG_OPCODE_RW;
+               }
+               if (opcode & QLA82XX_DBG_OPCODE_AND) {
+                       read_value = qla4_8xxx_md_rw_32(ha, crb_addr, 0, 0);
+                       read_value &= crb_entry->value_2;
+                       opcode &= ~QLA82XX_DBG_OPCODE_AND;
+                       if (opcode & QLA82XX_DBG_OPCODE_OR) {
+                               read_value |= crb_entry->value_3;
+                               opcode &= ~QLA82XX_DBG_OPCODE_OR;
+                       }
+                       qla4_8xxx_md_rw_32(ha, crb_addr, read_value, 1);
+               }
+               if (opcode & QLA82XX_DBG_OPCODE_OR) {
+                       read_value = qla4_8xxx_md_rw_32(ha, crb_addr, 0, 0);
+                       read_value |= crb_entry->value_3;
+                       qla4_8xxx_md_rw_32(ha, crb_addr, read_value, 1);
+                       opcode &= ~QLA82XX_DBG_OPCODE_OR;
+               }
+               if (opcode & QLA82XX_DBG_OPCODE_POLL) {
+                       poll_time = crb_entry->crb_strd.poll_timeout;
+                       wtime = jiffies + poll_time;
+                       read_value = qla4_8xxx_md_rw_32(ha, crb_addr, 0, 0);
+
+                       do {
+                               if ((read_value & crb_entry->value_2) ==
+                                   crb_entry->value_1)
+                                       break;
+                               else if (time_after_eq(jiffies, wtime)) {
+                                       /* capturing dump failed */
+                                       rval = QLA_ERROR;
+                                       break;
+                               } else
+                                       read_value = qla4_8xxx_md_rw_32(ha,
+                                                               crb_addr, 0, 0);
+                       } while (1);
+                       opcode &= ~QLA82XX_DBG_OPCODE_POLL;
+               }
+
+               if (opcode & QLA82XX_DBG_OPCODE_RDSTATE) {
+                       if (crb_entry->crb_strd.state_index_a) {
+                               index = crb_entry->crb_strd.state_index_a;
+                               addr = tmplt_hdr->saved_state_array[index];
+                       } else {
+                               addr = crb_addr;
+                       }
+
+                       read_value = qla4_8xxx_md_rw_32(ha, addr, 0, 0);
+                       index = crb_entry->crb_ctrl.state_index_v;
+                       tmplt_hdr->saved_state_array[index] = read_value;
+                       opcode &= ~QLA82XX_DBG_OPCODE_RDSTATE;
+               }
+
+               if (opcode & QLA82XX_DBG_OPCODE_WRSTATE) {
+                       if (crb_entry->crb_strd.state_index_a) {
+                               index = crb_entry->crb_strd.state_index_a;
+                               addr = tmplt_hdr->saved_state_array[index];
+                       } else {
+                               addr = crb_addr;
+                       }
+
+                       if (crb_entry->crb_ctrl.state_index_v) {
+                               index = crb_entry->crb_ctrl.state_index_v;
+                               read_value =
+                                       tmplt_hdr->saved_state_array[index];
+                       } else {
+                               read_value = crb_entry->value_1;
+                       }
+
+                       qla4_8xxx_md_rw_32(ha, addr, read_value, 1);
+                       opcode &= ~QLA82XX_DBG_OPCODE_WRSTATE;
+               }
+
+               if (opcode & QLA82XX_DBG_OPCODE_MDSTATE) {
+                       index = crb_entry->crb_ctrl.state_index_v;
+                       read_value = tmplt_hdr->saved_state_array[index];
+                       read_value <<= crb_entry->crb_ctrl.shl;
+                       read_value >>= crb_entry->crb_ctrl.shr;
+                       if (crb_entry->value_2)
+                               read_value &= crb_entry->value_2;
+                       read_value |= crb_entry->value_3;
+                       read_value += crb_entry->value_1;
+                       tmplt_hdr->saved_state_array[index] = read_value;
+                       opcode &= ~QLA82XX_DBG_OPCODE_MDSTATE;
+               }
+               crb_addr += crb_entry->crb_strd.addr_stride;
+       }
+       DEBUG2(ql4_printk(KERN_INFO, ha, "Leaving fn: %s\n", __func__));
+       return rval;
+}
+
+static void qla4_8xxx_minidump_process_rdocm(struct scsi_qla_host *ha,
+                               struct qla82xx_minidump_entry_hdr *entry_hdr,
+                               uint32_t **d_ptr)
+{
+       uint32_t r_addr, r_stride, loop_cnt, i, r_value;
+       struct qla82xx_minidump_entry_rdocm *ocm_hdr;
+       uint32_t *data_ptr = *d_ptr;
+
+       DEBUG2(ql4_printk(KERN_INFO, ha, "Entering fn: %s\n", __func__));
+       ocm_hdr = (struct qla82xx_minidump_entry_rdocm *)entry_hdr;
+       r_addr = ocm_hdr->read_addr;
+       r_stride = ocm_hdr->read_addr_stride;
+       loop_cnt = ocm_hdr->op_count;
+
+       DEBUG2(ql4_printk(KERN_INFO, ha,
+                         "[%s]: r_addr: 0x%x, r_stride: 0x%x, loop_cnt: 0x%x\n",
+                         __func__, r_addr, r_stride, loop_cnt));
+
+       for (i = 0; i < loop_cnt; i++) {
+               r_value = readl((void __iomem *)(r_addr + ha->nx_pcibase));
+               *data_ptr++ = cpu_to_le32(r_value);
+               r_addr += r_stride;
+       }
+       DEBUG2(ql4_printk(KERN_INFO, ha, "Leaving fn: %s datacount: 0x%lx\n",
+                         __func__, (loop_cnt * sizeof(uint32_t))));
+       *d_ptr = data_ptr;
+}
+
+static void qla4_8xxx_minidump_process_rdmux(struct scsi_qla_host *ha,
+                               struct qla82xx_minidump_entry_hdr *entry_hdr,
+                               uint32_t **d_ptr)
+{
+       uint32_t r_addr, s_stride, s_addr, s_value, loop_cnt, i, r_value;
+       struct qla82xx_minidump_entry_mux *mux_hdr;
+       uint32_t *data_ptr = *d_ptr;
+
+       DEBUG2(ql4_printk(KERN_INFO, ha, "Entering fn: %s\n", __func__));
+       mux_hdr = (struct qla82xx_minidump_entry_mux *)entry_hdr;
+       r_addr = mux_hdr->read_addr;
+       s_addr = mux_hdr->select_addr;
+       s_stride = mux_hdr->select_value_stride;
+       s_value = mux_hdr->select_value;
+       loop_cnt = mux_hdr->op_count;
+
+       for (i = 0; i < loop_cnt; i++) {
+               qla4_8xxx_md_rw_32(ha, s_addr, s_value, 1);
+               r_value = qla4_8xxx_md_rw_32(ha, r_addr, 0, 0);
+               *data_ptr++ = cpu_to_le32(s_value);
+               *data_ptr++ = cpu_to_le32(r_value);
+               s_value += s_stride;
+       }
+       *d_ptr = data_ptr;
+}
+
+static void qla4_8xxx_minidump_process_l1cache(struct scsi_qla_host *ha,
+                               struct qla82xx_minidump_entry_hdr *entry_hdr,
+                               uint32_t **d_ptr)
+{
+       uint32_t addr, r_addr, c_addr, t_r_addr;
+       uint32_t i, k, loop_count, t_value, r_cnt, r_value;
+       uint32_t c_value_w;
+       struct qla82xx_minidump_entry_cache *cache_hdr;
+       uint32_t *data_ptr = *d_ptr;
+
+       cache_hdr = (struct qla82xx_minidump_entry_cache *)entry_hdr;
+       loop_count = cache_hdr->op_count;
+       r_addr = cache_hdr->read_addr;
+       c_addr = cache_hdr->control_addr;
+       c_value_w = cache_hdr->cache_ctrl.write_value;
+
+       t_r_addr = cache_hdr->tag_reg_addr;
+       t_value = cache_hdr->addr_ctrl.init_tag_value;
+       r_cnt = cache_hdr->read_ctrl.read_addr_cnt;
+
+       for (i = 0; i < loop_count; i++) {
+               qla4_8xxx_md_rw_32(ha, t_r_addr, t_value, 1);
+               qla4_8xxx_md_rw_32(ha, c_addr, c_value_w, 1);
+               addr = r_addr;
+               for (k = 0; k < r_cnt; k++) {
+                       r_value = qla4_8xxx_md_rw_32(ha, addr, 0, 0);
+                       *data_ptr++ = cpu_to_le32(r_value);
+                       addr += cache_hdr->read_ctrl.read_addr_stride;
+               }
+               t_value += cache_hdr->addr_ctrl.tag_value_stride;
+       }
+       *d_ptr = data_ptr;
+}
+
+static void qla4_8xxx_minidump_process_queue(struct scsi_qla_host *ha,
+                               struct qla82xx_minidump_entry_hdr *entry_hdr,
+                               uint32_t **d_ptr)
+{
+       uint32_t s_addr, r_addr;
+       uint32_t r_stride, r_value, r_cnt, qid = 0;
+       uint32_t i, k, loop_cnt;
+       struct qla82xx_minidump_entry_queue *q_hdr;
+       uint32_t *data_ptr = *d_ptr;
+
+       DEBUG2(ql4_printk(KERN_INFO, ha, "Entering fn: %s\n", __func__));
+       q_hdr = (struct qla82xx_minidump_entry_queue *)entry_hdr;
+       s_addr = q_hdr->select_addr;
+       r_cnt = q_hdr->rd_strd.read_addr_cnt;
+       r_stride = q_hdr->rd_strd.read_addr_stride;
+       loop_cnt = q_hdr->op_count;
+
+       for (i = 0; i < loop_cnt; i++) {
+               qla4_8xxx_md_rw_32(ha, s_addr, qid, 1);
+               r_addr = q_hdr->read_addr;
+               for (k = 0; k < r_cnt; k++) {
+                       r_value = qla4_8xxx_md_rw_32(ha, r_addr, 0, 0);
+                       *data_ptr++ = cpu_to_le32(r_value);
+                       r_addr += r_stride;
+               }
+               qid += q_hdr->q_strd.queue_id_stride;
+       }
+       *d_ptr = data_ptr;
+}
+
+#define MD_DIRECT_ROM_WINDOW           0x42110030
+#define MD_DIRECT_ROM_READ_BASE                0x42150000
+
+static void qla4_8xxx_minidump_process_rdrom(struct scsi_qla_host *ha,
+                               struct qla82xx_minidump_entry_hdr *entry_hdr,
+                               uint32_t **d_ptr)
+{
+       uint32_t r_addr, r_value;
+       uint32_t i, loop_cnt;
+       struct qla82xx_minidump_entry_rdrom *rom_hdr;
+       uint32_t *data_ptr = *d_ptr;
+
+       DEBUG2(ql4_printk(KERN_INFO, ha, "Entering fn: %s\n", __func__));
+       rom_hdr = (struct qla82xx_minidump_entry_rdrom *)entry_hdr;
+       r_addr = rom_hdr->read_addr;
+       loop_cnt = rom_hdr->read_data_size/sizeof(uint32_t);
+
+       DEBUG2(ql4_printk(KERN_INFO, ha,
+                         "[%s]: flash_addr: 0x%x, read_data_size: 0x%x\n",
+                          __func__, r_addr, loop_cnt));
+
+       for (i = 0; i < loop_cnt; i++) {
+               qla4_8xxx_md_rw_32(ha, MD_DIRECT_ROM_WINDOW,
+                                  (r_addr & 0xFFFF0000), 1);
+               r_value = qla4_8xxx_md_rw_32(ha,
+                                            MD_DIRECT_ROM_READ_BASE +
+                                            (r_addr & 0x0000FFFF), 0, 0);
+               *data_ptr++ = cpu_to_le32(r_value);
+               r_addr += sizeof(uint32_t);
+       }
+       *d_ptr = data_ptr;
+}
+
+#define MD_MIU_TEST_AGT_CTRL           0x41000090
+#define MD_MIU_TEST_AGT_ADDR_LO                0x41000094
+#define MD_MIU_TEST_AGT_ADDR_HI                0x41000098
+
+static int qla4_8xxx_minidump_process_rdmem(struct scsi_qla_host *ha,
+                               struct qla82xx_minidump_entry_hdr *entry_hdr,
+                               uint32_t **d_ptr)
+{
+       uint32_t r_addr, r_value, r_data;
+       uint32_t i, j, loop_cnt;
+       struct qla82xx_minidump_entry_rdmem *m_hdr;
+       unsigned long flags;
+       uint32_t *data_ptr = *d_ptr;
+
+       DEBUG2(ql4_printk(KERN_INFO, ha, "Entering fn: %s\n", __func__));
+       m_hdr = (struct qla82xx_minidump_entry_rdmem *)entry_hdr;
+       r_addr = m_hdr->read_addr;
+       loop_cnt = m_hdr->read_data_size/16;
+
+       DEBUG2(ql4_printk(KERN_INFO, ha,
+                         "[%s]: Read addr: 0x%x, read_data_size: 0x%x\n",
+                         __func__, r_addr, m_hdr->read_data_size));
+
+       if (r_addr & 0xf) {
+               DEBUG2(ql4_printk(KERN_INFO, ha,
+                                 "[%s]: Read addr 0x%x not 16 bytes alligned\n",
+                                 __func__, r_addr));
+               return QLA_ERROR;
+       }
+
+       if (m_hdr->read_data_size % 16) {
+               DEBUG2(ql4_printk(KERN_INFO, ha,
+                                 "[%s]: Read data[0x%x] not multiple of 16 bytes\n",
+                                 __func__, m_hdr->read_data_size));
+               return QLA_ERROR;
+       }
+
+       DEBUG2(ql4_printk(KERN_INFO, ha,
+                         "[%s]: rdmem_addr: 0x%x, read_data_size: 0x%x, loop_cnt: 0x%x\n",
+                         __func__, r_addr, m_hdr->read_data_size, loop_cnt));
+
+       write_lock_irqsave(&ha->hw_lock, flags);
+       for (i = 0; i < loop_cnt; i++) {
+               qla4_8xxx_md_rw_32(ha, MD_MIU_TEST_AGT_ADDR_LO, r_addr, 1);
+               r_value = 0;
+               qla4_8xxx_md_rw_32(ha, MD_MIU_TEST_AGT_ADDR_HI, r_value, 1);
+               r_value = MIU_TA_CTL_ENABLE;
+               qla4_8xxx_md_rw_32(ha, MD_MIU_TEST_AGT_CTRL, r_value, 1);
+               r_value = MIU_TA_CTL_START | MIU_TA_CTL_ENABLE;
+               qla4_8xxx_md_rw_32(ha, MD_MIU_TEST_AGT_CTRL, r_value, 1);
+
+               for (j = 0; j < MAX_CTL_CHECK; j++) {
+                       r_value = qla4_8xxx_md_rw_32(ha, MD_MIU_TEST_AGT_CTRL,
+                                                    0, 0);
+                       if ((r_value & MIU_TA_CTL_BUSY) == 0)
+                               break;
+               }
+
+               if (j >= MAX_CTL_CHECK) {
+                       printk_ratelimited(KERN_ERR
+                                          "%s: failed to read through agent\n",
+                                           __func__);
+                       write_unlock_irqrestore(&ha->hw_lock, flags);
+                       return QLA_SUCCESS;
+               }
+
+               for (j = 0; j < 4; j++) {
+                       r_data = qla4_8xxx_md_rw_32(ha,
+                                                   MD_MIU_TEST_AGT_RDDATA[j],
+                                                   0, 0);
+                       *data_ptr++ = cpu_to_le32(r_data);
+               }
+
+               r_addr += 16;
+       }
+       write_unlock_irqrestore(&ha->hw_lock, flags);
+
+       DEBUG2(ql4_printk(KERN_INFO, ha, "Leaving fn: %s datacount: 0x%x\n",
+                         __func__, (loop_cnt * 16)));
+
+       *d_ptr = data_ptr;
+       return QLA_SUCCESS;
+}
+
+static void ql4_8xxx_mark_entry_skipped(struct scsi_qla_host *ha,
+                               struct qla82xx_minidump_entry_hdr *entry_hdr,
+                               int index)
+{
+       entry_hdr->d_ctrl.driver_flags |= QLA82XX_DBG_SKIPPED_FLAG;
+       DEBUG2(ql4_printk(KERN_INFO, ha,
+                         "scsi(%ld): Skipping entry[%d]: ETYPE[0x%x]-ELEVEL[0x%x]\n",
+                         ha->host_no, index, entry_hdr->entry_type,
+                         entry_hdr->d_ctrl.entry_capture_mask));
+}
+
+/**
+ * qla82xx_collect_md_data - Retrieve firmware minidump data.
+ * @ha: pointer to adapter structure
+ **/
+static int qla4_8xxx_collect_md_data(struct scsi_qla_host *ha)
+{
+       int num_entry_hdr = 0;
+       struct qla82xx_minidump_entry_hdr *entry_hdr;
+       struct qla4_8xxx_minidump_template_hdr *tmplt_hdr;
+       uint32_t *data_ptr;
+       uint32_t data_collected = 0;
+       int i, rval = QLA_ERROR;
+       uint64_t now;
+       uint32_t timestamp;
+
+       if (!ha->fw_dump) {
+               ql4_printk(KERN_INFO, ha, "%s(%ld) No buffer to dump\n",
+                          __func__, ha->host_no);
+               return rval;
+       }
+
+       tmplt_hdr = (struct qla4_8xxx_minidump_template_hdr *)
+                                               ha->fw_dump_tmplt_hdr;
+       data_ptr = (uint32_t *)((uint8_t *)ha->fw_dump +
+                                               ha->fw_dump_tmplt_size);
+       data_collected += ha->fw_dump_tmplt_size;
+
+       num_entry_hdr = tmplt_hdr->num_of_entries;
+       ql4_printk(KERN_INFO, ha, "[%s]: starting data ptr: %p\n",
+                  __func__, data_ptr);
+       ql4_printk(KERN_INFO, ha,
+                  "[%s]: no of entry headers in Template: 0x%x\n",
+                  __func__, num_entry_hdr);
+       ql4_printk(KERN_INFO, ha, "[%s]: Capture Mask obtained: 0x%x\n",
+                  __func__, ha->fw_dump_capture_mask);
+       ql4_printk(KERN_INFO, ha, "[%s]: Total_data_size 0x%x, %d obtained\n",
+                  __func__, ha->fw_dump_size, ha->fw_dump_size);
+
+       /* Update current timestamp before taking dump */
+       now = get_jiffies_64();
+       timestamp = (u32)(jiffies_to_msecs(now) / 1000);
+       tmplt_hdr->driver_timestamp = timestamp;
+
+       entry_hdr = (struct qla82xx_minidump_entry_hdr *)
+                                       (((uint8_t *)ha->fw_dump_tmplt_hdr) +
+                                        tmplt_hdr->first_entry_offset);
+
+       /* Walk through the entry headers - validate/perform required action */
+       for (i = 0; i < num_entry_hdr; i++) {
+               if (data_collected >= ha->fw_dump_size) {
+                       ql4_printk(KERN_INFO, ha,
+                                  "Data collected: [0x%x], Total Dump size: [0x%x]\n",
+                                  data_collected, ha->fw_dump_size);
+                       return rval;
+               }
+
+               if (!(entry_hdr->d_ctrl.entry_capture_mask &
+                     ha->fw_dump_capture_mask)) {
+                       entry_hdr->d_ctrl.driver_flags |=
+                                               QLA82XX_DBG_SKIPPED_FLAG;
+                       goto skip_nxt_entry;
+               }
+
+               DEBUG2(ql4_printk(KERN_INFO, ha,
+                                 "Data collected: [0x%x], Dump size left:[0x%x]\n",
+                                 data_collected,
+                                 (ha->fw_dump_size - data_collected)));
+
+               /* Decode the entry type and take required action to capture
+                * debug data
+                */
+               switch (entry_hdr->entry_type) {
+               case QLA82XX_RDEND:
+                       ql4_8xxx_mark_entry_skipped(ha, entry_hdr, i);
+                       break;
+               case QLA82XX_CNTRL:
+                       rval = qla4_8xxx_minidump_process_control(ha,
+                                                                 entry_hdr);
+                       if (rval != QLA_SUCCESS) {
+                               ql4_8xxx_mark_entry_skipped(ha, entry_hdr, i);
+                               goto md_failed;
+                       }
+                       break;
+               case QLA82XX_RDCRB:
+                       qla4_8xxx_minidump_process_rdcrb(ha, entry_hdr,
+                                                        &data_ptr);
+                       break;
+               case QLA82XX_RDMEM:
+                       rval = qla4_8xxx_minidump_process_rdmem(ha, entry_hdr,
+                                                               &data_ptr);
+                       if (rval != QLA_SUCCESS) {
+                               ql4_8xxx_mark_entry_skipped(ha, entry_hdr, i);
+                               goto md_failed;
+                       }
+                       break;
+               case QLA82XX_BOARD:
+               case QLA82XX_RDROM:
+                       qla4_8xxx_minidump_process_rdrom(ha, entry_hdr,
+                                                        &data_ptr);
+                       break;
+               case QLA82XX_L2DTG:
+               case QLA82XX_L2ITG:
+               case QLA82XX_L2DAT:
+               case QLA82XX_L2INS:
+                       rval = qla4_8xxx_minidump_process_l2tag(ha, entry_hdr,
+                                                               &data_ptr);
+                       if (rval != QLA_SUCCESS) {
+                               ql4_8xxx_mark_entry_skipped(ha, entry_hdr, i);
+                               goto md_failed;
+                       }
+                       break;
+               case QLA82XX_L1DAT:
+               case QLA82XX_L1INS:
+                       qla4_8xxx_minidump_process_l1cache(ha, entry_hdr,
+                                                          &data_ptr);
+                       break;
+               case QLA82XX_RDOCM:
+                       qla4_8xxx_minidump_process_rdocm(ha, entry_hdr,
+                                                        &data_ptr);
+                       break;
+               case QLA82XX_RDMUX:
+                       qla4_8xxx_minidump_process_rdmux(ha, entry_hdr,
+                                                        &data_ptr);
+                       break;
+               case QLA82XX_QUEUE:
+                       qla4_8xxx_minidump_process_queue(ha, entry_hdr,
+                                                        &data_ptr);
+                       break;
+               case QLA82XX_RDNOP:
+               default:
+                       ql4_8xxx_mark_entry_skipped(ha, entry_hdr, i);
+                       break;
+               }
+
+               data_collected = (uint8_t *)data_ptr -
+                                ((uint8_t *)((uint8_t *)ha->fw_dump +
+                                               ha->fw_dump_tmplt_size));
+skip_nxt_entry:
+               /*  next entry in the template */
+               entry_hdr = (struct qla82xx_minidump_entry_hdr *)
+                               (((uint8_t *)entry_hdr) +
+                                entry_hdr->entry_size);
+       }
+
+       if ((data_collected + ha->fw_dump_tmplt_size) != ha->fw_dump_size) {
+               ql4_printk(KERN_INFO, ha,
+                          "Dump data mismatch: Data collected: [0x%x], total_data_size:[0x%x]\n",
+                          data_collected, ha->fw_dump_size);
+               goto md_failed;
+       }
+
+       DEBUG2(ql4_printk(KERN_INFO, ha, "Leaving fn: %s Last entry: 0x%x\n",
+                         __func__, i));
+md_failed:
+       return rval;
+}
+
+/**
+ * qla4_8xxx_uevent_emit - Send uevent when the firmware dump is ready.
+ * @ha: pointer to adapter structure
+ **/
+static void qla4_8xxx_uevent_emit(struct scsi_qla_host *ha, u32 code)
+{
+       char event_string[40];
+       char *envp[] = { event_string, NULL };
+
+       switch (code) {
+       case QL4_UEVENT_CODE_FW_DUMP:
+               snprintf(event_string, sizeof(event_string), "FW_DUMP=%ld",
+                        ha->host_no);
+               break;
+       default:
+               /*do nothing*/
+               break;
+       }
+
+       kobject_uevent_env(&(&ha->pdev->dev)->kobj, KOBJ_CHANGE, envp);
+}
+
 /**
  * qla4_8xxx_device_bootstrap - Initialize device, set DEV_READY, start fw
  * @ha: pointer to adapter structure
@@ -1659,6 +2324,15 @@ dev_initialize:
        qla4_8xxx_wr_32(ha, QLA82XX_CRB_DRV_IDC_VERSION, QLA82XX_IDC_VERSION);
 
        qla4_8xxx_idc_unlock(ha);
+       if (ql4xenablemd && test_bit(AF_FW_RECOVERY, &ha->flags) &&
+           !test_and_set_bit(AF_82XX_FW_DUMPED, &ha->flags)) {
+               if (!qla4_8xxx_collect_md_data(ha)) {
+                       qla4_8xxx_uevent_emit(ha, QL4_UEVENT_CODE_FW_DUMP);
+               } else {
+                       ql4_printk(KERN_INFO, ha, "Unable to collect minidump\n");
+                       clear_bit(AF_82XX_FW_DUMPED, &ha->flags);
+               }
+       }
        rval = qla4_8xxx_try_start_fw(ha);
        qla4_8xxx_idc_lock(ha);
 
@@ -1686,6 +2360,7 @@ static void
 qla4_8xxx_need_reset_handler(struct scsi_qla_host *ha)
 {
        uint32_t dev_state, drv_state, drv_active;
+       uint32_t active_mask = 0xFFFFFFFF;
        unsigned long reset_timeout;
 
        ql4_printk(KERN_INFO, ha,
@@ -1697,7 +2372,14 @@ qla4_8xxx_need_reset_handler(struct scsi_qla_host *ha)
                qla4_8xxx_idc_lock(ha);
        }
 
-       qla4_8xxx_set_rst_ready(ha);
+       if (!test_bit(AF_82XX_RST_OWNER, &ha->flags)) {
+               DEBUG2(ql4_printk(KERN_INFO, ha,
+                                 "%s(%ld): reset acknowledged\n",
+                                 __func__, ha->host_no));
+               qla4_8xxx_set_rst_ready(ha);
+       } else {
+               active_mask = (~(1 << (ha->func_num * 4)));
+       }
 
        /* wait for 10 seconds for reset ack from all functions */
        reset_timeout = jiffies + (ha->nx_reset_timeout * HZ);
@@ -1709,12 +2391,24 @@ qla4_8xxx_need_reset_handler(struct scsi_qla_host *ha)
                "%s(%ld): drv_state = 0x%x, drv_active = 0x%x\n",
                __func__, ha->host_no, drv_state, drv_active);
 
-       while (drv_state != drv_active) {
+       while (drv_state != (drv_active & active_mask)) {
                if (time_after_eq(jiffies, reset_timeout)) {
-                       printk("%s: RESET TIMEOUT!\n", DRIVER_NAME);
+                       ql4_printk(KERN_INFO, ha,
+                                  "%s: RESET TIMEOUT! drv_state: 0x%08x, drv_active: 0x%08x\n",
+                                  DRIVER_NAME, drv_state, drv_active);
                        break;
                }
 
+               /*
+                * When reset_owner times out, check which functions
+                * acked/did not ack
+                */
+               if (test_bit(AF_82XX_RST_OWNER, &ha->flags)) {
+                       ql4_printk(KERN_INFO, ha,
+                                  "%s(%ld): drv_state = 0x%x, drv_active = 0x%x\n",
+                                  __func__, ha->host_no, drv_state,
+                                  drv_active);
+               }
                qla4_8xxx_idc_unlock(ha);
                msleep(1000);
                qla4_8xxx_idc_lock(ha);
@@ -1723,14 +2417,18 @@ qla4_8xxx_need_reset_handler(struct scsi_qla_host *ha)
                drv_active = qla4_8xxx_rd_32(ha, QLA82XX_CRB_DRV_ACTIVE);
        }
 
+       /* Clear RESET OWNER as we are not going to use it any further */
+       clear_bit(AF_82XX_RST_OWNER, &ha->flags);
+
        dev_state = qla4_8xxx_rd_32(ha, QLA82XX_CRB_DEV_STATE);
-       ql4_printk(KERN_INFO, ha, "3:Device state is 0x%x = %s\n", dev_state,
-               dev_state < MAX_STATES ? qdev_state[dev_state] : "Unknown");
+       ql4_printk(KERN_INFO, ha, "Device state is 0x%x = %s\n", dev_state,
+                  dev_state < MAX_STATES ? qdev_state[dev_state] : "Unknown");
 
        /* Force to DEV_COLD unless someone else is starting a reset */
        if (dev_state != QLA82XX_DEV_INITIALIZING) {
                ql4_printk(KERN_INFO, ha, "HW State: COLD/RE-INIT\n");
                qla4_8xxx_wr_32(ha, QLA82XX_CRB_DEV_STATE, QLA82XX_DEV_COLD);
+               qla4_8xxx_set_rst_ready(ha);
        }
 }
 
@@ -1765,8 +2463,9 @@ int qla4_8xxx_device_state_handler(struct scsi_qla_host *ha)
        }
 
        dev_state = qla4_8xxx_rd_32(ha, QLA82XX_CRB_DEV_STATE);
-       ql4_printk(KERN_INFO, ha, "1:Device state is 0x%x = %s\n", dev_state,
-               dev_state < MAX_STATES ? qdev_state[dev_state] : "Unknown");
+       DEBUG2(ql4_printk(KERN_INFO, ha, "Device state is 0x%x = %s\n",
+                         dev_state, dev_state < MAX_STATES ?
+                         qdev_state[dev_state] : "Unknown"));
 
        /* wait for 30 seconds for device to go ready */
        dev_init_timeout = jiffies + (ha->nx_dev_init_timeout * HZ);
@@ -1775,15 +2474,19 @@ int qla4_8xxx_device_state_handler(struct scsi_qla_host *ha)
        while (1) {
 
                if (time_after_eq(jiffies, dev_init_timeout)) {
-                       ql4_printk(KERN_WARNING, ha, "Device init failed!\n");
+                       ql4_printk(KERN_WARNING, ha,
+                                  "%s: Device Init Failed 0x%x = %s\n",
+                                  DRIVER_NAME,
+                                  dev_state, dev_state < MAX_STATES ?
+                                  qdev_state[dev_state] : "Unknown");
                        qla4_8xxx_wr_32(ha, QLA82XX_CRB_DEV_STATE,
                                QLA82XX_DEV_FAILED);
                }
 
                dev_state = qla4_8xxx_rd_32(ha, QLA82XX_CRB_DEV_STATE);
-               ql4_printk(KERN_INFO, ha,
-                   "2:Device state is 0x%x = %s\n", dev_state,
-                   dev_state < MAX_STATES ? qdev_state[dev_state] : "Unknown");
+               ql4_printk(KERN_INFO, ha, "Device state is 0x%x = %s\n",
+                          dev_state, dev_state < MAX_STATES ?
+                          qdev_state[dev_state] : "Unknown");
 
                /* NOTE: Make sure idc unlocked upon exit of switch statement */
                switch (dev_state) {
@@ -2184,6 +2887,7 @@ qla4_8xxx_isp_reset(struct scsi_qla_host *ha)
                ql4_printk(KERN_INFO, ha, "HW State: NEED RESET\n");
                qla4_8xxx_wr_32(ha, QLA82XX_CRB_DEV_STATE,
                    QLA82XX_DEV_NEED_RESET);
+               set_bit(AF_82XX_RST_OWNER, &ha->flags);
        } else
                ql4_printk(KERN_INFO, ha, "HW State: DEVICE INITIALIZING\n");
 
@@ -2195,8 +2899,10 @@ qla4_8xxx_isp_reset(struct scsi_qla_host *ha)
        qla4_8xxx_clear_rst_ready(ha);
        qla4_8xxx_idc_unlock(ha);
 
-       if (rval == QLA_SUCCESS)
+       if (rval == QLA_SUCCESS) {
+               ql4_printk(KERN_INFO, ha, "Clearing AF_RECOVERY in qla4_8xxx_isp_reset\n");
                clear_bit(AF_FW_RECOVERY, &ha->flags);
+       }
 
        return rval;
 }
index dc7500e47b8b8e3a564cae69f4f158ec0e6a8a07..30258479f100370400590278f725d6da470b49a1 100644 (file)
@@ -792,4 +792,196 @@ struct crb_addr_pair {
 #define MIU_TEST_AGT_WRDATA_UPPER_LO   (0x0b0)
 #define        MIU_TEST_AGT_WRDATA_UPPER_HI    (0x0b4)
 
+/* Minidump related */
+
+/* Entry Type Defines */
+#define QLA82XX_RDNOP  0
+#define QLA82XX_RDCRB  1
+#define QLA82XX_RDMUX  2
+#define QLA82XX_QUEUE  3
+#define QLA82XX_BOARD  4
+#define QLA82XX_RDOCM  6
+#define QLA82XX_PREGS  7
+#define QLA82XX_L1DTG  8
+#define QLA82XX_L1ITG  9
+#define QLA82XX_L1DAT  11
+#define QLA82XX_L1INS  12
+#define QLA82XX_L2DTG  21
+#define QLA82XX_L2ITG  22
+#define QLA82XX_L2DAT  23
+#define QLA82XX_L2INS  24
+#define QLA82XX_RDROM  71
+#define QLA82XX_RDMEM  72
+#define QLA82XX_CNTRL  98
+#define QLA82XX_RDEND  255
+
+/* Opcodes for Control Entries.
+ * These Flags are bit fields.
+ */
+#define QLA82XX_DBG_OPCODE_WR          0x01
+#define QLA82XX_DBG_OPCODE_RW          0x02
+#define QLA82XX_DBG_OPCODE_AND         0x04
+#define QLA82XX_DBG_OPCODE_OR          0x08
+#define QLA82XX_DBG_OPCODE_POLL                0x10
+#define QLA82XX_DBG_OPCODE_RDSTATE     0x20
+#define QLA82XX_DBG_OPCODE_WRSTATE     0x40
+#define QLA82XX_DBG_OPCODE_MDSTATE     0x80
+
+/* Driver Flags */
+#define QLA82XX_DBG_SKIPPED_FLAG       0x80 /* driver skipped this entry  */
+#define QLA82XX_DBG_SIZE_ERR_FLAG      0x40 /* Entry vs Capture size
+                                             * mismatch */
+
+/* Driver_code is for driver to write some info about the entry
+ * currently not used.
+ */
+struct qla82xx_minidump_entry_hdr {
+       uint32_t entry_type;
+       uint32_t entry_size;
+       uint32_t entry_capture_size;
+       struct {
+               uint8_t entry_capture_mask;
+               uint8_t entry_code;
+               uint8_t driver_code;
+               uint8_t driver_flags;
+       } d_ctrl;
+};
+
+/*  Read CRB entry header */
+struct qla82xx_minidump_entry_crb {
+       struct qla82xx_minidump_entry_hdr h;
+       uint32_t addr;
+       struct {
+               uint8_t addr_stride;
+               uint8_t state_index_a;
+               uint16_t poll_timeout;
+       } crb_strd;
+       uint32_t data_size;
+       uint32_t op_count;
+
+       struct {
+               uint8_t opcode;
+               uint8_t state_index_v;
+               uint8_t shl;
+               uint8_t shr;
+       } crb_ctrl;
+
+       uint32_t value_1;
+       uint32_t value_2;
+       uint32_t value_3;
+};
+
+struct qla82xx_minidump_entry_cache {
+       struct qla82xx_minidump_entry_hdr h;
+       uint32_t tag_reg_addr;
+       struct {
+               uint16_t tag_value_stride;
+               uint16_t init_tag_value;
+       } addr_ctrl;
+       uint32_t data_size;
+       uint32_t op_count;
+       uint32_t control_addr;
+       struct {
+               uint16_t write_value;
+               uint8_t poll_mask;
+               uint8_t poll_wait;
+       } cache_ctrl;
+       uint32_t read_addr;
+       struct {
+               uint8_t read_addr_stride;
+               uint8_t read_addr_cnt;
+               uint16_t rsvd_1;
+       } read_ctrl;
+};
+
+/* Read OCM */
+struct qla82xx_minidump_entry_rdocm {
+       struct qla82xx_minidump_entry_hdr h;
+       uint32_t rsvd_0;
+       uint32_t rsvd_1;
+       uint32_t data_size;
+       uint32_t op_count;
+       uint32_t rsvd_2;
+       uint32_t rsvd_3;
+       uint32_t read_addr;
+       uint32_t read_addr_stride;
+};
+
+/* Read Memory */
+struct qla82xx_minidump_entry_rdmem {
+       struct qla82xx_minidump_entry_hdr h;
+       uint32_t rsvd[6];
+       uint32_t read_addr;
+       uint32_t read_data_size;
+};
+
+/* Read ROM */
+struct qla82xx_minidump_entry_rdrom {
+       struct qla82xx_minidump_entry_hdr h;
+       uint32_t rsvd[6];
+       uint32_t read_addr;
+       uint32_t read_data_size;
+};
+
+/* Mux entry */
+struct qla82xx_minidump_entry_mux {
+       struct qla82xx_minidump_entry_hdr h;
+       uint32_t select_addr;
+       uint32_t rsvd_0;
+       uint32_t data_size;
+       uint32_t op_count;
+       uint32_t select_value;
+       uint32_t select_value_stride;
+       uint32_t read_addr;
+       uint32_t rsvd_1;
+};
+
+/* Queue entry */
+struct qla82xx_minidump_entry_queue {
+       struct qla82xx_minidump_entry_hdr h;
+       uint32_t select_addr;
+       struct {
+               uint16_t queue_id_stride;
+               uint16_t rsvd_0;
+       } q_strd;
+       uint32_t data_size;
+       uint32_t op_count;
+       uint32_t rsvd_1;
+       uint32_t rsvd_2;
+       uint32_t read_addr;
+       struct {
+               uint8_t read_addr_stride;
+               uint8_t read_addr_cnt;
+               uint16_t rsvd_3;
+       } rd_strd;
+};
+
+#define QLA82XX_MINIDUMP_OCM0_SIZE             (256 * 1024)
+#define QLA82XX_MINIDUMP_L1C_SIZE              (256 * 1024)
+#define QLA82XX_MINIDUMP_L2C_SIZE              1572864
+#define QLA82XX_MINIDUMP_COMMON_STR_SIZE       0
+#define QLA82XX_MINIDUMP_FCOE_STR_SIZE         0
+#define QLA82XX_MINIDUMP_MEM_SIZE              0
+#define QLA82XX_MAX_ENTRY_HDR                  4
+
+struct qla82xx_minidump {
+       uint32_t md_ocm0_data[QLA82XX_MINIDUMP_OCM0_SIZE];
+       uint32_t md_l1c_data[QLA82XX_MINIDUMP_L1C_SIZE];
+       uint32_t md_l2c_data[QLA82XX_MINIDUMP_L2C_SIZE];
+       uint32_t md_cs_data[QLA82XX_MINIDUMP_COMMON_STR_SIZE];
+       uint32_t md_fcoes_data[QLA82XX_MINIDUMP_FCOE_STR_SIZE];
+       uint32_t md_mem_data[QLA82XX_MINIDUMP_MEM_SIZE];
+};
+
+#define MBC_DIAGNOSTIC_MINIDUMP_TEMPLATE       0x129
+#define RQST_TMPLT_SIZE                                0x0
+#define RQST_TMPLT                             0x1
+#define MD_DIRECT_ROM_WINDOW                   0x42110030
+#define MD_DIRECT_ROM_READ_BASE                        0x42150000
+#define MD_MIU_TEST_AGT_CTRL                   0x41000090
+#define MD_MIU_TEST_AGT_ADDR_LO                        0x41000094
+#define MD_MIU_TEST_AGT_ADDR_HI                        0x41000098
+
+static const int MD_MIU_TEST_AGT_RDDATA[] = { 0x410000A8,
+                               0x410000AC, 0x410000B8, 0x410000BC };
 #endif
index ee47820c30a6591824cfa42abff426fb09114e7b..cd15678f9ada740d4448f7b04f96ea89b1a956c5 100644 (file)
@@ -68,12 +68,34 @@ MODULE_PARM_DESC(ql4xmaxqdepth,
                 " Maximum queue depth to report for target devices.\n"
                 "\t\t  Default: 32.");
 
+static int ql4xqfulltracking = 1;
+module_param(ql4xqfulltracking, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(ql4xqfulltracking,
+                " Enable or disable dynamic tracking and adjustment of\n"
+                "\t\t scsi device queue depth.\n"
+                "\t\t  0 - Disable.\n"
+                "\t\t  1 - Enable. (Default)");
+
 static int ql4xsess_recovery_tmo = QL4_SESS_RECOVERY_TMO;
 module_param(ql4xsess_recovery_tmo, int, S_IRUGO);
 MODULE_PARM_DESC(ql4xsess_recovery_tmo,
                " Target Session Recovery Timeout.\n"
                "\t\t  Default: 120 sec.");
 
+int ql4xmdcapmask = 0x1F;
+module_param(ql4xmdcapmask, int, S_IRUGO);
+MODULE_PARM_DESC(ql4xmdcapmask,
+                " Set the Minidump driver capture mask level.\n"
+                "\t\t  Default is 0x1F.\n"
+                "\t\t  Can be set to 0x3, 0x7, 0xF, 0x1F, 0x3F, 0x7F");
+
+int ql4xenablemd = 1;
+module_param(ql4xenablemd, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(ql4xenablemd,
+                " Set to enable minidump.\n"
+                "\t\t  0 - disable minidump\n"
+                "\t\t  1 - enable minidump (Default)");
+
 static int qla4xxx_wait_for_hba_online(struct scsi_qla_host *ha);
 /*
  * SCSI host template entry points
@@ -140,6 +162,8 @@ static int qla4xxx_slave_configure(struct scsi_device *device);
 static void qla4xxx_slave_destroy(struct scsi_device *sdev);
 static umode_t ql4_attr_is_visible(int param_type, int param);
 static int qla4xxx_host_reset(struct Scsi_Host *shost, int reset_type);
+static int qla4xxx_change_queue_depth(struct scsi_device *sdev, int qdepth,
+                                     int reason);
 
 static struct qla4_8xxx_legacy_intr_set legacy_intr[] =
     QLA82XX_LEGACY_INTR_CONFIG;
@@ -159,6 +183,7 @@ static struct scsi_host_template qla4xxx_driver_template = {
        .slave_configure        = qla4xxx_slave_configure,
        .slave_alloc            = qla4xxx_slave_alloc,
        .slave_destroy          = qla4xxx_slave_destroy,
+       .change_queue_depth     = qla4xxx_change_queue_depth,
 
        .this_id                = -1,
        .cmd_per_lun            = 3,
@@ -1555,19 +1580,53 @@ static void qla4xxx_session_destroy(struct iscsi_cls_session *cls_sess)
        struct iscsi_session *sess;
        struct ddb_entry *ddb_entry;
        struct scsi_qla_host *ha;
-       unsigned long flags;
+       unsigned long flags, wtime;
+       struct dev_db_entry *fw_ddb_entry = NULL;
+       dma_addr_t fw_ddb_entry_dma;
+       uint32_t ddb_state;
+       int ret;
 
        DEBUG2(printk(KERN_INFO "Func: %s\n", __func__));
        sess = cls_sess->dd_data;
        ddb_entry = sess->dd_data;
        ha = ddb_entry->ha;
 
+       fw_ddb_entry = dma_alloc_coherent(&ha->pdev->dev, sizeof(*fw_ddb_entry),
+                                         &fw_ddb_entry_dma, GFP_KERNEL);
+       if (!fw_ddb_entry) {
+               ql4_printk(KERN_ERR, ha,
+                          "%s: Unable to allocate dma buffer\n", __func__);
+               goto destroy_session;
+       }
+
+       wtime = jiffies + (HZ * LOGOUT_TOV);
+       do {
+               ret = qla4xxx_get_fwddb_entry(ha, ddb_entry->fw_ddb_index,
+                                             fw_ddb_entry, fw_ddb_entry_dma,
+                                             NULL, NULL, &ddb_state, NULL,
+                                             NULL, NULL);
+               if (ret == QLA_ERROR)
+                       goto destroy_session;
+
+               if ((ddb_state == DDB_DS_NO_CONNECTION_ACTIVE) ||
+                   (ddb_state == DDB_DS_SESSION_FAILED))
+                       goto destroy_session;
+
+               schedule_timeout_uninterruptible(HZ);
+       } while ((time_after(wtime, jiffies)));
+
+destroy_session:
        qla4xxx_clear_ddb_entry(ha, ddb_entry->fw_ddb_index);
 
        spin_lock_irqsave(&ha->hardware_lock, flags);
        qla4xxx_free_ddb(ha, ddb_entry);
        spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
        iscsi_session_teardown(cls_sess);
+
+       if (fw_ddb_entry)
+               dma_free_coherent(&ha->pdev->dev, sizeof(*fw_ddb_entry),
+                                 fw_ddb_entry, fw_ddb_entry_dma);
 }
 
 static struct iscsi_cls_conn *
@@ -2220,6 +2279,9 @@ static void qla4xxx_mem_free(struct scsi_qla_host *ha)
                dma_free_coherent(&ha->pdev->dev, ha->queues_len, ha->queues,
                                  ha->queues_dma);
 
+        if (ha->fw_dump)
+               vfree(ha->fw_dump);
+
        ha->queues_len = 0;
        ha->queues = NULL;
        ha->queues_dma = 0;
@@ -2229,6 +2291,8 @@ static void qla4xxx_mem_free(struct scsi_qla_host *ha)
        ha->response_dma = 0;
        ha->shadow_regs = NULL;
        ha->shadow_regs_dma = 0;
+       ha->fw_dump = NULL;
+       ha->fw_dump_size = 0;
 
        /* Free srb pool. */
        if (ha->srb_mempool)
@@ -5023,6 +5087,8 @@ static int __devinit qla4xxx_probe_adapter(struct pci_dev *pdev,
 
        set_bit(AF_INIT_DONE, &ha->flags);
 
+       qla4_8xxx_alloc_sysfs_attr(ha);
+
        printk(KERN_INFO
               " QLogic iSCSI HBA Driver version: %s\n"
               "  QLogic ISP%04x @ %s, host#=%ld, fw=%02d.%02d.%02d.%02d\n",
@@ -5149,6 +5215,7 @@ static void __devexit qla4xxx_remove_adapter(struct pci_dev *pdev)
                iscsi_boot_destroy_kset(ha->boot_kset);
 
        qla4xxx_destroy_fw_ddb_session(ha);
+       qla4_8xxx_free_sysfs_attr(ha);
 
        scsi_remove_host(ha->host);
 
@@ -5217,6 +5284,15 @@ static void qla4xxx_slave_destroy(struct scsi_device *sdev)
        scsi_deactivate_tcq(sdev, 1);
 }
 
+static int qla4xxx_change_queue_depth(struct scsi_device *sdev, int qdepth,
+                                     int reason)
+{
+       if (!ql4xqfulltracking)
+               return -EOPNOTSUPP;
+
+       return iscsi_change_queue_depth(sdev, qdepth, reason);
+}
+
 /**
  * qla4xxx_del_from_active_array - returns an active srb
  * @ha: Pointer to host adapter structure.
index 97b30c108e365f6d22e93fc4ca826b6eb46c5e34..cc1cc3518b87cfad495304d247d8c5bf2e0332c2 100644 (file)
@@ -5,4 +5,4 @@
  * See LICENSE.qla4xxx for copyright and licensing details.
  */
 
-#define QLA4XXX_DRIVER_VERSION "5.02.00-k16"
+#define QLA4XXX_DRIVER_VERSION "5.02.00-k17"
index 62ddfd31d4ce3539b129ee3f7763d9422ab32f5e..6dfb9785d34581eb06395ef1d4b372b243de5d87 100644 (file)
@@ -1378,16 +1378,19 @@ static int scsi_lld_busy(struct request_queue *q)
 {
        struct scsi_device *sdev = q->queuedata;
        struct Scsi_Host *shost;
-       struct scsi_target *starget;
 
        if (!sdev)
                return 0;
 
        shost = sdev->host;
-       starget = scsi_target(sdev);
 
-       if (scsi_host_in_recovery(shost) || scsi_host_is_busy(shost) ||
-           scsi_target_is_busy(starget) || scsi_device_is_busy(sdev))
+       /*
+        * Ignore host/starget busy state.
+        * Since block layer does not have a concept of fairness across
+        * multiple queues, congestion of host/starget needs to be handled
+        * in SCSI layer.
+        */
+       if (scsi_host_in_recovery(shost) || scsi_device_is_busy(sdev))
                return 1;
 
        return 0;
index f661a41fa4c6fef7e054ed15dd1b73596bc99691..d4201ded3b2203c0bd9cc17d8a26b528f5ccfc2c 100644 (file)
@@ -24,8 +24,11 @@ static int scsi_dev_type_suspend(struct device *dev, pm_message_t msg)
        err = scsi_device_quiesce(to_scsi_device(dev));
        if (err == 0) {
                drv = dev->driver;
-               if (drv && drv->suspend)
+               if (drv && drv->suspend) {
                        err = drv->suspend(dev, msg);
+                       if (err)
+                               scsi_device_resume(to_scsi_device(dev));
+               }
        }
        dev_dbg(dev, "scsi suspend: %d\n", err);
        return err;
index 01b03744f1f99ced5879d0f9e9f3a7d1d0677c11..2e5fe584aad32d2130ad59945c922eaa238fb0be 100644 (file)
@@ -147,7 +147,7 @@ int scsi_complete_async_scans(void)
 
        do {
                if (list_empty(&scanning_hosts))
-                       return 0;
+                       goto out;
                /* If we can't get memory immediately, that's OK.  Just
                 * sleep a little.  Even if we never get memory, the async
                 * scans will finish eventually.
@@ -179,8 +179,11 @@ int scsi_complete_async_scans(void)
        }
  done:
        spin_unlock(&async_scan_lock);
-
        kfree(data);
+
+ out:
+       async_synchronize_full_domain(&scsi_sd_probe_domain);
+
        return 0;
 }
 
index 74708fcaf82fe900c3a77689ccad11c91b48b173..ae781487461829ae190f41f52d6f3e267e1fa5bb 100644 (file)
@@ -12,7 +12,7 @@
 
 #include <linux/module.h>
 #include <linux/device.h>
-#include <scsi/scsi_scan.h>
+#include "scsi_priv.h"
 
 static int __init wait_scan_init(void)
 {
index 4e010b727818cd341968bdb378a1cb3f6e544cdf..6a4fd00117ca66667173a27728b7055d2e9a5f76 100644 (file)
@@ -1836,7 +1836,7 @@ ufshcd_probe(struct pci_dev *pdev, const struct pci_device_id *id)
        err = pci_request_regions(pdev, UFSHCD);
        if (err < 0) {
                dev_err(&pdev->dev, "request regions failed\n");
-               goto out_disable;
+               goto out_host_put;
        }
 
        hba->mmio_base = pci_ioremap_bar(pdev, 0);
@@ -1925,8 +1925,9 @@ out_iounmap:
        iounmap(hba->mmio_base);
 out_release_regions:
        pci_release_regions(pdev);
-out_disable:
+out_host_put:
        scsi_host_put(host);
+out_disable:
        pci_clear_master(pdev);
        pci_disable_device(pdev);
 out_error:
index 4511420849bc1dd61368e7cbbfc7b8080e819bee..e84dbecd09911715817be0b43339ec65bc956441 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/module.h>
 #include <linux/file.h>
 #include <linux/fs.h>
+#include <linux/falloc.h>
 #include <linux/miscdevice.h>
 #include <linux/security.h>
 #include <linux/mm.h>
@@ -363,11 +364,12 @@ static int ashmem_shrink(struct shrinker *s, struct shrink_control *sc)
 
        mutex_lock(&ashmem_mutex);
        list_for_each_entry_safe(range, next, &ashmem_lru_list, lru) {
-               struct inode *inode = range->asma->file->f_dentry->d_inode;
                loff_t start = range->pgstart * PAGE_SIZE;
-               loff_t end = (range->pgend + 1) * PAGE_SIZE - 1;
+               loff_t end = (range->pgend + 1) * PAGE_SIZE;
 
-               vmtruncate_range(inode, start, end);
+               do_fallocate(range->asma->file,
+                               FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
+                               start, end - start);
                range->purged = ASHMEM_WAS_PURGED;
                lru_del(range);
 
index 59af3945ea859c334719fed6a41b4192a30f87f5..65c7c62c7aae9b2d81b936a45f0d67f659507324 100644 (file)
@@ -633,7 +633,6 @@ static int ptmx_open(struct inode *inode, struct file *filp)
        mutex_unlock(&devpts_mutex);
 
        mutex_lock(&tty_mutex);
-       mutex_lock(&devpts_mutex);
        tty = tty_init_dev(ptm_driver, index);
 
        if (IS_ERR(tty)) {
@@ -643,7 +642,6 @@ static int ptmx_open(struct inode *inode, struct file *filp)
 
        /* The tty returned here is locked so we can safely
           drop the mutex */
-       mutex_unlock(&devpts_mutex);
        mutex_unlock(&tty_mutex);
 
        set_bit(TTY_PTY_LOCK, &tty->flags); /* LOCK THE SLAVE */
index 96c1cacc73608c277de8f5379147498ca67f849f..02da071fe1e7e3a4796ae2b43f92739525eed59f 100644 (file)
 #include <linux/tty_flip.h>
 #include <linux/serial_core.h>
 #include <linux/serial.h>
-#include <linux/platform_device.h>
+#include <linux/of_platform.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
 #include <linux/io.h>
 #include <linux/clk.h>
+#include <linux/gpio.h>
 
 #include <lantiq_soc.h>
 
 #define PORT_LTQ_ASC           111
 #define MAXPORTS               2
 #define UART_DUMMY_UER_RX      1
-#define DRVNAME                        "ltq_asc"
+#define DRVNAME                        "lantiq,asc"
 #ifdef __BIG_ENDIAN
 #define LTQ_ASC_TBUF           (0x0020 + 3)
 #define LTQ_ASC_RBUF           (0x0024 + 3)
@@ -114,6 +117,9 @@ static DEFINE_SPINLOCK(ltq_asc_lock);
 
 struct ltq_uart_port {
        struct uart_port        port;
+       /* clock used to derive divider */
+       struct clk              *fpiclk;
+       /* clock gating of the ASC core */
        struct clk              *clk;
        unsigned int            tx_irq;
        unsigned int            rx_irq;
@@ -316,7 +322,9 @@ lqasc_startup(struct uart_port *port)
        struct ltq_uart_port *ltq_port = to_ltq_uart_port(port);
        int retval;
 
-       port->uartclk = clk_get_rate(ltq_port->clk);
+       if (ltq_port->clk)
+               clk_enable(ltq_port->clk);
+       port->uartclk = clk_get_rate(ltq_port->fpiclk);
 
        ltq_w32_mask(ASCCLC_DISS | ASCCLC_RMCMASK, (1 << ASCCLC_RMCOFFSET),
                port->membase + LTQ_ASC_CLC);
@@ -382,6 +390,8 @@ lqasc_shutdown(struct uart_port *port)
                port->membase + LTQ_ASC_RXFCON);
        ltq_w32_mask(ASCTXFCON_TXFEN, ASCTXFCON_TXFFLU,
                port->membase + LTQ_ASC_TXFCON);
+       if (ltq_port->clk)
+               clk_disable(ltq_port->clk);
 }
 
 static void
@@ -630,7 +640,7 @@ lqasc_console_setup(struct console *co, char *options)
 
        port = &ltq_port->port;
 
-       port->uartclk = clk_get_rate(ltq_port->clk);
+       port->uartclk = clk_get_rate(ltq_port->fpiclk);
 
        if (options)
                uart_parse_options(options, &baud, &parity, &bits, &flow);
@@ -668,37 +678,32 @@ static struct uart_driver lqasc_reg = {
 static int __init
 lqasc_probe(struct platform_device *pdev)
 {
+       struct device_node *node = pdev->dev.of_node;
        struct ltq_uart_port *ltq_port;
        struct uart_port *port;
-       struct resource *mmres, *irqres;
-       int tx_irq, rx_irq, err_irq;
-       struct clk *clk;
+       struct resource *mmres, irqres[3];
+       int line = 0;
        int ret;
 
        mmres = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       irqres = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
-       if (!mmres || !irqres)
+       ret = of_irq_to_resource_table(node, irqres, 3);
+       if (!mmres || (ret != 3)) {
+               dev_err(&pdev->dev,
+                       "failed to get memory/irq for serial port\n");
                return -ENODEV;
+       }
 
-       if (pdev->id >= MAXPORTS)
-               return -EBUSY;
+       /* check if this is the console port */
+       if (mmres->start != CPHYSADDR(LTQ_EARLY_ASC))
+               line = 1;
 
-       if (lqasc_port[pdev->id] != NULL)
+       if (lqasc_port[line]) {
+               dev_err(&pdev->dev, "port %d already allocated\n", line);
                return -EBUSY;
-
-       clk = clk_get(&pdev->dev, "fpi");
-       if (IS_ERR(clk)) {
-               pr_err("failed to get fpi clk\n");
-               return -ENOENT;
        }
 
-       tx_irq = platform_get_irq_byname(pdev, "tx");
-       rx_irq = platform_get_irq_byname(pdev, "rx");
-       err_irq = platform_get_irq_byname(pdev, "err");
-       if ((tx_irq < 0) | (rx_irq < 0) | (err_irq < 0))
-               return -ENODEV;
-
-       ltq_port = kzalloc(sizeof(struct ltq_uart_port), GFP_KERNEL);
+       ltq_port = devm_kzalloc(&pdev->dev, sizeof(struct ltq_uart_port),
+                       GFP_KERNEL);
        if (!ltq_port)
                return -ENOMEM;
 
@@ -709,19 +714,26 @@ lqasc_probe(struct platform_device *pdev)
        port->ops       = &lqasc_pops;
        port->fifosize  = 16;
        port->type      = PORT_LTQ_ASC,
-       port->line      = pdev->id;
+       port->line      = line;
        port->dev       = &pdev->dev;
-
-       port->irq       = tx_irq; /* unused, just to be backward-compatibe */
+       /* unused, just to be backward-compatible */
+       port->irq       = irqres[0].start;
        port->mapbase   = mmres->start;
 
-       ltq_port->clk   = clk;
+       ltq_port->fpiclk = clk_get_fpi();
+       if (IS_ERR(ltq_port->fpiclk)) {
+               pr_err("failed to get fpi clk\n");
+               return -ENOENT;
+       }
 
-       ltq_port->tx_irq = tx_irq;
-       ltq_port->rx_irq = rx_irq;
-       ltq_port->err_irq = err_irq;
+       /* not all asc ports have clock gates, lets ignore the return code */
+       ltq_port->clk = clk_get(&pdev->dev, NULL);
 
-       lqasc_port[pdev->id] = ltq_port;
+       ltq_port->tx_irq = irqres[0].start;
+       ltq_port->rx_irq = irqres[1].start;
+       ltq_port->err_irq = irqres[2].start;
+
+       lqasc_port[line] = ltq_port;
        platform_set_drvdata(pdev, ltq_port);
 
        ret = uart_add_one_port(&lqasc_reg, port);
@@ -729,10 +741,17 @@ lqasc_probe(struct platform_device *pdev)
        return ret;
 }
 
+static const struct of_device_id ltq_asc_match[] = {
+       { .compatible = DRVNAME },
+       {},
+};
+MODULE_DEVICE_TABLE(of, ltq_asc_match);
+
 static struct platform_driver lqasc_driver = {
        .driver         = {
                .name   = DRVNAME,
                .owner  = THIS_MODULE,
+               .of_match_table = ltq_asc_match,
        },
 };
 
index 0be8a2f00d0ba92b76c990c8204f23ce4cc0d84d..f76b1688c5c864fc1a7d8f2619f04a1d0566acab 100644 (file)
@@ -31,6 +31,7 @@
 #include <linux/interrupt.h>
 #include <linux/ioport.h>
 #include <linux/kernel.h>
+#include <linux/module.h>
 #include <linux/major.h>
 #include <linux/serial.h>
 #include <linux/serial_core.h>
index 4001eee6c08de214f596e932c7fe454dc0f4c2f8..92c00b24d0df961f62b47b39dd0bffc40de2f47e 100644 (file)
@@ -57,6 +57,7 @@
 #include <linux/ioport.h>
 #include <linux/irqflags.h>
 #include <linux/kernel.h>
+#include <linux/module.h>
 #include <linux/major.h>
 #include <linux/serial.h>
 #include <linux/serial_core.h>
index 173a9000a6cb3940ab2dcb068c5efba300f9613e..ba8be396a6215339200c5ff3f79ad34e93489acb 100644 (file)
@@ -894,6 +894,23 @@ int tty_ldisc_setup(struct tty_struct *tty, struct tty_struct *o_tty)
        tty_ldisc_enable(tty);
        return 0;
 }
+
+static void tty_ldisc_kill(struct tty_struct *tty)
+{
+       mutex_lock(&tty->ldisc_mutex);
+       /*
+        * Now kill off the ldisc
+        */
+       tty_ldisc_close(tty, tty->ldisc);
+       tty_ldisc_put(tty->ldisc);
+       /* Force an oops if we mess this up */
+       tty->ldisc = NULL;
+
+       /* Ensure the next open requests the N_TTY ldisc */
+       tty_set_termios_ldisc(tty, N_TTY);
+       mutex_unlock(&tty->ldisc_mutex);
+}
+
 /**
  *     tty_ldisc_release               -       release line discipline
  *     @tty: tty being shut down
@@ -912,27 +929,19 @@ void tty_ldisc_release(struct tty_struct *tty, struct tty_struct *o_tty)
         * race with the set_ldisc code path.
         */
 
-       tty_unlock(tty);
+       tty_unlock_pair(tty, o_tty);
        tty_ldisc_halt(tty);
        tty_ldisc_flush_works(tty);
-       tty_lock(tty);
-
-       mutex_lock(&tty->ldisc_mutex);
-       /*
-        * Now kill off the ldisc
-        */
-       tty_ldisc_close(tty, tty->ldisc);
-       tty_ldisc_put(tty->ldisc);
-       /* Force an oops if we mess this up */
-       tty->ldisc = NULL;
+       if (o_tty) {
+               tty_ldisc_halt(o_tty);
+               tty_ldisc_flush_works(o_tty);
+       }
+       tty_lock_pair(tty, o_tty);
 
-       /* Ensure the next open requests the N_TTY ldisc */
-       tty_set_termios_ldisc(tty, N_TTY);
-       mutex_unlock(&tty->ldisc_mutex);
 
-       /* This will need doing differently if we need to lock */
+       tty_ldisc_kill(tty);
        if (o_tty)
-               tty_ldisc_release(o_tty, NULL);
+               tty_ldisc_kill(o_tty);
 
        /* And the memory resources remaining (buffers, termios) will be
           disposed of when the kref hits zero */
index 69adc80c98cd5c035fa0f3a18df6fc3e3c127623..67feac9e6ebbef7e224c47672bff95f7f511c5a2 100644 (file)
@@ -6,11 +6,17 @@
 
 /* Legacy tty mutex glue */
 
+enum {
+       TTY_MUTEX_NORMAL,
+       TTY_MUTEX_NESTED,
+};
+
 /*
  * Getting the big tty mutex.
  */
 
-void __lockfunc tty_lock(struct tty_struct *tty)
+static void __lockfunc tty_lock_nested(struct tty_struct *tty,
+                                      unsigned int subclass)
 {
        if (tty->magic != TTY_MAGIC) {
                printk(KERN_ERR "L Bad %p\n", tty);
@@ -18,7 +24,12 @@ void __lockfunc tty_lock(struct tty_struct *tty)
                return;
        }
        tty_kref_get(tty);
-       mutex_lock(&tty->legacy_mutex);
+       mutex_lock_nested(&tty->legacy_mutex, subclass);
+}
+
+void __lockfunc tty_lock(struct tty_struct *tty)
+{
+       return tty_lock_nested(tty, TTY_MUTEX_NORMAL);
 }
 EXPORT_SYMBOL(tty_lock);
 
@@ -43,11 +54,11 @@ void __lockfunc tty_lock_pair(struct tty_struct *tty,
 {
        if (tty < tty2) {
                tty_lock(tty);
-               tty_lock(tty2);
+               tty_lock_nested(tty2, TTY_MUTEX_NESTED);
        } else {
                if (tty2 && tty2 != tty)
                        tty_lock(tty2);
-               tty_lock(tty);
+               tty_lock_nested(tty, TTY_MUTEX_NESTED);
        }
 }
 EXPORT_SYMBOL(tty_lock_pair);
index af16884491edf45cf3105f79e340a6b912757541..fa2b03750316b175f36f72d6f7cf8cf8a69bc375 100644 (file)
@@ -184,6 +184,18 @@ config BACKLIGHT_GENERIC
          known as the Corgi backlight driver. If you have a Sharp Zaurus
          SL-C7xx, SL-Cxx00 or SL-6000x say y.
 
+config BACKLIGHT_LM3533
+       tristate "Backlight Driver for LM3533"
+       depends on BACKLIGHT_CLASS_DEVICE
+       depends on MFD_LM3533
+       help
+         Say Y to enable the backlight driver for National Semiconductor / TI
+         LM3533 Lighting Power chips.
+
+         The backlights can be controlled directly, through PWM input, or by
+         the ambient-light-sensor interface. The chip supports 256 brightness
+         levels.
+
 config BACKLIGHT_LOCOMO
        tristate "Sharp LOCOMO LCD/Backlight Driver"
        depends on SHARP_LOCOMO
index 36855ae887d6ae9f56a85a0b02b24a3afa271c83..a2ac9cfbaf6bf1869e1c2b020b597952d4a7633a 100644 (file)
@@ -21,6 +21,7 @@ obj-$(CONFIG_BACKLIGHT_EP93XX)        += ep93xx_bl.o
 obj-$(CONFIG_BACKLIGHT_GENERIC)        += generic_bl.o
 obj-$(CONFIG_BACKLIGHT_HP700)  += jornada720_bl.o
 obj-$(CONFIG_BACKLIGHT_HP680)  += hp680_bl.o
+obj-$(CONFIG_BACKLIGHT_LM3533) += lm3533_bl.o
 obj-$(CONFIG_BACKLIGHT_LOCOMO) += locomolcd.o
 obj-$(CONFIG_BACKLIGHT_LP855X) += lp855x_bl.o
 obj-$(CONFIG_BACKLIGHT_OMAP1)  += omap1_bl.o
index 4911ea7989c82553dd90558a8afc4637da8c539b..df5db99af23d7cfe711b6d155766ca362d89a7dd 100644 (file)
@@ -160,7 +160,7 @@ static ssize_t adp5520_store(struct device *dev, const char *buf,
        unsigned long val;
        int ret;
 
-       ret = strict_strtoul(buf, 10, &val);
+       ret = kstrtoul(buf, 10, &val);
        if (ret)
                return ret;
 
@@ -214,7 +214,7 @@ static ssize_t adp5520_bl_daylight_max_store(struct device *dev,
        struct adp5520_bl *data = dev_get_drvdata(dev);
        int ret;
 
-       ret = strict_strtoul(buf, 10, &data->cached_daylight_max);
+       ret = kstrtoul(buf, 10, &data->cached_daylight_max);
        if (ret < 0)
                return ret;
 
index 550dbf0bb896f9b64083b6673a8da0359ef53912..77d1fdba597fb561037d865176f261989b908845 100644 (file)
@@ -222,7 +222,8 @@ static int __devinit adp8860_led_probe(struct i2c_client *client)
        struct led_info *cur_led;
        int ret, i;
 
-       led = kzalloc(sizeof(*led) * pdata->num_leds, GFP_KERNEL);
+       led = devm_kzalloc(&client->dev, sizeof(*led) * pdata->num_leds,
+                               GFP_KERNEL);
        if (led == NULL) {
                dev_err(&client->dev, "failed to alloc memory\n");
                return -ENOMEM;
@@ -236,7 +237,7 @@ static int __devinit adp8860_led_probe(struct i2c_client *client)
 
        if (ret) {
                dev_err(&client->dev, "failed to write\n");
-               goto err_free;
+               return ret;
        }
 
        for (i = 0; i < pdata->num_leds; ++i) {
@@ -291,9 +292,6 @@ static int __devinit adp8860_led_probe(struct i2c_client *client)
                cancel_work_sync(&led[i].work);
        }
 
- err_free:
-       kfree(led);
-
        return ret;
 }
 
@@ -309,7 +307,6 @@ static int __devexit adp8860_led_remove(struct i2c_client *client)
                cancel_work_sync(&data->led[i].work);
        }
 
-       kfree(data->led);
        return 0;
 }
 #else
@@ -451,7 +448,7 @@ static ssize_t adp8860_store(struct device *dev, const char *buf,
        unsigned long val;
        int ret;
 
-       ret = strict_strtoul(buf, 10, &val);
+       ret = kstrtoul(buf, 10, &val);
        if (ret)
                return ret;
 
@@ -501,7 +498,7 @@ static ssize_t adp8860_bl_l1_daylight_max_store(struct device *dev,
                struct device_attribute *attr, const char *buf, size_t count)
 {
        struct adp8860_bl *data = dev_get_drvdata(dev);
-       int ret = strict_strtoul(buf, 10, &data->cached_daylight_max);
+       int ret = kstrtoul(buf, 10, &data->cached_daylight_max);
        if (ret)
                return ret;
 
@@ -608,7 +605,7 @@ static ssize_t adp8860_bl_ambient_light_zone_store(struct device *dev,
        uint8_t reg_val;
        int ret;
 
-       ret = strict_strtoul(buf, 10, &val);
+       ret = kstrtoul(buf, 10, &val);
        if (ret)
                return ret;
 
@@ -675,13 +672,13 @@ static int __devinit adp8860_probe(struct i2c_client *client,
                return -EINVAL;
        }
 
-       data = kzalloc(sizeof(*data), GFP_KERNEL);
+       data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
        if (data == NULL)
                return -ENOMEM;
 
        ret = adp8860_read(client, ADP8860_MFDVID, &reg_val);
        if (ret < 0)
-               goto out2;
+               return ret;
 
        switch (ADP8860_MANID(reg_val)) {
        case ADP8863_MANUFID:
@@ -694,8 +691,7 @@ static int __devinit adp8860_probe(struct i2c_client *client,
                break;
        default:
                dev_err(&client->dev, "failed to probe\n");
-               ret = -ENODEV;
-               goto out2;
+               return -ENODEV;
        }
 
        /* It's confirmed that the DEVID field is actually a REVID */
@@ -717,8 +713,7 @@ static int __devinit adp8860_probe(struct i2c_client *client,
                        &client->dev, data, &adp8860_bl_ops, &props);
        if (IS_ERR(bl)) {
                dev_err(&client->dev, "failed to register backlight\n");
-               ret = PTR_ERR(bl);
-               goto out2;
+               return PTR_ERR(bl);
        }
 
        bl->props.brightness = ADP8860_MAX_BRIGHTNESS;
@@ -756,8 +751,6 @@ out:
                        &adp8860_bl_attr_group);
 out1:
        backlight_device_unregister(bl);
-out2:
-       kfree(data);
 
        return ret;
 }
@@ -776,7 +769,6 @@ static int __devexit adp8860_remove(struct i2c_client *client)
                        &adp8860_bl_attr_group);
 
        backlight_device_unregister(data->bl);
-       kfree(data);
 
        return 0;
 }
index 9be58c6f18f10d10c0f0ecff370c3d8e3cf0f3e3..edf7f91c8e612e7da24aa6d313cd67ff56cf9dbc 100644 (file)
@@ -244,8 +244,8 @@ static int __devinit adp8870_led_probe(struct i2c_client *client)
        struct led_info *cur_led;
        int ret, i;
 
-
-       led = kcalloc(pdata->num_leds, sizeof(*led), GFP_KERNEL);
+       led = devm_kzalloc(&client->dev, pdata->num_leds * sizeof(*led),
+                               GFP_KERNEL);
        if (led == NULL) {
                dev_err(&client->dev, "failed to alloc memory\n");
                return -ENOMEM;
@@ -253,17 +253,17 @@ static int __devinit adp8870_led_probe(struct i2c_client *client)
 
        ret = adp8870_write(client, ADP8870_ISCLAW, pdata->led_fade_law);
        if (ret)
-               goto err_free;
+               return ret;
 
        ret = adp8870_write(client, ADP8870_ISCT1,
                        (pdata->led_on_time & 0x3) << 6);
        if (ret)
-               goto err_free;
+               return ret;
 
        ret = adp8870_write(client, ADP8870_ISCF,
                        FADE_VAL(pdata->led_fade_in, pdata->led_fade_out));
        if (ret)
-               goto err_free;
+               return ret;
 
        for (i = 0; i < pdata->num_leds; ++i) {
                cur_led = &pdata->leds[i];
@@ -317,9 +317,6 @@ static int __devinit adp8870_led_probe(struct i2c_client *client)
                cancel_work_sync(&led[i].work);
        }
 
- err_free:
-       kfree(led);
-
        return ret;
 }
 
@@ -335,7 +332,6 @@ static int __devexit adp8870_led_remove(struct i2c_client *client)
                cancel_work_sync(&data->led[i].work);
        }
 
-       kfree(data->led);
        return 0;
 }
 #else
@@ -572,7 +568,7 @@ static ssize_t adp8870_store(struct device *dev, const char *buf,
        unsigned long val;
        int ret;
 
-       ret = strict_strtoul(buf, 10, &val);
+       ret = kstrtoul(buf, 10, &val);
        if (ret)
                return ret;
 
@@ -652,7 +648,7 @@ static ssize_t adp8870_bl_l1_daylight_max_store(struct device *dev,
                struct device_attribute *attr, const char *buf, size_t count)
 {
        struct adp8870_bl *data = dev_get_drvdata(dev);
-       int ret = strict_strtoul(buf, 10, &data->cached_daylight_max);
+       int ret = kstrtoul(buf, 10, &data->cached_daylight_max);
        if (ret)
                return ret;
 
@@ -794,7 +790,7 @@ static ssize_t adp8870_bl_ambient_light_zone_store(struct device *dev,
        uint8_t reg_val;
        int ret;
 
-       ret = strict_strtoul(buf, 10, &val);
+       ret = kstrtoul(buf, 10, &val);
        if (ret)
                return ret;
 
@@ -874,7 +870,7 @@ static int __devinit adp8870_probe(struct i2c_client *client,
                return -ENODEV;
        }
 
-       data = kzalloc(sizeof(*data), GFP_KERNEL);
+       data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
        if (data == NULL)
                return -ENOMEM;
 
@@ -894,8 +890,7 @@ static int __devinit adp8870_probe(struct i2c_client *client,
                        &client->dev, data, &adp8870_bl_ops, &props);
        if (IS_ERR(bl)) {
                dev_err(&client->dev, "failed to register backlight\n");
-               ret = PTR_ERR(bl);
-               goto out2;
+               return PTR_ERR(bl);
        }
 
        data->bl = bl;
@@ -930,8 +925,6 @@ out:
                        &adp8870_bl_attr_group);
 out1:
        backlight_device_unregister(bl);
-out2:
-       kfree(data);
 
        return ret;
 }
@@ -950,7 +943,6 @@ static int __devexit adp8870_remove(struct i2c_client *client)
                        &adp8870_bl_attr_group);
 
        backlight_device_unregister(data->bl);
-       kfree(data);
 
        return 0;
 }
index 7bdadc790117c977e5b23727eb70dac4fac51309..3729238e70963d7e2d6d7e25ec79cf5a5f7288db 100644 (file)
@@ -482,7 +482,7 @@ static int __devinit ams369fg06_probe(struct spi_device *spi)
        struct backlight_device *bd = NULL;
        struct backlight_properties props;
 
-       lcd = kzalloc(sizeof(struct ams369fg06), GFP_KERNEL);
+       lcd = devm_kzalloc(&spi->dev, sizeof(struct ams369fg06), GFP_KERNEL);
        if (!lcd)
                return -ENOMEM;
 
@@ -492,7 +492,7 @@ static int __devinit ams369fg06_probe(struct spi_device *spi)
        ret = spi_setup(spi);
        if (ret < 0) {
                dev_err(&spi->dev, "spi setup failed.\n");
-               goto out_free_lcd;
+               return ret;
        }
 
        lcd->spi = spi;
@@ -501,15 +501,13 @@ static int __devinit ams369fg06_probe(struct spi_device *spi)
        lcd->lcd_pd = spi->dev.platform_data;
        if (!lcd->lcd_pd) {
                dev_err(&spi->dev, "platform data is NULL\n");
-               goto out_free_lcd;
+               return -EFAULT;
        }
 
        ld = lcd_device_register("ams369fg06", &spi->dev, lcd,
                &ams369fg06_lcd_ops);
-       if (IS_ERR(ld)) {
-               ret = PTR_ERR(ld);
-               goto out_free_lcd;
-       }
+       if (IS_ERR(ld))
+               return PTR_ERR(ld);
 
        lcd->ld = ld;
 
@@ -547,8 +545,6 @@ static int __devinit ams369fg06_probe(struct spi_device *spi)
 
 out_lcd_unregister:
        lcd_device_unregister(ld);
-out_free_lcd:
-       kfree(lcd);
        return ret;
 }
 
@@ -559,7 +555,6 @@ static int __devexit ams369fg06_remove(struct spi_device *spi)
        ams369fg06_power(lcd, FB_BLANK_POWERDOWN);
        backlight_device_unregister(lcd->bd);
        lcd_device_unregister(lcd->ld);
-       kfree(lcd);
 
        return 0;
 }
@@ -619,7 +614,6 @@ static void ams369fg06_shutdown(struct spi_device *spi)
 static struct spi_driver ams369fg06_driver = {
        .driver = {
                .name   = "ams369fg06",
-               .bus    = &spi_bus_type,
                .owner  = THIS_MODULE,
        },
        .probe          = ams369fg06_probe,
index a523b255e124cc3ff44e4b39d15c9ba4a6f5761a..9dc73ac3709a63b912ffce6c8eaf08067db321a4 100644 (file)
@@ -16,6 +16,8 @@
  *  get at the firmware code in order to figure out what it's actually doing.
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
@@ -25,6 +27,7 @@
 #include <linux/pci.h>
 #include <linux/acpi.h>
 #include <linux/atomic.h>
+#include <linux/apple_bl.h>
 
 static struct backlight_device *apple_backlight_device;
 
@@ -39,8 +42,6 @@ struct hw_data {
 
 static const struct hw_data *hw_data;
 
-#define DRIVER "apple_backlight: "
-
 /* Module parameters. */
 static int debug;
 module_param_named(debug, debug, int, 0644);
@@ -60,8 +61,7 @@ static int intel_chipset_send_intensity(struct backlight_device *bd)
        int intensity = bd->props.brightness;
 
        if (debug)
-               printk(KERN_DEBUG DRIVER "setting brightness to %d\n",
-                      intensity);
+               pr_debug("setting brightness to %d\n", intensity);
 
        intel_chipset_set_brightness(intensity);
        return 0;
@@ -76,8 +76,7 @@ static int intel_chipset_get_intensity(struct backlight_device *bd)
        intensity = inb(0xb3) >> 4;
 
        if (debug)
-               printk(KERN_DEBUG DRIVER "read brightness of %d\n",
-                      intensity);
+               pr_debug("read brightness of %d\n", intensity);
 
        return intensity;
 }
@@ -107,8 +106,7 @@ static int nvidia_chipset_send_intensity(struct backlight_device *bd)
        int intensity = bd->props.brightness;
 
        if (debug)
-               printk(KERN_DEBUG DRIVER "setting brightness to %d\n",
-                      intensity);
+               pr_debug("setting brightness to %d\n", intensity);
 
        nvidia_chipset_set_brightness(intensity);
        return 0;
@@ -123,8 +121,7 @@ static int nvidia_chipset_get_intensity(struct backlight_device *bd)
        intensity = inb(0x52f) >> 4;
 
        if (debug)
-               printk(KERN_DEBUG DRIVER "read brightness of %d\n",
-                      intensity);
+               pr_debug("read brightness of %d\n", intensity);
 
        return intensity;
 }
@@ -149,7 +146,7 @@ static int __devinit apple_bl_add(struct acpi_device *dev)
        host = pci_get_bus_and_slot(0, 0);
 
        if (!host) {
-               printk(KERN_ERR DRIVER "unable to find PCI host\n");
+               pr_err("unable to find PCI host\n");
                return -ENODEV;
        }
 
@@ -161,7 +158,7 @@ static int __devinit apple_bl_add(struct acpi_device *dev)
        pci_dev_put(host);
 
        if (!hw_data) {
-               printk(KERN_ERR DRIVER "unknown hardware\n");
+               pr_err("unknown hardware\n");
                return -ENODEV;
        }
 
index bf5b1ece71605d701516f599af7ef1ce18751a11..297db2fa91f58e556873a114b812c214e650dca8 100644 (file)
@@ -5,6 +5,8 @@
  *
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/device.h>
@@ -123,7 +125,7 @@ static ssize_t backlight_store_power(struct device *dev,
        rc = -ENXIO;
        mutex_lock(&bd->ops_lock);
        if (bd->ops) {
-               pr_debug("backlight: set power to %lu\n", power);
+               pr_debug("set power to %lu\n", power);
                if (bd->props.power != power) {
                        bd->props.power = power;
                        backlight_update_status(bd);
@@ -161,8 +163,7 @@ static ssize_t backlight_store_brightness(struct device *dev,
                if (brightness > bd->props.max_brightness)
                        rc = -EINVAL;
                else {
-                       pr_debug("backlight: set brightness to %lu\n",
-                                brightness);
+                       pr_debug("set brightness to %lu\n", brightness);
                        bd->props.brightness = brightness;
                        backlight_update_status(bd);
                        rc = count;
@@ -378,8 +379,8 @@ static int __init backlight_class_init(void)
 {
        backlight_class = class_create(THIS_MODULE, "backlight");
        if (IS_ERR(backlight_class)) {
-               printk(KERN_WARNING "Unable to create backlight class; errno = %ld\n",
-                               PTR_ERR(backlight_class));
+               pr_warn("Unable to create backlight class; errno = %ld\n",
+                       PTR_ERR(backlight_class));
                return PTR_ERR(backlight_class);
        }
 
index 6dab13fe562ee8ed2a43fd42370b68eb81d76a15..23d732677ba177e6594818dbc98bf17fb5ada229 100644 (file)
@@ -544,7 +544,7 @@ static int __devinit corgi_lcd_probe(struct spi_device *spi)
                return -EINVAL;
        }
 
-       lcd = kzalloc(sizeof(struct corgi_lcd), GFP_KERNEL);
+       lcd = devm_kzalloc(&spi->dev, sizeof(struct corgi_lcd), GFP_KERNEL);
        if (!lcd) {
                dev_err(&spi->dev, "failed to allocate memory\n");
                return -ENOMEM;
@@ -554,10 +554,9 @@ static int __devinit corgi_lcd_probe(struct spi_device *spi)
 
        lcd->lcd_dev = lcd_device_register("corgi_lcd", &spi->dev,
                                        lcd, &corgi_lcd_ops);
-       if (IS_ERR(lcd->lcd_dev)) {
-               ret = PTR_ERR(lcd->lcd_dev);
-               goto err_free_lcd;
-       }
+       if (IS_ERR(lcd->lcd_dev))
+               return PTR_ERR(lcd->lcd_dev);
+
        lcd->power = FB_BLANK_POWERDOWN;
        lcd->mode = (pdata) ? pdata->init_mode : CORGI_LCD_MODE_VGA;
 
@@ -591,8 +590,6 @@ err_unregister_bl:
        backlight_device_unregister(lcd->bl_dev);
 err_unregister_lcd:
        lcd_device_unregister(lcd->lcd_dev);
-err_free_lcd:
-       kfree(lcd);
        return ret;
 }
 
@@ -613,7 +610,6 @@ static int __devexit corgi_lcd_remove(struct spi_device *spi)
 
        corgi_lcd_set_power(lcd->lcd_dev, FB_BLANK_POWERDOWN);
        lcd_device_unregister(lcd->lcd_dev);
-       kfree(lcd);
 
        return 0;
 }
index 22489eb5f3e0beb525c3965df1d1186c1ee914ca..37bae801e23bfa5e9c8085ef6bae47648730ac14 100644 (file)
@@ -27,6 +27,8 @@
  *   Alan Hourihane <alanh-at-tungstengraphics-dot-com>
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
@@ -180,14 +182,13 @@ static int cr_backlight_probe(struct platform_device *pdev)
        lpc_dev = pci_get_device(PCI_VENDOR_ID_INTEL,
                                        CRVML_DEVICE_LPC, NULL);
        if (!lpc_dev) {
-               printk("INTEL CARILLO RANCH LPC not found.\n");
+               pr_err("INTEL CARILLO RANCH LPC not found.\n");
                return -ENODEV;
        }
 
        pci_read_config_byte(lpc_dev, CRVML_REG_GPIOEN, &dev_en);
        if (!(dev_en & CRVML_GPIOEN_BIT)) {
-               printk(KERN_ERR
-                      "Carillo Ranch GPIO device was not enabled.\n");
+               pr_err("Carillo Ranch GPIO device was not enabled.\n");
                pci_dev_put(lpc_dev);
                return -ENODEV;
        }
@@ -270,7 +271,7 @@ static int __init cr_backlight_init(void)
                return PTR_ERR(crp);
        }
 
-       printk("Carillo Ranch Backlight Driver Initialized.\n");
+       pr_info("Carillo Ranch Backlight Driver Initialized.\n");
 
        return 0;
 }
index 30e19681a30b452a9c06181b83d2f448c10c4db1..573c7ece0fde88f7b1990bd3989f17ccd8a86ed9 100644 (file)
@@ -136,6 +136,7 @@ static int da903x_backlight_probe(struct platform_device *pdev)
                da903x_write(data->da903x_dev, DA9034_WLED_CONTROL2,
                                DA9034_WLED_ISET(pdata->output_current));
 
+       memset(&props, 0, sizeof(props));
        props.type = BACKLIGHT_RAW;
        props.max_brightness = max_brightness;
        bl = backlight_device_register(pdev->name, data->da903x_dev, data,
index 9ce6170c186079414dcb62123100e7786b032721..8c660fcd250da09f446e07f9e0dfce3b5677a128 100644 (file)
@@ -9,6 +9,8 @@
  *
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
@@ -106,7 +108,7 @@ static int genericbl_probe(struct platform_device *pdev)
 
        generic_backlight_device = bd;
 
-       printk("Generic Backlight Driver Initialized.\n");
+       pr_info("Generic Backlight Driver Initialized.\n");
        return 0;
 }
 
@@ -120,7 +122,7 @@ static int genericbl_remove(struct platform_device *pdev)
 
        backlight_device_unregister(bd);
 
-       printk("Generic Backlight Driver Unloaded\n");
+       pr_info("Generic Backlight Driver Unloaded\n");
        return 0;
 }
 
index 5118a9f029aba5a872c231239d9f86c52738b73c..6c9399341bcf4aefcac58ffacae39f6b83ac5247 100644 (file)
@@ -220,7 +220,7 @@ int __devinit ili9320_probe_spi(struct spi_device *spi,
 
        /* allocate and initialse our state */
 
-       ili = kzalloc(sizeof(struct ili9320), GFP_KERNEL);
+       ili = devm_kzalloc(&spi->dev, sizeof(struct ili9320), GFP_KERNEL);
        if (ili == NULL) {
                dev_err(dev, "no memory for device\n");
                return -ENOMEM;
@@ -240,8 +240,7 @@ int __devinit ili9320_probe_spi(struct spi_device *spi,
        lcd = lcd_device_register("ili9320", dev, ili, &ili9320_ops);
        if (IS_ERR(lcd)) {
                dev_err(dev, "failed to register lcd device\n");
-               ret = PTR_ERR(lcd);
-               goto err_free;
+               return PTR_ERR(lcd);
        }
 
        ili->lcd = lcd;
@@ -259,9 +258,6 @@ int __devinit ili9320_probe_spi(struct spi_device *spi,
  err_unregister:
        lcd_device_unregister(lcd);
 
- err_free:
-       kfree(ili);
-
        return ret;
 }
 
@@ -272,7 +268,6 @@ int __devexit ili9320_remove(struct ili9320 *ili)
        ili9320_power(ili, FB_BLANK_POWERDOWN);
 
        lcd_device_unregister(ili->lcd);
-       kfree(ili);
 
        return 0;
 }
index 2f8af5d786abbb5971aed6cad8c153229a4b4b84..16f593b64427a606093f1c14dce6498ab791a5cb 100644 (file)
@@ -9,6 +9,8 @@
  *
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/backlight.h>
 #include <linux/device.h>
 #include <linux/fb.h>
@@ -38,7 +40,7 @@ static int jornada_bl_get_brightness(struct backlight_device *bd)
        ret = jornada_ssp_byte(GETBRIGHTNESS);
 
        if (jornada_ssp_byte(GETBRIGHTNESS) != TXDUMMY) {
-               printk(KERN_ERR "bl : get brightness timeout\n");
+               pr_err("get brightness timeout\n");
                jornada_ssp_end();
                return -ETIMEDOUT;
        } else /* exchange txdummy for value */
@@ -59,7 +61,7 @@ static int jornada_bl_update_status(struct backlight_device *bd)
        if ((bd->props.power != FB_BLANK_UNBLANK) || (bd->props.fb_blank != FB_BLANK_UNBLANK)) {
                ret = jornada_ssp_byte(BRIGHTNESSOFF);
                if (ret != TXDUMMY) {
-                       printk(KERN_INFO "bl : brightness off timeout\n");
+                       pr_info("brightness off timeout\n");
                        /* turn off backlight */
                        PPSR &= ~PPC_LDD1;
                        PPDR |= PPC_LDD1;
@@ -70,7 +72,7 @@ static int jornada_bl_update_status(struct backlight_device *bd)
 
                /* send command to our mcu */
                if (jornada_ssp_byte(SETBRIGHTNESS) != TXDUMMY) {
-                       printk(KERN_INFO "bl : failed to set brightness\n");
+                       pr_info("failed to set brightness\n");
                        ret = -ETIMEDOUT;
                        goto out;
                }
@@ -81,7 +83,7 @@ static int jornada_bl_update_status(struct backlight_device *bd)
                   but due to physical layout it is equal to 0, so we simply
                   invert the value (MAX VALUE - NEW VALUE). */
                if (jornada_ssp_byte(BL_MAX_BRIGHT - bd->props.brightness) != TXDUMMY) {
-                       printk(KERN_ERR "bl : set brightness failed\n");
+                       pr_err("set brightness failed\n");
                        ret = -ETIMEDOUT;
                }
 
@@ -113,7 +115,7 @@ static int jornada_bl_probe(struct platform_device *pdev)
 
        if (IS_ERR(bd)) {
                ret = PTR_ERR(bd);
-               printk(KERN_ERR "bl : failed to register device, err=%x\n", ret);
+               pr_err("failed to register device, err=%x\n", ret);
                return ret;
        }
 
@@ -125,7 +127,7 @@ static int jornada_bl_probe(struct platform_device *pdev)
        jornada_bl_update_status(bd);
 
        platform_set_drvdata(pdev, bd);
-       printk(KERN_INFO "HP Jornada 700 series backlight driver\n");
+       pr_info("HP Jornada 700 series backlight driver\n");
 
        return 0;
 }
index 22d231a17e3c4649652ec1992567111e5b70cd6c..635b30523fd598556f674ff31c8d46031475718f 100644 (file)
@@ -9,6 +9,8 @@
  *
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/device.h>
 #include <linux/fb.h>
 #include <linux/kernel.h>
@@ -44,7 +46,7 @@ static int jornada_lcd_get_contrast(struct lcd_device *dev)
        jornada_ssp_start();
 
        if (jornada_ssp_byte(GETCONTRAST) != TXDUMMY) {
-               printk(KERN_ERR "lcd: get contrast failed\n");
+               pr_err("get contrast failed\n");
                jornada_ssp_end();
                return -ETIMEDOUT;
        } else {
@@ -65,7 +67,7 @@ static int jornada_lcd_set_contrast(struct lcd_device *dev, int value)
 
        /* push the new value */
        if (jornada_ssp_byte(value) != TXDUMMY) {
-               printk(KERN_ERR "lcd : set contrast failed\n");
+               pr_err("set contrast failed\n");
                jornada_ssp_end();
                return -ETIMEDOUT;
        }
@@ -103,7 +105,7 @@ static int jornada_lcd_probe(struct platform_device *pdev)
 
        if (IS_ERR(lcd_device)) {
                ret = PTR_ERR(lcd_device);
-               printk(KERN_ERR "lcd : failed to register device\n");
+               pr_err("failed to register device\n");
                return ret;
        }
 
index 6022b67285ecd63e9bb799e082bb00a4e445ff52..40f606a860934a7bb21fab7fb60b457a390e1c2a 100644 (file)
@@ -11,6 +11,8 @@
  * published by the Free Software Foundation.
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/device.h>
 #include <linux/kernel.h>
 #include <linux/delay.h>
@@ -159,7 +161,8 @@ static int __devinit l4f00242t03_probe(struct spi_device *spi)
                return -EINVAL;
        }
 
-       priv = kzalloc(sizeof(struct l4f00242t03_priv), GFP_KERNEL);
+       priv = devm_kzalloc(&spi->dev, sizeof(struct l4f00242t03_priv),
+                               GFP_KERNEL);
 
        if (priv == NULL) {
                dev_err(&spi->dev, "No memory for this device.\n");
@@ -177,7 +180,7 @@ static int __devinit l4f00242t03_probe(struct spi_device *spi)
        if (ret) {
                dev_err(&spi->dev,
                        "Unable to get the lcd l4f00242t03 reset gpio.\n");
-               goto err;
+               return ret;
        }
 
        ret = gpio_request_one(pdata->data_enable_gpio, GPIOF_OUT_INIT_LOW,
@@ -185,7 +188,7 @@ static int __devinit l4f00242t03_probe(struct spi_device *spi)
        if (ret) {
                dev_err(&spi->dev,
                        "Unable to get the lcd l4f00242t03 data en gpio.\n");
-               goto err2;
+               goto err;
        }
 
        priv->io_reg = regulator_get(&spi->dev, "vdd");
@@ -193,7 +196,7 @@ static int __devinit l4f00242t03_probe(struct spi_device *spi)
                ret = PTR_ERR(priv->io_reg);
                dev_err(&spi->dev, "%s: Unable to get the IO regulator\n",
                       __func__);
-               goto err3;
+               goto err2;
        }
 
        priv->core_reg = regulator_get(&spi->dev, "vcore");
@@ -201,14 +204,14 @@ static int __devinit l4f00242t03_probe(struct spi_device *spi)
                ret = PTR_ERR(priv->core_reg);
                dev_err(&spi->dev, "%s: Unable to get the core regulator\n",
                       __func__);
-               goto err4;
+               goto err3;
        }
 
        priv->ld = lcd_device_register("l4f00242t03",
                                        &spi->dev, priv, &l4f_ops);
        if (IS_ERR(priv->ld)) {
                ret = PTR_ERR(priv->ld);
-               goto err5;
+               goto err4;
        }
 
        /* Init the LCD */
@@ -220,16 +223,14 @@ static int __devinit l4f00242t03_probe(struct spi_device *spi)
 
        return 0;
 
-err5:
-       regulator_put(priv->core_reg);
 err4:
-       regulator_put(priv->io_reg);
+       regulator_put(priv->core_reg);
 err3:
-       gpio_free(pdata->data_enable_gpio);
+       regulator_put(priv->io_reg);
 err2:
-       gpio_free(pdata->reset_gpio);
+       gpio_free(pdata->data_enable_gpio);
 err:
-       kfree(priv);
+       gpio_free(pdata->reset_gpio);
 
        return ret;
 }
@@ -250,8 +251,6 @@ static int __devexit l4f00242t03_remove(struct spi_device *spi)
        regulator_put(priv->io_reg);
        regulator_put(priv->core_reg);
 
-       kfree(priv);
-
        return 0;
 }
 
index 79c1b0d609a809e189f43515ee20f648a7d2e8fa..a5d0d024bb92939ec9cdefbd6788febabebfb337 100644 (file)
@@ -5,6 +5,8 @@
  *
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/device.h>
@@ -32,6 +34,8 @@ static int fb_notifier_callback(struct notifier_block *self,
        case FB_EVENT_BLANK:
        case FB_EVENT_MODE_CHANGE:
        case FB_EVENT_MODE_CHANGE_ALL:
+       case FB_EARLY_EVENT_BLANK:
+       case FB_R_EARLY_EVENT_BLANK:
                break;
        default:
                return 0;
@@ -46,6 +50,14 @@ static int fb_notifier_callback(struct notifier_block *self,
                if (event == FB_EVENT_BLANK) {
                        if (ld->ops->set_power)
                                ld->ops->set_power(ld, *(int *)evdata->data);
+               } else if (event == FB_EARLY_EVENT_BLANK) {
+                       if (ld->ops->early_set_power)
+                               ld->ops->early_set_power(ld,
+                                               *(int *)evdata->data);
+               } else if (event == FB_R_EARLY_EVENT_BLANK) {
+                       if (ld->ops->r_early_set_power)
+                               ld->ops->r_early_set_power(ld,
+                                               *(int *)evdata->data);
                } else {
                        if (ld->ops->set_mode)
                                ld->ops->set_mode(ld, evdata->data);
@@ -106,7 +118,7 @@ static ssize_t lcd_store_power(struct device *dev,
 
        mutex_lock(&ld->ops_lock);
        if (ld->ops && ld->ops->set_power) {
-               pr_debug("lcd: set power to %lu\n", power);
+               pr_debug("set power to %lu\n", power);
                ld->ops->set_power(ld, power);
                rc = count;
        }
@@ -142,7 +154,7 @@ static ssize_t lcd_store_contrast(struct device *dev,
 
        mutex_lock(&ld->ops_lock);
        if (ld->ops && ld->ops->set_contrast) {
-               pr_debug("lcd: set contrast to %lu\n", contrast);
+               pr_debug("set contrast to %lu\n", contrast);
                ld->ops->set_contrast(ld, contrast);
                rc = count;
        }
@@ -253,8 +265,8 @@ static int __init lcd_class_init(void)
 {
        lcd_class = class_create(THIS_MODULE, "lcd");
        if (IS_ERR(lcd_class)) {
-               printk(KERN_WARNING "Unable to create backlight class; errno = %ld\n",
-                               PTR_ERR(lcd_class));
+               pr_warn("Unable to create backlight class; errno = %ld\n",
+                       PTR_ERR(lcd_class));
                return PTR_ERR(lcd_class);
        }
 
index efd352be21ae44a553cf8735392261d90552ff19..58f517fb7d40fa2ebd1035b5bf92371f0fd50b0c 100644 (file)
@@ -707,7 +707,7 @@ static int ld9040_probe(struct spi_device *spi)
        struct backlight_device *bd = NULL;
        struct backlight_properties props;
 
-       lcd = kzalloc(sizeof(struct ld9040), GFP_KERNEL);
+       lcd = devm_kzalloc(&spi->dev, sizeof(struct ld9040), GFP_KERNEL);
        if (!lcd)
                return -ENOMEM;
 
@@ -717,7 +717,7 @@ static int ld9040_probe(struct spi_device *spi)
        ret = spi_setup(spi);
        if (ret < 0) {
                dev_err(&spi->dev, "spi setup failed.\n");
-               goto out_free_lcd;
+               return ret;
        }
 
        lcd->spi = spi;
@@ -726,7 +726,7 @@ static int ld9040_probe(struct spi_device *spi)
        lcd->lcd_pd = spi->dev.platform_data;
        if (!lcd->lcd_pd) {
                dev_err(&spi->dev, "platform data is NULL.\n");
-               goto out_free_lcd;
+               return -EFAULT;
        }
 
        mutex_init(&lcd->lock);
@@ -734,13 +734,13 @@ static int ld9040_probe(struct spi_device *spi)
        ret = regulator_bulk_get(lcd->dev, ARRAY_SIZE(supplies), supplies);
        if (ret) {
                dev_err(lcd->dev, "Failed to get regulators: %d\n", ret);
-               goto out_free_lcd;
+               return ret;
        }
 
        ld = lcd_device_register("ld9040", &spi->dev, lcd, &ld9040_lcd_ops);
        if (IS_ERR(ld)) {
                ret = PTR_ERR(ld);
-               goto out_free_lcd;
+               goto out_free_regulator;
        }
 
        lcd->ld = ld;
@@ -782,10 +782,9 @@ static int ld9040_probe(struct spi_device *spi)
 
 out_unregister_lcd:
        lcd_device_unregister(lcd->ld);
-out_free_lcd:
+out_free_regulator:
        regulator_bulk_free(ARRAY_SIZE(supplies), supplies);
 
-       kfree(lcd);
        return ret;
 }
 
@@ -797,7 +796,6 @@ static int __devexit ld9040_remove(struct spi_device *spi)
        backlight_device_unregister(lcd->bd);
        lcd_device_unregister(lcd->ld);
        regulator_bulk_free(ARRAY_SIZE(supplies), supplies);
-       kfree(lcd);
 
        return 0;
 }
@@ -846,7 +844,6 @@ static void ld9040_shutdown(struct spi_device *spi)
 static struct spi_driver ld9040_driver = {
        .driver = {
                .name   = "ld9040",
-               .bus    = &spi_bus_type,
                .owner  = THIS_MODULE,
        },
        .probe          = ld9040_probe,
diff --git a/drivers/video/backlight/lm3533_bl.c b/drivers/video/backlight/lm3533_bl.c
new file mode 100644 (file)
index 0000000..bebeb63
--- /dev/null
@@ -0,0 +1,423 @@
+/*
+ * lm3533-bl.c -- LM3533 Backlight driver
+ *
+ * Copyright (C) 2011-2012 Texas Instruments
+ *
+ * Author: Johan Hovold <jhovold@gmail.com>
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/backlight.h>
+#include <linux/fb.h>
+#include <linux/slab.h>
+
+#include <linux/mfd/lm3533.h>
+
+
+#define LM3533_HVCTRLBANK_COUNT                2
+#define LM3533_BL_MAX_BRIGHTNESS       255
+
+#define LM3533_REG_CTRLBANK_AB_BCONF   0x1a
+
+
+struct lm3533_bl {
+       struct lm3533 *lm3533;
+       struct lm3533_ctrlbank cb;
+       struct backlight_device *bd;
+       int id;
+};
+
+
+static inline int lm3533_bl_get_ctrlbank_id(struct lm3533_bl *bl)
+{
+       return bl->id;
+}
+
+static int lm3533_bl_update_status(struct backlight_device *bd)
+{
+       struct lm3533_bl *bl = bl_get_data(bd);
+       int brightness = bd->props.brightness;
+
+       if (bd->props.power != FB_BLANK_UNBLANK)
+               brightness = 0;
+       if (bd->props.fb_blank != FB_BLANK_UNBLANK)
+               brightness = 0;
+
+       return lm3533_ctrlbank_set_brightness(&bl->cb, (u8)brightness);
+}
+
+static int lm3533_bl_get_brightness(struct backlight_device *bd)
+{
+       struct lm3533_bl *bl = bl_get_data(bd);
+       u8 val;
+       int ret;
+
+       ret = lm3533_ctrlbank_get_brightness(&bl->cb, &val);
+       if (ret)
+               return ret;
+
+       return val;
+}
+
+static const struct backlight_ops lm3533_bl_ops = {
+       .get_brightness = lm3533_bl_get_brightness,
+       .update_status  = lm3533_bl_update_status,
+};
+
+static ssize_t show_id(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       struct lm3533_bl *bl = dev_get_drvdata(dev);
+
+       return scnprintf(buf, PAGE_SIZE, "%d\n", bl->id);
+}
+
+static ssize_t show_als_channel(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       struct lm3533_bl *bl = dev_get_drvdata(dev);
+       unsigned channel = lm3533_bl_get_ctrlbank_id(bl);
+
+       return scnprintf(buf, PAGE_SIZE, "%u\n", channel);
+}
+
+static ssize_t show_als_en(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       struct lm3533_bl *bl = dev_get_drvdata(dev);
+       int ctrlbank = lm3533_bl_get_ctrlbank_id(bl);
+       u8 val;
+       u8 mask;
+       bool enable;
+       int ret;
+
+       ret = lm3533_read(bl->lm3533, LM3533_REG_CTRLBANK_AB_BCONF, &val);
+       if (ret)
+               return ret;
+
+       mask = 1 << (2 * ctrlbank);
+       enable = val & mask;
+
+       return scnprintf(buf, PAGE_SIZE, "%d\n", enable);
+}
+
+static ssize_t store_als_en(struct device *dev,
+                                       struct device_attribute *attr,
+                                       const char *buf, size_t len)
+{
+       struct lm3533_bl *bl = dev_get_drvdata(dev);
+       int ctrlbank = lm3533_bl_get_ctrlbank_id(bl);
+       int enable;
+       u8 val;
+       u8 mask;
+       int ret;
+
+       if (kstrtoint(buf, 0, &enable))
+               return -EINVAL;
+
+       mask = 1 << (2 * ctrlbank);
+
+       if (enable)
+               val = mask;
+       else
+               val = 0;
+
+       ret = lm3533_update(bl->lm3533, LM3533_REG_CTRLBANK_AB_BCONF, val,
+                                                                       mask);
+       if (ret)
+               return ret;
+
+       return len;
+}
+
+static ssize_t show_linear(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       struct lm3533_bl *bl = dev_get_drvdata(dev);
+       u8 val;
+       u8 mask;
+       int linear;
+       int ret;
+
+       ret = lm3533_read(bl->lm3533, LM3533_REG_CTRLBANK_AB_BCONF, &val);
+       if (ret)
+               return ret;
+
+       mask = 1 << (2 * lm3533_bl_get_ctrlbank_id(bl) + 1);
+
+       if (val & mask)
+               linear = 1;
+       else
+               linear = 0;
+
+       return scnprintf(buf, PAGE_SIZE, "%x\n", linear);
+}
+
+static ssize_t store_linear(struct device *dev,
+                                       struct device_attribute *attr,
+                                       const char *buf, size_t len)
+{
+       struct lm3533_bl *bl = dev_get_drvdata(dev);
+       unsigned long linear;
+       u8 mask;
+       u8 val;
+       int ret;
+
+       if (kstrtoul(buf, 0, &linear))
+               return -EINVAL;
+
+       mask = 1 << (2 * lm3533_bl_get_ctrlbank_id(bl) + 1);
+
+       if (linear)
+               val = mask;
+       else
+               val = 0;
+
+       ret = lm3533_update(bl->lm3533, LM3533_REG_CTRLBANK_AB_BCONF, val,
+                                                                       mask);
+       if (ret)
+               return ret;
+
+       return len;
+}
+
+static ssize_t show_pwm(struct device *dev,
+                                       struct device_attribute *attr,
+                                       char *buf)
+{
+       struct lm3533_bl *bl = dev_get_drvdata(dev);
+       u8 val;
+       int ret;
+
+       ret = lm3533_ctrlbank_get_pwm(&bl->cb, &val);
+       if (ret)
+               return ret;
+
+       return scnprintf(buf, PAGE_SIZE, "%u\n", val);
+}
+
+static ssize_t store_pwm(struct device *dev,
+                                       struct device_attribute *attr,
+                                       const char *buf, size_t len)
+{
+       struct lm3533_bl *bl = dev_get_drvdata(dev);
+       u8 val;
+       int ret;
+
+       if (kstrtou8(buf, 0, &val))
+               return -EINVAL;
+
+       ret = lm3533_ctrlbank_set_pwm(&bl->cb, val);
+       if (ret)
+               return ret;
+
+       return len;
+}
+
+static LM3533_ATTR_RO(als_channel);
+static LM3533_ATTR_RW(als_en);
+static LM3533_ATTR_RO(id);
+static LM3533_ATTR_RW(linear);
+static LM3533_ATTR_RW(pwm);
+
+static struct attribute *lm3533_bl_attributes[] = {
+       &dev_attr_als_channel.attr,
+       &dev_attr_als_en.attr,
+       &dev_attr_id.attr,
+       &dev_attr_linear.attr,
+       &dev_attr_pwm.attr,
+       NULL,
+};
+
+static umode_t lm3533_bl_attr_is_visible(struct kobject *kobj,
+                                            struct attribute *attr, int n)
+{
+       struct device *dev = container_of(kobj, struct device, kobj);
+       struct lm3533_bl *bl = dev_get_drvdata(dev);
+       umode_t mode = attr->mode;
+
+       if (attr == &dev_attr_als_channel.attr ||
+                                       attr == &dev_attr_als_en.attr) {
+               if (!bl->lm3533->have_als)
+                       mode = 0;
+       }
+
+       return mode;
+};
+
+static struct attribute_group lm3533_bl_attribute_group = {
+       .is_visible     = lm3533_bl_attr_is_visible,
+       .attrs          = lm3533_bl_attributes
+};
+
+static int __devinit lm3533_bl_setup(struct lm3533_bl *bl,
+                                       struct lm3533_bl_platform_data *pdata)
+{
+       int ret;
+
+       ret = lm3533_ctrlbank_set_max_current(&bl->cb, pdata->max_current);
+       if (ret)
+               return ret;
+
+       return lm3533_ctrlbank_set_pwm(&bl->cb, pdata->pwm);
+}
+
+static int __devinit lm3533_bl_probe(struct platform_device *pdev)
+{
+       struct lm3533 *lm3533;
+       struct lm3533_bl_platform_data *pdata;
+       struct lm3533_bl *bl;
+       struct backlight_device *bd;
+       struct backlight_properties props;
+       int ret;
+
+       dev_dbg(&pdev->dev, "%s\n", __func__);
+
+       lm3533 = dev_get_drvdata(pdev->dev.parent);
+       if (!lm3533)
+               return -EINVAL;
+
+       pdata = pdev->dev.platform_data;
+       if (!pdata) {
+               dev_err(&pdev->dev, "no platform data\n");
+               return -EINVAL;
+       }
+
+       if (pdev->id < 0 || pdev->id >= LM3533_HVCTRLBANK_COUNT) {
+               dev_err(&pdev->dev, "illegal backlight id %d\n", pdev->id);
+               return -EINVAL;
+       }
+
+       bl = kzalloc(sizeof(*bl), GFP_KERNEL);
+       if (!bl) {
+               dev_err(&pdev->dev,
+                               "failed to allocate memory for backlight\n");
+               return -ENOMEM;
+       }
+
+       bl->lm3533 = lm3533;
+       bl->id = pdev->id;
+
+       bl->cb.lm3533 = lm3533;
+       bl->cb.id = lm3533_bl_get_ctrlbank_id(bl);
+       bl->cb.dev = NULL;                      /* until registered */
+
+       memset(&props, 0, sizeof(props));
+       props.type = BACKLIGHT_RAW;
+       props.max_brightness = LM3533_BL_MAX_BRIGHTNESS;
+       props.brightness = pdata->default_brightness;
+       bd = backlight_device_register(pdata->name, pdev->dev.parent, bl,
+                                               &lm3533_bl_ops, &props);
+       if (IS_ERR(bd)) {
+               dev_err(&pdev->dev, "failed to register backlight device\n");
+               ret = PTR_ERR(bd);
+               goto err_free;
+       }
+
+       bl->bd = bd;
+       bl->cb.dev = &bl->bd->dev;
+
+       platform_set_drvdata(pdev, bl);
+
+       ret = sysfs_create_group(&bd->dev.kobj, &lm3533_bl_attribute_group);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "failed to create sysfs attributes\n");
+               goto err_unregister;
+       }
+
+       backlight_update_status(bd);
+
+       ret = lm3533_bl_setup(bl, pdata);
+       if (ret)
+               goto err_sysfs_remove;
+
+       ret = lm3533_ctrlbank_enable(&bl->cb);
+       if (ret)
+               goto err_sysfs_remove;
+
+       return 0;
+
+err_sysfs_remove:
+       sysfs_remove_group(&bd->dev.kobj, &lm3533_bl_attribute_group);
+err_unregister:
+       backlight_device_unregister(bd);
+err_free:
+       kfree(bl);
+
+       return ret;
+}
+
+static int __devexit lm3533_bl_remove(struct platform_device *pdev)
+{
+       struct lm3533_bl *bl = platform_get_drvdata(pdev);
+       struct backlight_device *bd = bl->bd;
+
+       dev_dbg(&bd->dev, "%s\n", __func__);
+
+       bd->props.power = FB_BLANK_POWERDOWN;
+       bd->props.brightness = 0;
+
+       lm3533_ctrlbank_disable(&bl->cb);
+       sysfs_remove_group(&bd->dev.kobj, &lm3533_bl_attribute_group);
+       backlight_device_unregister(bd);
+       kfree(bl);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int lm3533_bl_suspend(struct platform_device *pdev, pm_message_t state)
+{
+       struct lm3533_bl *bl = platform_get_drvdata(pdev);
+
+       dev_dbg(&pdev->dev, "%s\n", __func__);
+
+       return lm3533_ctrlbank_disable(&bl->cb);
+}
+
+static int lm3533_bl_resume(struct platform_device *pdev)
+{
+       struct lm3533_bl *bl = platform_get_drvdata(pdev);
+
+       dev_dbg(&pdev->dev, "%s\n", __func__);
+
+       return lm3533_ctrlbank_enable(&bl->cb);
+}
+#else
+#define lm3533_bl_suspend      NULL
+#define lm3533_bl_resume       NULL
+#endif
+
+static void lm3533_bl_shutdown(struct platform_device *pdev)
+{
+       struct lm3533_bl *bl = platform_get_drvdata(pdev);
+
+       dev_dbg(&pdev->dev, "%s\n", __func__);
+
+       lm3533_ctrlbank_disable(&bl->cb);
+}
+
+static struct platform_driver lm3533_bl_driver = {
+       .driver = {
+               .name   = "lm3533-backlight",
+               .owner  = THIS_MODULE,
+       },
+       .probe          = lm3533_bl_probe,
+       .remove         = __devexit_p(lm3533_bl_remove),
+       .shutdown       = lm3533_bl_shutdown,
+       .suspend        = lm3533_bl_suspend,
+       .resume         = lm3533_bl_resume,
+};
+module_platform_driver(lm3533_bl_driver);
+
+MODULE_AUTHOR("Johan Hovold <jhovold@gmail.com>");
+MODULE_DESCRIPTION("LM3533 Backlight driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:lm3533-backlight");
index 4161f9e3982a2a544120142f100e45afa34b5d2c..a9f2c36966f1ff60fcf0a8873c1d01f93017c500 100644 (file)
@@ -168,7 +168,8 @@ static int __devinit lms283gf05_probe(struct spi_device *spi)
                        goto err;
        }
 
-       st = kzalloc(sizeof(struct lms283gf05_state), GFP_KERNEL);
+       st = devm_kzalloc(&spi->dev, sizeof(struct lms283gf05_state),
+                               GFP_KERNEL);
        if (st == NULL) {
                dev_err(&spi->dev, "No memory for device state\n");
                ret = -ENOMEM;
@@ -178,7 +179,7 @@ static int __devinit lms283gf05_probe(struct spi_device *spi)
        ld = lcd_device_register("lms283gf05", &spi->dev, st, &lms_ops);
        if (IS_ERR(ld)) {
                ret = PTR_ERR(ld);
-               goto err2;
+               goto err;
        }
 
        st->spi = spi;
@@ -193,8 +194,6 @@ static int __devinit lms283gf05_probe(struct spi_device *spi)
 
        return 0;
 
-err2:
-       kfree(st);
 err:
        if (pdata != NULL)
                gpio_free(pdata->reset_gpio);
@@ -212,8 +211,6 @@ static int __devexit lms283gf05_remove(struct spi_device *spi)
        if (pdata != NULL)
                gpio_free(pdata->reset_gpio);
 
-       kfree(st);
-
        return 0;
 }
 
index 333949ff3265200f716112be2afaa79e5c480e43..6c0f1ac0d32a93d9f278ad21155d2ca9d81b22d9 100644 (file)
@@ -232,23 +232,20 @@ static int __devinit ltv350qv_probe(struct spi_device *spi)
        struct lcd_device *ld;
        int ret;
 
-       lcd = kzalloc(sizeof(struct ltv350qv), GFP_KERNEL);
+       lcd = devm_kzalloc(&spi->dev, sizeof(struct ltv350qv), GFP_KERNEL);
        if (!lcd)
                return -ENOMEM;
 
        lcd->spi = spi;
        lcd->power = FB_BLANK_POWERDOWN;
-       lcd->buffer = kzalloc(8, GFP_KERNEL);
-       if (!lcd->buffer) {
-               ret = -ENOMEM;
-               goto out_free_lcd;
-       }
+       lcd->buffer = devm_kzalloc(&spi->dev, 8, GFP_KERNEL);
+       if (!lcd->buffer)
+               return -ENOMEM;
 
        ld = lcd_device_register("ltv350qv", &spi->dev, lcd, &ltv_ops);
-       if (IS_ERR(ld)) {
-               ret = PTR_ERR(ld);
-               goto out_free_buffer;
-       }
+       if (IS_ERR(ld))
+               return PTR_ERR(ld);
+
        lcd->ld = ld;
 
        ret = ltv350qv_power(lcd, FB_BLANK_UNBLANK);
@@ -261,10 +258,6 @@ static int __devinit ltv350qv_probe(struct spi_device *spi)
 
 out_unregister:
        lcd_device_unregister(ld);
-out_free_buffer:
-       kfree(lcd->buffer);
-out_free_lcd:
-       kfree(lcd);
        return ret;
 }
 
@@ -274,8 +267,6 @@ static int __devexit ltv350qv_remove(struct spi_device *spi)
 
        ltv350qv_power(lcd, FB_BLANK_POWERDOWN);
        lcd_device_unregister(lcd->ld);
-       kfree(lcd->buffer);
-       kfree(lcd);
 
        return 0;
 }
@@ -310,7 +301,6 @@ static void ltv350qv_shutdown(struct spi_device *spi)
 static struct spi_driver ltv350qv_driver = {
        .driver = {
                .name           = "ltv350qv",
-               .bus            = &spi_bus_type,
                .owner          = THIS_MODULE,
        },
 
index 0175bfb08a1ca13ef14412cd1e49d1bb033430d3..bfdc5fbeaa116aa7e919c191eac4dcac2f3985bc 100644 (file)
@@ -18,6 +18,8 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
@@ -168,7 +170,7 @@ static int omapbl_probe(struct platform_device *pdev)
        dev->props.brightness = pdata->default_intensity;
        omapbl_update_status(dev);
 
-       printk(KERN_INFO "OMAP LCD backlight initialised\n");
+       pr_info("OMAP LCD backlight initialised\n");
 
        return 0;
 }
index c65853cb9740633ab9d1ff6b5ba7a659fac276e1..c092159f438344dab682d8a1410169b45524d2ad 100644 (file)
@@ -111,6 +111,7 @@ static int __devinit pcf50633_bl_probe(struct platform_device *pdev)
        if (!pcf_bl)
                return -ENOMEM;
 
+       memset(&bl_props, 0, sizeof(bl_props));
        bl_props.type = BACKLIGHT_RAW;
        bl_props.max_brightness = 0x3f;
        bl_props.power = FB_BLANK_UNBLANK;
index 6af183d6465ee80b4892c671fce687b85a6a272a..69b35f02929e470d6d384977b11b141ed1f08620 100644 (file)
@@ -15,6 +15,8 @@
  *
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
@@ -68,13 +70,13 @@ static int progearbl_probe(struct platform_device *pdev)
 
        pmu_dev = pci_get_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M7101, NULL);
        if (!pmu_dev) {
-               printk("ALI M7101 PMU not found.\n");
+               pr_err("ALI M7101 PMU not found.\n");
                return -ENODEV;
        }
 
        sb_dev = pci_get_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533, NULL);
        if (!sb_dev) {
-               printk("ALI 1533 SB not found.\n");
+               pr_err("ALI 1533 SB not found.\n");
                ret = -ENODEV;
                goto put_pmu;
        }
index e264f55b257426bd342b9943ee80530af760ce68..6437ae474cf2be4d9b29a16a683c9de51a21fc61 100644 (file)
@@ -741,7 +741,7 @@ static int __devinit s6e63m0_probe(struct spi_device *spi)
        struct backlight_device *bd = NULL;
        struct backlight_properties props;
 
-       lcd = kzalloc(sizeof(struct s6e63m0), GFP_KERNEL);
+       lcd = devm_kzalloc(&spi->dev, sizeof(struct s6e63m0), GFP_KERNEL);
        if (!lcd)
                return -ENOMEM;
 
@@ -751,7 +751,7 @@ static int __devinit s6e63m0_probe(struct spi_device *spi)
        ret = spi_setup(spi);
        if (ret < 0) {
                dev_err(&spi->dev, "spi setup failed.\n");
-               goto out_free_lcd;
+               return ret;
        }
 
        lcd->spi = spi;
@@ -760,14 +760,12 @@ static int __devinit s6e63m0_probe(struct spi_device *spi)
        lcd->lcd_pd = (struct lcd_platform_data *)spi->dev.platform_data;
        if (!lcd->lcd_pd) {
                dev_err(&spi->dev, "platform data is NULL.\n");
-               goto out_free_lcd;
+               return -EFAULT;
        }
 
        ld = lcd_device_register("s6e63m0", &spi->dev, lcd, &s6e63m0_lcd_ops);
-       if (IS_ERR(ld)) {
-               ret = PTR_ERR(ld);
-               goto out_free_lcd;
-       }
+       if (IS_ERR(ld))
+               return PTR_ERR(ld);
 
        lcd->ld = ld;
 
@@ -824,8 +822,6 @@ static int __devinit s6e63m0_probe(struct spi_device *spi)
 
 out_lcd_unregister:
        lcd_device_unregister(ld);
-out_free_lcd:
-       kfree(lcd);
        return ret;
 }
 
@@ -838,7 +834,6 @@ static int __devexit s6e63m0_remove(struct spi_device *spi)
        device_remove_file(&spi->dev, &dev_attr_gamma_mode);
        backlight_device_unregister(lcd->bd);
        lcd_device_unregister(lcd->ld);
-       kfree(lcd);
 
        return 0;
 }
@@ -899,7 +894,6 @@ static void s6e63m0_shutdown(struct spi_device *spi)
 static struct spi_driver s6e63m0_driver = {
        .driver = {
                .name   = "s6e63m0",
-               .bus    = &spi_bus_type,
                .owner  = THIS_MODULE,
        },
        .probe          = s6e63m0_probe,
index 2368b8e5f89e99e6248e4f3eab7f7eda27f58e13..02444d042cd53db2972dc9978d11e456665c9ffc 100644 (file)
@@ -349,7 +349,7 @@ static int __devinit tdo24m_probe(struct spi_device *spi)
        if (err)
                return err;
 
-       lcd = kzalloc(sizeof(struct tdo24m), GFP_KERNEL);
+       lcd = devm_kzalloc(&spi->dev, sizeof(struct tdo24m), GFP_KERNEL);
        if (!lcd)
                return -ENOMEM;
 
@@ -357,11 +357,9 @@ static int __devinit tdo24m_probe(struct spi_device *spi)
        lcd->power = FB_BLANK_POWERDOWN;
        lcd->mode = MODE_VGA;   /* default to VGA */
 
-       lcd->buf = kmalloc(TDO24M_SPI_BUFF_SIZE, GFP_KERNEL);
-       if (lcd->buf == NULL) {
-               kfree(lcd);
+       lcd->buf = devm_kzalloc(&spi->dev, TDO24M_SPI_BUFF_SIZE, GFP_KERNEL);
+       if (lcd->buf == NULL)
                return -ENOMEM;
-       }
 
        m = &lcd->msg;
        x = &lcd->xfer;
@@ -383,15 +381,13 @@ static int __devinit tdo24m_probe(struct spi_device *spi)
                break;
        default:
                dev_err(&spi->dev, "Unsupported model");
-               goto out_free;
+               return -EINVAL;
        }
 
        lcd->lcd_dev = lcd_device_register("tdo24m", &spi->dev,
                                        lcd, &tdo24m_ops);
-       if (IS_ERR(lcd->lcd_dev)) {
-               err = PTR_ERR(lcd->lcd_dev);
-               goto out_free;
-       }
+       if (IS_ERR(lcd->lcd_dev))
+               return PTR_ERR(lcd->lcd_dev);
 
        dev_set_drvdata(&spi->dev, lcd);
        err = tdo24m_power(lcd, FB_BLANK_UNBLANK);
@@ -402,9 +398,6 @@ static int __devinit tdo24m_probe(struct spi_device *spi)
 
 out_unregister:
        lcd_device_unregister(lcd->lcd_dev);
-out_free:
-       kfree(lcd->buf);
-       kfree(lcd);
        return err;
 }
 
@@ -414,8 +407,6 @@ static int __devexit tdo24m_remove(struct spi_device *spi)
 
        tdo24m_power(lcd, FB_BLANK_POWERDOWN);
        lcd_device_unregister(lcd->lcd_dev);
-       kfree(lcd->buf);
-       kfree(lcd);
 
        return 0;
 }
index 2b241abced43467378ceb3ea68e9633f78644684..0d54e607e82d1bd3f86196944d425a8dee40fdb2 100644 (file)
@@ -82,8 +82,11 @@ static int __devinit tosa_bl_probe(struct i2c_client *client,
                const struct i2c_device_id *id)
 {
        struct backlight_properties props;
-       struct tosa_bl_data *data = kzalloc(sizeof(struct tosa_bl_data), GFP_KERNEL);
+       struct tosa_bl_data *data;
        int ret = 0;
+
+       data = devm_kzalloc(&client->dev, sizeof(struct tosa_bl_data),
+                               GFP_KERNEL);
        if (!data)
                return -ENOMEM;
 
@@ -92,7 +95,7 @@ static int __devinit tosa_bl_probe(struct i2c_client *client,
        ret = gpio_request(TOSA_GPIO_BL_C20MA, "backlight");
        if (ret) {
                dev_dbg(&data->bl->dev, "Unable to request gpio!\n");
-               goto err_gpio_bl;
+               return ret;
        }
        ret = gpio_direction_output(TOSA_GPIO_BL_C20MA, 0);
        if (ret)
@@ -122,8 +125,6 @@ err_reg:
        data->bl = NULL;
 err_gpio_dir:
        gpio_free(TOSA_GPIO_BL_C20MA);
-err_gpio_bl:
-       kfree(data);
        return ret;
 }
 
@@ -136,8 +137,6 @@ static int __devexit tosa_bl_remove(struct i2c_client *client)
 
        gpio_free(TOSA_GPIO_BL_C20MA);
 
-       kfree(data);
-
        return 0;
 }
 
index 2231aec23918fec6c1f65125421d1d35c229c759..47823b8efff060b77f1ef4db5bf19554b08e73c7 100644 (file)
@@ -174,7 +174,8 @@ static int __devinit tosa_lcd_probe(struct spi_device *spi)
        int ret;
        struct tosa_lcd_data *data;
 
-       data = kzalloc(sizeof(struct tosa_lcd_data), GFP_KERNEL);
+       data = devm_kzalloc(&spi->dev, sizeof(struct tosa_lcd_data),
+                               GFP_KERNEL);
        if (!data)
                return -ENOMEM;
 
@@ -187,7 +188,7 @@ static int __devinit tosa_lcd_probe(struct spi_device *spi)
 
        ret = spi_setup(spi);
        if (ret < 0)
-               goto err_spi;
+               return ret;
 
        data->spi = spi;
        dev_set_drvdata(&spi->dev, data);
@@ -224,8 +225,6 @@ err_gpio_dir:
        gpio_free(TOSA_GPIO_TG_ON);
 err_gpio_tg:
        dev_set_drvdata(&spi->dev, NULL);
-err_spi:
-       kfree(data);
        return ret;
 }
 
@@ -242,7 +241,6 @@ static int __devexit tosa_lcd_remove(struct spi_device *spi)
 
        gpio_free(TOSA_GPIO_TG_ON);
        dev_set_drvdata(&spi->dev, NULL);
-       kfree(data);
 
        return 0;
 }
index 5d365deb5f8220a31a4e2e0562dbf861d9270f06..9e5517a3a52baeea6546e0d24dc4a8ca2e5ef732 100644 (file)
@@ -194,6 +194,7 @@ static int wm831x_backlight_probe(struct platform_device *pdev)
        data->current_brightness = 0;
        data->isink_reg = isink_reg;
 
+       memset(&props, 0, sizeof(props));
        props.type = BACKLIGHT_RAW;
        props.max_brightness = max_isel;
        bl = backlight_device_register("wm831x", &pdev->dev, data,
index c6ce416ab587776f72f629a1ffecbfcc3ecce693..0dff12a1daef26af52949a6ff52f3b5c9de61a61 100644 (file)
@@ -1046,20 +1046,29 @@ fb_set_var(struct fb_info *info, struct fb_var_screeninfo *var)
 int
 fb_blank(struct fb_info *info, int blank)
 {      
-       int ret = -EINVAL;
+       struct fb_event event;
+       int ret = -EINVAL, early_ret;
 
        if (blank > FB_BLANK_POWERDOWN)
                blank = FB_BLANK_POWERDOWN;
 
+       event.info = info;
+       event.data = &blank;
+
+       early_ret = fb_notifier_call_chain(FB_EARLY_EVENT_BLANK, &event);
+
        if (info->fbops->fb_blank)
                ret = info->fbops->fb_blank(blank, info);
 
-       if (!ret) {
-               struct fb_event event;
-
-               event.info = info;
-               event.data = &blank;
+       if (!ret)
                fb_notifier_call_chain(FB_EVENT_BLANK, &event);
+       else {
+               /*
+                * if fb_blank is failed then revert effects of
+                * the early blank event.
+                */
+               if (!early_ret)
+                       fb_notifier_call_chain(FB_R_EARLY_EVENT_BLANK, &event);
        }
 
        return ret;
index 31b8f67477b7957b8e193f757c4821e810b685a3..217678e0b983affe7ec7e1f1571c6a7811af5d5c 100644 (file)
@@ -1243,6 +1243,7 @@ static int maven_probe(struct i2c_client *client,
 
        if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WRITE_WORD_DATA |
                                              I2C_FUNC_SMBUS_BYTE_DATA |
+                                             I2C_FUNC_NOSTART |
                                              I2C_FUNC_PROTOCOL_MANGLING))
                goto ERROR0;
        if (!(data = kzalloc(sizeof(*data), GFP_KERNEL))) {
index d26f37ac69d87d882eb7a2a5d539a7e90e55c4fa..74e7cf078505650498387f4c7e43292419fcf60c 100644 (file)
@@ -532,6 +532,7 @@ static int acx_panel_probe(struct omap_dss_device *dssdev)
 
        /*------- Backlight control --------*/
 
+       memset(&props, 0, sizeof(props));
        props.fb_blank = FB_BLANK_UNBLANK;
        props.power = FB_BLANK_UNBLANK;
        props.type = BACKLIGHT_RAW;
index a18bf6358eb89d5048ec8490985526bdf3141af1..fe819b76de5685f2cf28a2db9000a117ca8338ce 100644 (file)
@@ -64,6 +64,18 @@ config SOFT_WATCHDOG
          To compile this driver as a module, choose M here: the
          module will be called softdog.
 
+config DA9052_WATCHDOG
+        tristate "Dialog DA9052 Watchdog"
+        depends on PMIC_DA9052
+        select WATCHDOG_CORE
+        help
+          Support for the watchdog in the DA9052 PMIC. Watchdog trigger
+          cause system reset.
+
+          Say Y here to include support for the DA9052 watchdog.
+          Alternatively say M to compile the driver as a module,
+          which will be called da9052_wdt.
+
 config WM831X_WATCHDOG
        tristate "WM831x watchdog"
        depends on MFD_WM831X
@@ -87,6 +99,7 @@ config WM8350_WATCHDOG
 config ARM_SP805_WATCHDOG
        tristate "ARM SP805 Watchdog"
        depends on ARM_AMBA
+       select WATCHDOG_CORE
        help
          ARM Primecell SP805 Watchdog timer. This will reboot your system when
          the timeout is reached.
@@ -565,6 +578,7 @@ config INTEL_SCU_WATCHDOG
 config ITCO_WDT
        tristate "Intel TCO Timer/Watchdog"
        depends on (X86 || IA64) && PCI
+       select LPC_ICH
        ---help---
          Hardware driver for the intel TCO timer based watchdog devices.
          These drivers are included in the Intel 82801 I/O Controller
index 442bfbe0882a29206035d17c0faa5ca629c7af45..572b39bed06a256ff6c79df0b943acbda7e88174 100644 (file)
@@ -163,6 +163,7 @@ obj-$(CONFIG_WATCHDOG_CP1XXX)               += cpwd.o
 obj-$(CONFIG_XEN_WDT) += xen_wdt.o
 
 # Architecture Independent
+obj-$(CONFIG_DA9052_WATCHDOG) += da9052_wdt.o
 obj-$(CONFIG_WM831X_WATCHDOG) += wm831x_wdt.o
 obj-$(CONFIG_WM8350_WATCHDOG) += wm8350_wdt.o
 obj-$(CONFIG_MAX63XX_WATCHDOG) += max63xx_wdt.o
diff --git a/drivers/watchdog/da9052_wdt.c b/drivers/watchdog/da9052_wdt.c
new file mode 100644 (file)
index 0000000..3f75129
--- /dev/null
@@ -0,0 +1,251 @@
+/*
+ * System monitoring driver for DA9052 PMICs.
+ *
+ * Copyright(c) 2012 Dialog Semiconductor Ltd.
+ *
+ * Author: Anthony Olech <Anthony.Olech@diasemi.com>
+ *
+ * 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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/uaccess.h>
+#include <linux/platform_device.h>
+#include <linux/time.h>
+#include <linux/watchdog.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/jiffies.h>
+#include <linux/delay.h>
+
+#include <linux/mfd/da9052/reg.h>
+#include <linux/mfd/da9052/da9052.h>
+
+#define DA9052_DEF_TIMEOUT     4
+#define DA9052_TWDMIN          256
+
+struct da9052_wdt_data {
+       struct watchdog_device wdt;
+       struct da9052 *da9052;
+       struct kref kref;
+       unsigned long jpast;
+};
+
+static const struct {
+       u8 reg_val;
+       int time;  /* Seconds */
+} da9052_wdt_maps[] = {
+       { 1, 2 },
+       { 2, 4 },
+       { 3, 8 },
+       { 4, 16 },
+       { 5, 32 },
+       { 5, 33 },  /* Actual time  32.768s so included both 32s and 33s */
+       { 6, 65 },
+       { 6, 66 },  /* Actual time 65.536s so include both, 65s and 66s */
+       { 7, 131 },
+};
+
+
+static void da9052_wdt_release_resources(struct kref *r)
+{
+       struct da9052_wdt_data *driver_data =
+               container_of(r, struct da9052_wdt_data, kref);
+
+       kfree(driver_data);
+}
+
+static int da9052_wdt_set_timeout(struct watchdog_device *wdt_dev,
+                                 unsigned int timeout)
+{
+       struct da9052_wdt_data *driver_data = watchdog_get_drvdata(wdt_dev);
+       struct da9052 *da9052 = driver_data->da9052;
+       int ret, i;
+
+       /*
+        * Disable the Watchdog timer before setting
+        * new time out.
+        */
+       ret = da9052_reg_update(da9052, DA9052_CONTROL_D_REG,
+                               DA9052_CONTROLD_TWDSCALE, 0);
+       if (ret < 0) {
+               dev_err(da9052->dev, "Failed to disable watchdog bit, %d\n",
+                       ret);
+               return ret;
+       }
+       if (timeout) {
+               /*
+                * To change the timeout, da9052 needs to
+                * be disabled for at least 150 us.
+                */
+               udelay(150);
+
+               /* Set the desired timeout */
+               for (i = 0; i < ARRAY_SIZE(da9052_wdt_maps); i++)
+                       if (da9052_wdt_maps[i].time == timeout)
+                               break;
+
+               if (i == ARRAY_SIZE(da9052_wdt_maps))
+                       ret = -EINVAL;
+               else
+                       ret = da9052_reg_update(da9052, DA9052_CONTROL_D_REG,
+                                               DA9052_CONTROLD_TWDSCALE,
+                                               da9052_wdt_maps[i].reg_val);
+               if (ret < 0) {
+                       dev_err(da9052->dev,
+                               "Failed to update timescale bit, %d\n", ret);
+                       return ret;
+               }
+
+               wdt_dev->timeout = timeout;
+               driver_data->jpast = jiffies;
+       }
+
+       return 0;
+}
+
+static void da9052_wdt_ref(struct watchdog_device *wdt_dev)
+{
+       struct da9052_wdt_data *driver_data = watchdog_get_drvdata(wdt_dev);
+
+       kref_get(&driver_data->kref);
+}
+
+static void da9052_wdt_unref(struct watchdog_device *wdt_dev)
+{
+       struct da9052_wdt_data *driver_data = watchdog_get_drvdata(wdt_dev);
+
+       kref_put(&driver_data->kref, da9052_wdt_release_resources);
+}
+
+static int da9052_wdt_start(struct watchdog_device *wdt_dev)
+{
+       return da9052_wdt_set_timeout(wdt_dev, wdt_dev->timeout);
+}
+
+static int da9052_wdt_stop(struct watchdog_device *wdt_dev)
+{
+       return da9052_wdt_set_timeout(wdt_dev, 0);
+}
+
+static int da9052_wdt_ping(struct watchdog_device *wdt_dev)
+{
+       struct da9052_wdt_data *driver_data = watchdog_get_drvdata(wdt_dev);
+       struct da9052 *da9052 = driver_data->da9052;
+       unsigned long msec, jnow = jiffies;
+       int ret;
+
+       /*
+        * We have a minimum time for watchdog window called TWDMIN. A write
+        * to the watchdog before this elapsed time should cause an error.
+        */
+       msec = (jnow - driver_data->jpast) * 1000/HZ;
+       if (msec < DA9052_TWDMIN)
+               mdelay(msec);
+
+       /* Reset the watchdog timer */
+       ret = da9052_reg_update(da9052, DA9052_CONTROL_D_REG,
+                               DA9052_CONTROLD_WATCHDOG, 1 << 7);
+       if (ret < 0)
+               goto err_strobe;
+
+       /*
+        * FIXME: Reset the watchdog core, in general PMIC
+        * is supposed to do this
+        */
+       ret = da9052_reg_update(da9052, DA9052_CONTROL_D_REG,
+                               DA9052_CONTROLD_WATCHDOG, 0 << 7);
+err_strobe:
+       return ret;
+}
+
+static struct watchdog_info da9052_wdt_info = {
+       .options        = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
+       .identity       = "DA9052 Watchdog",
+};
+
+static const struct watchdog_ops da9052_wdt_ops = {
+       .owner = THIS_MODULE,
+       .start = da9052_wdt_start,
+       .stop = da9052_wdt_stop,
+       .ping = da9052_wdt_ping,
+       .set_timeout = da9052_wdt_set_timeout,
+       .ref = da9052_wdt_ref,
+       .unref = da9052_wdt_unref,
+};
+
+
+static int __devinit da9052_wdt_probe(struct platform_device *pdev)
+{
+       struct da9052 *da9052 = dev_get_drvdata(pdev->dev.parent);
+       struct da9052_wdt_data *driver_data;
+       struct watchdog_device *da9052_wdt;
+       int ret;
+
+       driver_data = devm_kzalloc(&pdev->dev, sizeof(*driver_data),
+                                  GFP_KERNEL);
+       if (!driver_data) {
+               dev_err(da9052->dev, "Unable to alloacate watchdog device\n");
+               ret = -ENOMEM;
+               goto err;
+       }
+       driver_data->da9052 = da9052;
+
+       da9052_wdt = &driver_data->wdt;
+
+       da9052_wdt->timeout = DA9052_DEF_TIMEOUT;
+       da9052_wdt->info = &da9052_wdt_info;
+       da9052_wdt->ops = &da9052_wdt_ops;
+       watchdog_set_drvdata(da9052_wdt, driver_data);
+
+       kref_init(&driver_data->kref);
+
+       ret = da9052_reg_update(da9052, DA9052_CONTROL_D_REG,
+                               DA9052_CONTROLD_TWDSCALE, 0);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "Failed to disable watchdog bits, %d\n",
+                       ret);
+               goto err;
+       }
+
+       ret = watchdog_register_device(&driver_data->wdt);
+       if (ret != 0) {
+               dev_err(da9052->dev, "watchdog_register_device() failed: %d\n",
+                       ret);
+               goto err;
+       }
+
+       dev_set_drvdata(&pdev->dev, driver_data);
+err:
+       return ret;
+}
+
+static int __devexit da9052_wdt_remove(struct platform_device *pdev)
+{
+       struct da9052_wdt_data *driver_data = dev_get_drvdata(&pdev->dev);
+
+       watchdog_unregister_device(&driver_data->wdt);
+       kref_put(&driver_data->kref, da9052_wdt_release_resources);
+
+       return 0;
+}
+
+static struct platform_driver da9052_wdt_driver = {
+       .probe = da9052_wdt_probe,
+       .remove = __devexit_p(da9052_wdt_remove),
+       .driver = {
+               .name   = "da9052-watchdog",
+       },
+};
+
+module_platform_driver(da9052_wdt_driver);
+
+MODULE_AUTHOR("Anthony Olech <Anthony.Olech@diasemi.com>");
+MODULE_DESCRIPTION("DA9052 SM Device Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:da9052-watchdog");
index 9e27e6422f661c5fe6a97f8655489b3285eb40b0..3c57b45537a2f0cd552c7fc571156428de918175 100644 (file)
@@ -1,8 +1,8 @@
 /* iTCO Vendor Specific Support hooks */
 #ifdef CONFIG_ITCO_VENDOR_SUPPORT
-extern void iTCO_vendor_pre_start(unsigned long, unsigned int);
-extern void iTCO_vendor_pre_stop(unsigned long);
-extern void iTCO_vendor_pre_keepalive(unsigned long, unsigned int);
+extern void iTCO_vendor_pre_start(struct resource *, unsigned int);
+extern void iTCO_vendor_pre_stop(struct resource *);
+extern void iTCO_vendor_pre_keepalive(struct resource *, unsigned int);
 extern void iTCO_vendor_pre_set_heartbeat(unsigned int);
 extern int iTCO_vendor_check_noreboot_on(void);
 #else
index 2721d29ce243fe0d663fea99a4c7823d6bb50277..b6b2f90b5d443c85f08c10e7a68de0f1b25942f4 100644 (file)
 
 #include "iTCO_vendor.h"
 
-/* iTCO defines */
-#define        SMI_EN          (acpibase + 0x30) /* SMI Control and Enable Register */
-#define        TCOBASE         (acpibase + 0x60) /* TCO base address */
-#define        TCO1_STS        (TCOBASE + 0x04)  /* TCO1 Status Register */
-
 /* List of vendor support modes */
 /* SuperMicro Pentium 3 Era 370SSE+-OEM1/P3TSSE */
 #define SUPERMICRO_OLD_BOARD   1
@@ -82,24 +77,24 @@ MODULE_PARM_DESC(vendorsupport, "iTCO vendor specific support mode, default="
  *         20.6 seconds.
  */
 
-static void supermicro_old_pre_start(unsigned long acpibase)
+static void supermicro_old_pre_start(struct resource *smires)
 {
        unsigned long val32;
 
        /* Bit 13: TCO_EN -> 0 = Disables TCO logic generating an SMI# */
-       val32 = inl(SMI_EN);
+       val32 = inl(smires->start);
        val32 &= 0xffffdfff;    /* Turn off SMI clearing watchdog */
-       outl(val32, SMI_EN);    /* Needed to activate watchdog */
+       outl(val32, smires->start);     /* Needed to activate watchdog */
 }
 
-static void supermicro_old_pre_stop(unsigned long acpibase)
+static void supermicro_old_pre_stop(struct resource *smires)
 {
        unsigned long val32;
 
        /* Bit 13: TCO_EN -> 1 = Enables the TCO logic to generate SMI# */
-       val32 = inl(SMI_EN);
+       val32 = inl(smires->start);
        val32 |= 0x00002000;    /* Turn on SMI clearing watchdog */
-       outl(val32, SMI_EN);    /* Needed to deactivate watchdog */
+       outl(val32, smires->start);     /* Needed to deactivate watchdog */
 }
 
 /*
@@ -270,66 +265,66 @@ static void supermicro_new_pre_set_heartbeat(unsigned int heartbeat)
  *     Don't use this fix if you don't need to!!!
  */
 
-static void broken_bios_start(unsigned long acpibase)
+static void broken_bios_start(struct resource *smires)
 {
        unsigned long val32;
 
-       val32 = inl(SMI_EN);
+       val32 = inl(smires->start);
        /* Bit 13: TCO_EN     -> 0 = Disables TCO logic generating an SMI#
           Bit  0: GBL_SMI_EN -> 0 = No SMI# will be generated by ICH. */
        val32 &= 0xffffdffe;
-       outl(val32, SMI_EN);
+       outl(val32, smires->start);
 }
 
-static void broken_bios_stop(unsigned long acpibase)
+static void broken_bios_stop(struct resource *smires)
 {
        unsigned long val32;
 
-       val32 = inl(SMI_EN);
+       val32 = inl(smires->start);
        /* Bit 13: TCO_EN     -> 1 = Enables TCO logic generating an SMI#
           Bit  0: GBL_SMI_EN -> 1 = Turn global SMI on again. */
        val32 |= 0x00002001;
-       outl(val32, SMI_EN);
+       outl(val32, smires->start);
 }
 
 /*
  *     Generic Support Functions
  */
 
-void iTCO_vendor_pre_start(unsigned long acpibase,
+void iTCO_vendor_pre_start(struct resource *smires,
                           unsigned int heartbeat)
 {
        switch (vendorsupport) {
        case SUPERMICRO_OLD_BOARD:
-               supermicro_old_pre_start(acpibase);
+               supermicro_old_pre_start(smires);
                break;
        case SUPERMICRO_NEW_BOARD:
                supermicro_new_pre_start(heartbeat);
                break;
        case BROKEN_BIOS:
-               broken_bios_start(acpibase);
+               broken_bios_start(smires);
                break;
        }
 }
 EXPORT_SYMBOL(iTCO_vendor_pre_start);
 
-void iTCO_vendor_pre_stop(unsigned long acpibase)
+void iTCO_vendor_pre_stop(struct resource *smires)
 {
        switch (vendorsupport) {
        case SUPERMICRO_OLD_BOARD:
-               supermicro_old_pre_stop(acpibase);
+               supermicro_old_pre_stop(smires);
                break;
        case SUPERMICRO_NEW_BOARD:
                supermicro_new_pre_stop();
                break;
        case BROKEN_BIOS:
-               broken_bios_stop(acpibase);
+               broken_bios_stop(smires);
                break;
        }
 }
 EXPORT_SYMBOL(iTCO_vendor_pre_stop);
 
-void iTCO_vendor_pre_keepalive(unsigned long acpibase, unsigned int heartbeat)
+void iTCO_vendor_pre_keepalive(struct resource *smires, unsigned int heartbeat)
 {
        if (vendorsupport == SUPERMICRO_NEW_BOARD)
                supermicro_new_pre_set_heartbeat(heartbeat);
index 9fecb95645a35d86d5183a27212786abad001fa0..bc47e9012f370ff43c0e50d33581fe751cc4742f 100644 (file)
 #include <linux/spinlock.h>            /* For spin_lock/spin_unlock/... */
 #include <linux/uaccess.h>             /* For copy_to_user/put_user/... */
 #include <linux/io.h>                  /* For inb/outb/... */
+#include <linux/mfd/core.h>
+#include <linux/mfd/lpc_ich.h>
 
 #include "iTCO_vendor.h"
 
-/* TCO related info */
-enum iTCO_chipsets {
-       TCO_ICH = 0,    /* ICH */
-       TCO_ICH0,       /* ICH0 */
-       TCO_ICH2,       /* ICH2 */
-       TCO_ICH2M,      /* ICH2-M */
-       TCO_ICH3,       /* ICH3-S */
-       TCO_ICH3M,      /* ICH3-M */
-       TCO_ICH4,       /* ICH4 */
-       TCO_ICH4M,      /* ICH4-M */
-       TCO_CICH,       /* C-ICH */
-       TCO_ICH5,       /* ICH5 & ICH5R */
-       TCO_6300ESB,    /* 6300ESB */
-       TCO_ICH6,       /* ICH6 & ICH6R */
-       TCO_ICH6M,      /* ICH6-M */
-       TCO_ICH6W,      /* ICH6W & ICH6RW */
-       TCO_631XESB,    /* 631xESB/632xESB */
-       TCO_ICH7,       /* ICH7 & ICH7R */
-       TCO_ICH7DH,     /* ICH7DH */
-       TCO_ICH7M,      /* ICH7-M & ICH7-U */
-       TCO_ICH7MDH,    /* ICH7-M DH */
-       TCO_NM10,       /* NM10 */
-       TCO_ICH8,       /* ICH8 & ICH8R */
-       TCO_ICH8DH,     /* ICH8DH */
-       TCO_ICH8DO,     /* ICH8DO */
-       TCO_ICH8M,      /* ICH8M */
-       TCO_ICH8ME,     /* ICH8M-E */
-       TCO_ICH9,       /* ICH9 */
-       TCO_ICH9R,      /* ICH9R */
-       TCO_ICH9DH,     /* ICH9DH */
-       TCO_ICH9DO,     /* ICH9DO */
-       TCO_ICH9M,      /* ICH9M */
-       TCO_ICH9ME,     /* ICH9M-E */
-       TCO_ICH10,      /* ICH10 */
-       TCO_ICH10R,     /* ICH10R */
-       TCO_ICH10D,     /* ICH10D */
-       TCO_ICH10DO,    /* ICH10DO */
-       TCO_PCH,        /* PCH Desktop Full Featured */
-       TCO_PCHM,       /* PCH Mobile Full Featured */
-       TCO_P55,        /* P55 */
-       TCO_PM55,       /* PM55 */
-       TCO_H55,        /* H55 */
-       TCO_QM57,       /* QM57 */
-       TCO_H57,        /* H57 */
-       TCO_HM55,       /* HM55 */
-       TCO_Q57,        /* Q57 */
-       TCO_HM57,       /* HM57 */
-       TCO_PCHMSFF,    /* PCH Mobile SFF Full Featured */
-       TCO_QS57,       /* QS57 */
-       TCO_3400,       /* 3400 */
-       TCO_3420,       /* 3420 */
-       TCO_3450,       /* 3450 */
-       TCO_EP80579,    /* EP80579 */
-       TCO_CPT,        /* Cougar Point */
-       TCO_CPTD,       /* Cougar Point Desktop */
-       TCO_CPTM,       /* Cougar Point Mobile */
-       TCO_PBG,        /* Patsburg */
-       TCO_DH89XXCC,   /* DH89xxCC */
-       TCO_PPT,        /* Panther Point */
-       TCO_LPT,        /* Lynx Point */
-};
-
-static struct {
-       char *name;
-       unsigned int iTCO_version;
-} iTCO_chipset_info[] __devinitdata = {
-       {"ICH", 1},
-       {"ICH0", 1},
-       {"ICH2", 1},
-       {"ICH2-M", 1},
-       {"ICH3-S", 1},
-       {"ICH3-M", 1},
-       {"ICH4", 1},
-       {"ICH4-M", 1},
-       {"C-ICH", 1},
-       {"ICH5 or ICH5R", 1},
-       {"6300ESB", 1},
-       {"ICH6 or ICH6R", 2},
-       {"ICH6-M", 2},
-       {"ICH6W or ICH6RW", 2},
-       {"631xESB/632xESB", 2},
-       {"ICH7 or ICH7R", 2},
-       {"ICH7DH", 2},
-       {"ICH7-M or ICH7-U", 2},
-       {"ICH7-M DH", 2},
-       {"NM10", 2},
-       {"ICH8 or ICH8R", 2},
-       {"ICH8DH", 2},
-       {"ICH8DO", 2},
-       {"ICH8M", 2},
-       {"ICH8M-E", 2},
-       {"ICH9", 2},
-       {"ICH9R", 2},
-       {"ICH9DH", 2},
-       {"ICH9DO", 2},
-       {"ICH9M", 2},
-       {"ICH9M-E", 2},
-       {"ICH10", 2},
-       {"ICH10R", 2},
-       {"ICH10D", 2},
-       {"ICH10DO", 2},
-       {"PCH Desktop Full Featured", 2},
-       {"PCH Mobile Full Featured", 2},
-       {"P55", 2},
-       {"PM55", 2},
-       {"H55", 2},
-       {"QM57", 2},
-       {"H57", 2},
-       {"HM55", 2},
-       {"Q57", 2},
-       {"HM57", 2},
-       {"PCH Mobile SFF Full Featured", 2},
-       {"QS57", 2},
-       {"3400", 2},
-       {"3420", 2},
-       {"3450", 2},
-       {"EP80579", 2},
-       {"Cougar Point", 2},
-       {"Cougar Point Desktop", 2},
-       {"Cougar Point Mobile", 2},
-       {"Patsburg", 2},
-       {"DH89xxCC", 2},
-       {"Panther Point", 2},
-       {"Lynx Point", 2},
-       {NULL, 0}
-};
-
-/*
- * This data only exists for exporting the supported PCI ids
- * via MODULE_DEVICE_TABLE.  We do not actually register a
- * pci_driver, because the I/O Controller Hub has also other
- * functions that probably will be registered by other drivers.
- */
-static DEFINE_PCI_DEVICE_TABLE(iTCO_wdt_pci_tbl) = {
-       { PCI_VDEVICE(INTEL, 0x2410), TCO_ICH},
-       { PCI_VDEVICE(INTEL, 0x2420), TCO_ICH0},
-       { PCI_VDEVICE(INTEL, 0x2440), TCO_ICH2},
-       { PCI_VDEVICE(INTEL, 0x244c), TCO_ICH2M},
-       { PCI_VDEVICE(INTEL, 0x2480), TCO_ICH3},
-       { PCI_VDEVICE(INTEL, 0x248c), TCO_ICH3M},
-       { PCI_VDEVICE(INTEL, 0x24c0), TCO_ICH4},
-       { PCI_VDEVICE(INTEL, 0x24cc), TCO_ICH4M},
-       { PCI_VDEVICE(INTEL, 0x2450), TCO_CICH},
-       { PCI_VDEVICE(INTEL, 0x24d0), TCO_ICH5},
-       { PCI_VDEVICE(INTEL, 0x25a1), TCO_6300ESB},
-       { PCI_VDEVICE(INTEL, 0x2640), TCO_ICH6},
-       { PCI_VDEVICE(INTEL, 0x2641), TCO_ICH6M},
-       { PCI_VDEVICE(INTEL, 0x2642), TCO_ICH6W},
-       { PCI_VDEVICE(INTEL, 0x2670), TCO_631XESB},
-       { PCI_VDEVICE(INTEL, 0x2671), TCO_631XESB},
-       { PCI_VDEVICE(INTEL, 0x2672), TCO_631XESB},
-       { PCI_VDEVICE(INTEL, 0x2673), TCO_631XESB},
-       { PCI_VDEVICE(INTEL, 0x2674), TCO_631XESB},
-       { PCI_VDEVICE(INTEL, 0x2675), TCO_631XESB},
-       { PCI_VDEVICE(INTEL, 0x2676), TCO_631XESB},
-       { PCI_VDEVICE(INTEL, 0x2677), TCO_631XESB},
-       { PCI_VDEVICE(INTEL, 0x2678), TCO_631XESB},
-       { PCI_VDEVICE(INTEL, 0x2679), TCO_631XESB},
-       { PCI_VDEVICE(INTEL, 0x267a), TCO_631XESB},
-       { PCI_VDEVICE(INTEL, 0x267b), TCO_631XESB},
-       { PCI_VDEVICE(INTEL, 0x267c), TCO_631XESB},
-       { PCI_VDEVICE(INTEL, 0x267d), TCO_631XESB},
-       { PCI_VDEVICE(INTEL, 0x267e), TCO_631XESB},
-       { PCI_VDEVICE(INTEL, 0x267f), TCO_631XESB},
-       { PCI_VDEVICE(INTEL, 0x27b8), TCO_ICH7},
-       { PCI_VDEVICE(INTEL, 0x27b0), TCO_ICH7DH},
-       { PCI_VDEVICE(INTEL, 0x27b9), TCO_ICH7M},
-       { PCI_VDEVICE(INTEL, 0x27bd), TCO_ICH7MDH},
-       { PCI_VDEVICE(INTEL, 0x27bc), TCO_NM10},
-       { PCI_VDEVICE(INTEL, 0x2810), TCO_ICH8},
-       { PCI_VDEVICE(INTEL, 0x2812), TCO_ICH8DH},
-       { PCI_VDEVICE(INTEL, 0x2814), TCO_ICH8DO},
-       { PCI_VDEVICE(INTEL, 0x2815), TCO_ICH8M},
-       { PCI_VDEVICE(INTEL, 0x2811), TCO_ICH8ME},
-       { PCI_VDEVICE(INTEL, 0x2918), TCO_ICH9},
-       { PCI_VDEVICE(INTEL, 0x2916), TCO_ICH9R},
-       { PCI_VDEVICE(INTEL, 0x2912), TCO_ICH9DH},
-       { PCI_VDEVICE(INTEL, 0x2914), TCO_ICH9DO},
-       { PCI_VDEVICE(INTEL, 0x2919), TCO_ICH9M},
-       { PCI_VDEVICE(INTEL, 0x2917), TCO_ICH9ME},
-       { PCI_VDEVICE(INTEL, 0x3a18), TCO_ICH10},
-       { PCI_VDEVICE(INTEL, 0x3a16), TCO_ICH10R},
-       { PCI_VDEVICE(INTEL, 0x3a1a), TCO_ICH10D},
-       { PCI_VDEVICE(INTEL, 0x3a14), TCO_ICH10DO},
-       { PCI_VDEVICE(INTEL, 0x3b00), TCO_PCH},
-       { PCI_VDEVICE(INTEL, 0x3b01), TCO_PCHM},
-       { PCI_VDEVICE(INTEL, 0x3b02), TCO_P55},
-       { PCI_VDEVICE(INTEL, 0x3b03), TCO_PM55},
-       { PCI_VDEVICE(INTEL, 0x3b06), TCO_H55},
-       { PCI_VDEVICE(INTEL, 0x3b07), TCO_QM57},
-       { PCI_VDEVICE(INTEL, 0x3b08), TCO_H57},
-       { PCI_VDEVICE(INTEL, 0x3b09), TCO_HM55},
-       { PCI_VDEVICE(INTEL, 0x3b0a), TCO_Q57},
-       { PCI_VDEVICE(INTEL, 0x3b0b), TCO_HM57},
-       { PCI_VDEVICE(INTEL, 0x3b0d), TCO_PCHMSFF},
-       { PCI_VDEVICE(INTEL, 0x3b0f), TCO_QS57},
-       { PCI_VDEVICE(INTEL, 0x3b12), TCO_3400},
-       { PCI_VDEVICE(INTEL, 0x3b14), TCO_3420},
-       { PCI_VDEVICE(INTEL, 0x3b16), TCO_3450},
-       { PCI_VDEVICE(INTEL, 0x5031), TCO_EP80579},
-       { PCI_VDEVICE(INTEL, 0x1c41), TCO_CPT},
-       { PCI_VDEVICE(INTEL, 0x1c42), TCO_CPTD},
-       { PCI_VDEVICE(INTEL, 0x1c43), TCO_CPTM},
-       { PCI_VDEVICE(INTEL, 0x1c44), TCO_CPT},
-       { PCI_VDEVICE(INTEL, 0x1c45), TCO_CPT},
-       { PCI_VDEVICE(INTEL, 0x1c46), TCO_CPT},
-       { PCI_VDEVICE(INTEL, 0x1c47), TCO_CPT},
-       { PCI_VDEVICE(INTEL, 0x1c48), TCO_CPT},
-       { PCI_VDEVICE(INTEL, 0x1c49), TCO_CPT},
-       { PCI_VDEVICE(INTEL, 0x1c4a), TCO_CPT},
-       { PCI_VDEVICE(INTEL, 0x1c4b), TCO_CPT},
-       { PCI_VDEVICE(INTEL, 0x1c4c), TCO_CPT},
-       { PCI_VDEVICE(INTEL, 0x1c4d), TCO_CPT},
-       { PCI_VDEVICE(INTEL, 0x1c4e), TCO_CPT},
-       { PCI_VDEVICE(INTEL, 0x1c4f), TCO_CPT},
-       { PCI_VDEVICE(INTEL, 0x1c50), TCO_CPT},
-       { PCI_VDEVICE(INTEL, 0x1c51), TCO_CPT},
-       { PCI_VDEVICE(INTEL, 0x1c52), TCO_CPT},
-       { PCI_VDEVICE(INTEL, 0x1c53), TCO_CPT},
-       { PCI_VDEVICE(INTEL, 0x1c54), TCO_CPT},
-       { PCI_VDEVICE(INTEL, 0x1c55), TCO_CPT},
-       { PCI_VDEVICE(INTEL, 0x1c56), TCO_CPT},
-       { PCI_VDEVICE(INTEL, 0x1c57), TCO_CPT},
-       { PCI_VDEVICE(INTEL, 0x1c58), TCO_CPT},
-       { PCI_VDEVICE(INTEL, 0x1c59), TCO_CPT},
-       { PCI_VDEVICE(INTEL, 0x1c5a), TCO_CPT},
-       { PCI_VDEVICE(INTEL, 0x1c5b), TCO_CPT},
-       { PCI_VDEVICE(INTEL, 0x1c5c), TCO_CPT},
-       { PCI_VDEVICE(INTEL, 0x1c5d), TCO_CPT},
-       { PCI_VDEVICE(INTEL, 0x1c5e), TCO_CPT},
-       { PCI_VDEVICE(INTEL, 0x1c5f), TCO_CPT},
-       { PCI_VDEVICE(INTEL, 0x1d40), TCO_PBG},
-       { PCI_VDEVICE(INTEL, 0x1d41), TCO_PBG},
-       { PCI_VDEVICE(INTEL, 0x2310), TCO_DH89XXCC},
-       { PCI_VDEVICE(INTEL, 0x1e40), TCO_PPT},
-       { PCI_VDEVICE(INTEL, 0x1e41), TCO_PPT},
-       { PCI_VDEVICE(INTEL, 0x1e42), TCO_PPT},
-       { PCI_VDEVICE(INTEL, 0x1e43), TCO_PPT},
-       { PCI_VDEVICE(INTEL, 0x1e44), TCO_PPT},
-       { PCI_VDEVICE(INTEL, 0x1e45), TCO_PPT},
-       { PCI_VDEVICE(INTEL, 0x1e46), TCO_PPT},
-       { PCI_VDEVICE(INTEL, 0x1e47), TCO_PPT},
-       { PCI_VDEVICE(INTEL, 0x1e48), TCO_PPT},
-       { PCI_VDEVICE(INTEL, 0x1e49), TCO_PPT},
-       { PCI_VDEVICE(INTEL, 0x1e4a), TCO_PPT},
-       { PCI_VDEVICE(INTEL, 0x1e4b), TCO_PPT},
-       { PCI_VDEVICE(INTEL, 0x1e4c), TCO_PPT},
-       { PCI_VDEVICE(INTEL, 0x1e4d), TCO_PPT},
-       { PCI_VDEVICE(INTEL, 0x1e4e), TCO_PPT},
-       { PCI_VDEVICE(INTEL, 0x1e4f), TCO_PPT},
-       { PCI_VDEVICE(INTEL, 0x1e50), TCO_PPT},
-       { PCI_VDEVICE(INTEL, 0x1e51), TCO_PPT},
-       { PCI_VDEVICE(INTEL, 0x1e52), TCO_PPT},
-       { PCI_VDEVICE(INTEL, 0x1e53), TCO_PPT},
-       { PCI_VDEVICE(INTEL, 0x1e54), TCO_PPT},
-       { PCI_VDEVICE(INTEL, 0x1e55), TCO_PPT},
-       { PCI_VDEVICE(INTEL, 0x1e56), TCO_PPT},
-       { PCI_VDEVICE(INTEL, 0x1e57), TCO_PPT},
-       { PCI_VDEVICE(INTEL, 0x1e58), TCO_PPT},
-       { PCI_VDEVICE(INTEL, 0x1e59), TCO_PPT},
-       { PCI_VDEVICE(INTEL, 0x1e5a), TCO_PPT},
-       { PCI_VDEVICE(INTEL, 0x1e5b), TCO_PPT},
-       { PCI_VDEVICE(INTEL, 0x1e5c), TCO_PPT},
-       { PCI_VDEVICE(INTEL, 0x1e5d), TCO_PPT},
-       { PCI_VDEVICE(INTEL, 0x1e5e), TCO_PPT},
-       { PCI_VDEVICE(INTEL, 0x1e5f), TCO_PPT},
-       { PCI_VDEVICE(INTEL, 0x8c40), TCO_LPT},
-       { PCI_VDEVICE(INTEL, 0x8c41), TCO_LPT},
-       { PCI_VDEVICE(INTEL, 0x8c42), TCO_LPT},
-       { PCI_VDEVICE(INTEL, 0x8c43), TCO_LPT},
-       { PCI_VDEVICE(INTEL, 0x8c44), TCO_LPT},
-       { PCI_VDEVICE(INTEL, 0x8c45), TCO_LPT},
-       { PCI_VDEVICE(INTEL, 0x8c46), TCO_LPT},
-       { PCI_VDEVICE(INTEL, 0x8c47), TCO_LPT},
-       { PCI_VDEVICE(INTEL, 0x8c48), TCO_LPT},
-       { PCI_VDEVICE(INTEL, 0x8c49), TCO_LPT},
-       { PCI_VDEVICE(INTEL, 0x8c4a), TCO_LPT},
-       { PCI_VDEVICE(INTEL, 0x8c4b), TCO_LPT},
-       { PCI_VDEVICE(INTEL, 0x8c4c), TCO_LPT},
-       { PCI_VDEVICE(INTEL, 0x8c4d), TCO_LPT},
-       { PCI_VDEVICE(INTEL, 0x8c4e), TCO_LPT},
-       { PCI_VDEVICE(INTEL, 0x8c4f), TCO_LPT},
-       { PCI_VDEVICE(INTEL, 0x8c50), TCO_LPT},
-       { PCI_VDEVICE(INTEL, 0x8c51), TCO_LPT},
-       { PCI_VDEVICE(INTEL, 0x8c52), TCO_LPT},
-       { PCI_VDEVICE(INTEL, 0x8c53), TCO_LPT},
-       { PCI_VDEVICE(INTEL, 0x8c54), TCO_LPT},
-       { PCI_VDEVICE(INTEL, 0x8c55), TCO_LPT},
-       { PCI_VDEVICE(INTEL, 0x8c56), TCO_LPT},
-       { PCI_VDEVICE(INTEL, 0x8c57), TCO_LPT},
-       { PCI_VDEVICE(INTEL, 0x8c58), TCO_LPT},
-       { PCI_VDEVICE(INTEL, 0x8c59), TCO_LPT},
-       { PCI_VDEVICE(INTEL, 0x8c5a), TCO_LPT},
-       { PCI_VDEVICE(INTEL, 0x8c5b), TCO_LPT},
-       { PCI_VDEVICE(INTEL, 0x8c5c), TCO_LPT},
-       { PCI_VDEVICE(INTEL, 0x8c5d), TCO_LPT},
-       { PCI_VDEVICE(INTEL, 0x8c5e), TCO_LPT},
-       { PCI_VDEVICE(INTEL, 0x8c5f), TCO_LPT},
-       { 0, },                 /* End of list */
-};
-MODULE_DEVICE_TABLE(pci, iTCO_wdt_pci_tbl);
-
 /* Address definitions for the TCO */
 /* TCO base address */
-#define TCOBASE                (iTCO_wdt_private.ACPIBASE + 0x60)
+#define TCOBASE                (iTCO_wdt_private.tco_res->start)
 /* SMI Control and Enable Register */
-#define SMI_EN         (iTCO_wdt_private.ACPIBASE + 0x30)
+#define SMI_EN         (iTCO_wdt_private.smi_res->start)
 
 #define TCO_RLD                (TCOBASE + 0x00) /* TCO Timer Reload and Curr. Value */
 #define TCOv1_TMR      (TCOBASE + 0x01) /* TCOv1 Timer Initial Value   */
@@ -393,19 +93,18 @@ static char expect_release;
 static struct {                /* this is private data for the iTCO_wdt device */
        /* TCO version/generation */
        unsigned int iTCO_version;
-       /* The device's ACPIBASE address (TCOBASE = ACPIBASE+0x60) */
-       unsigned long ACPIBASE;
+       struct resource *tco_res;
+       struct resource *smi_res;
+       struct resource *gcs_res;
        /* NO_REBOOT flag is Memory-Mapped GCS register bit 5 (TCO version 2)*/
        unsigned long __iomem *gcs;
        /* the lock for io operations */
        spinlock_t io_lock;
+       struct platform_device *dev;
        /* the PCI-device */
        struct pci_dev *pdev;
 } iTCO_wdt_private;
 
-/* the watchdog platform device */
-static struct platform_device *iTCO_wdt_platform_device;
-
 /* module parameters */
 #define WATCHDOG_HEARTBEAT 30  /* 30 sec default heartbeat */
 static int heartbeat = WATCHDOG_HEARTBEAT;  /* in seconds */
@@ -485,7 +184,7 @@ static int iTCO_wdt_start(void)
 
        spin_lock(&iTCO_wdt_private.io_lock);
 
-       iTCO_vendor_pre_start(iTCO_wdt_private.ACPIBASE, heartbeat);
+       iTCO_vendor_pre_start(iTCO_wdt_private.smi_res, heartbeat);
 
        /* disable chipset's NO_REBOOT bit */
        if (iTCO_wdt_unset_NO_REBOOT_bit()) {
@@ -519,7 +218,7 @@ static int iTCO_wdt_stop(void)
 
        spin_lock(&iTCO_wdt_private.io_lock);
 
-       iTCO_vendor_pre_stop(iTCO_wdt_private.ACPIBASE);
+       iTCO_vendor_pre_stop(iTCO_wdt_private.smi_res);
 
        /* Bit 11: TCO Timer Halt -> 1 = The TCO timer is disabled */
        val = inw(TCO1_CNT);
@@ -541,7 +240,7 @@ static int iTCO_wdt_keepalive(void)
 {
        spin_lock(&iTCO_wdt_private.io_lock);
 
-       iTCO_vendor_pre_keepalive(iTCO_wdt_private.ACPIBASE, heartbeat);
+       iTCO_vendor_pre_keepalive(iTCO_wdt_private.smi_res, heartbeat);
 
        /* Reload the timer by writing to the TCO Timer Counter register */
        if (iTCO_wdt_private.iTCO_version == 2)
@@ -786,83 +485,120 @@ static struct miscdevice iTCO_wdt_miscdev = {
  *     Init & exit routines
  */
 
-static int __devinit iTCO_wdt_init(struct pci_dev *pdev,
-               const struct pci_device_id *ent, struct platform_device *dev)
+static void __devexit iTCO_wdt_cleanup(void)
+{
+       /* Stop the timer before we leave */
+       if (!nowayout)
+               iTCO_wdt_stop();
+
+       /* Deregister */
+       misc_deregister(&iTCO_wdt_miscdev);
+
+       /* release resources */
+       release_region(iTCO_wdt_private.tco_res->start,
+                       resource_size(iTCO_wdt_private.tco_res));
+       release_region(iTCO_wdt_private.smi_res->start,
+                       resource_size(iTCO_wdt_private.smi_res));
+       if (iTCO_wdt_private.iTCO_version == 2) {
+               iounmap(iTCO_wdt_private.gcs);
+               release_mem_region(iTCO_wdt_private.gcs_res->start,
+                               resource_size(iTCO_wdt_private.gcs_res));
+       }
+
+       iTCO_wdt_private.tco_res = NULL;
+       iTCO_wdt_private.smi_res = NULL;
+       iTCO_wdt_private.gcs_res = NULL;
+       iTCO_wdt_private.gcs = NULL;
+}
+
+static int __devinit iTCO_wdt_probe(struct platform_device *dev)
 {
-       int ret;
-       u32 base_address;
-       unsigned long RCBA;
+       int ret = -ENODEV;
        unsigned long val32;
+       struct lpc_ich_info *ich_info = dev->dev.platform_data;
+
+       if (!ich_info)
+               goto out;
+
+       spin_lock_init(&iTCO_wdt_private.io_lock);
+
+       iTCO_wdt_private.tco_res =
+               platform_get_resource(dev, IORESOURCE_IO, ICH_RES_IO_TCO);
+       if (!iTCO_wdt_private.tco_res)
+               goto out;
+
+       iTCO_wdt_private.smi_res =
+               platform_get_resource(dev, IORESOURCE_IO, ICH_RES_IO_SMI);
+       if (!iTCO_wdt_private.smi_res)
+               goto out;
+
+       iTCO_wdt_private.iTCO_version = ich_info->iTCO_version;
+       iTCO_wdt_private.dev = dev;
+       iTCO_wdt_private.pdev = to_pci_dev(dev->dev.parent);
 
        /*
-        *      Find the ACPI/PM base I/O address which is the base
-        *      for the TCO registers (TCOBASE=ACPIBASE + 0x60)
-        *      ACPIBASE is bits [15:7] from 0x40-0x43
+        * Get the Memory-Mapped GCS register, we need it for the
+        * NO_REBOOT flag (TCO v2).
         */
-       pci_read_config_dword(pdev, 0x40, &base_address);
-       base_address &= 0x0000ff80;
-       if (base_address == 0x00000000) {
-               /* Something's wrong here, ACPIBASE has to be set */
-               pr_err("failed to get TCOBASE address, device disabled by hardware/BIOS\n");
-               return -ENODEV;
-       }
-       iTCO_wdt_private.iTCO_version =
-                       iTCO_chipset_info[ent->driver_data].iTCO_version;
-       iTCO_wdt_private.ACPIBASE = base_address;
-       iTCO_wdt_private.pdev = pdev;
-
-       /* Get the Memory-Mapped GCS register, we need it for the
-          NO_REBOOT flag (TCO v2). To get access to it you have to
-          read RCBA from PCI Config space 0xf0 and use it as base.
-          GCS = RCBA + ICH6_GCS(0x3410). */
        if (iTCO_wdt_private.iTCO_version == 2) {
-               pci_read_config_dword(pdev, 0xf0, &base_address);
-               if ((base_address & 1) == 0) {
-                       pr_err("RCBA is disabled by hardware/BIOS, device disabled\n");
-                       ret = -ENODEV;
+               iTCO_wdt_private.gcs_res = platform_get_resource(dev,
+                                                       IORESOURCE_MEM,
+                                                       ICH_RES_MEM_GCS);
+
+               if (!iTCO_wdt_private.gcs_res)
+                       goto out;
+
+               if (!request_mem_region(iTCO_wdt_private.gcs_res->start,
+                       resource_size(iTCO_wdt_private.gcs_res), dev->name)) {
+                       ret = -EBUSY;
                        goto out;
                }
-               RCBA = base_address & 0xffffc000;
-               iTCO_wdt_private.gcs = ioremap((RCBA + 0x3410), 4);
+               iTCO_wdt_private.gcs = ioremap(iTCO_wdt_private.gcs_res->start,
+                       resource_size(iTCO_wdt_private.gcs_res));
+               if (!iTCO_wdt_private.gcs) {
+                       ret = -EIO;
+                       goto unreg_gcs;
+               }
        }
 
        /* Check chipset's NO_REBOOT bit */
        if (iTCO_wdt_unset_NO_REBOOT_bit() && iTCO_vendor_check_noreboot_on()) {
                pr_info("unable to reset NO_REBOOT flag, device disabled by hardware/BIOS\n");
                ret = -ENODEV;  /* Cannot reset NO_REBOOT bit */
-               goto out_unmap;
+               goto unmap_gcs;
        }
 
        /* Set the NO_REBOOT bit to prevent later reboots, just for sure */
        iTCO_wdt_set_NO_REBOOT_bit();
 
        /* The TCO logic uses the TCO_EN bit in the SMI_EN register */
-       if (!request_region(SMI_EN, 4, "iTCO_wdt")) {
-               pr_err("I/O address 0x%04lx already in use, device disabled\n",
-                      SMI_EN);
-               ret = -EIO;
-               goto out_unmap;
+       if (!request_region(iTCO_wdt_private.smi_res->start,
+                       resource_size(iTCO_wdt_private.smi_res), dev->name)) {
+               pr_err("I/O address 0x%04llx already in use, device disabled\n",
+                      (u64)SMI_EN);
+               ret = -EBUSY;
+               goto unmap_gcs;
        }
        if (turn_SMI_watchdog_clear_off >= iTCO_wdt_private.iTCO_version) {
-               /* Bit 13: TCO_EN -> 0 = Disables TCO logic generating an SMI# */
+               /*
+                * Bit 13: TCO_EN -> 0
+                * Disables TCO logic generating an SMI#
+                */
                val32 = inl(SMI_EN);
                val32 &= 0xffffdfff;    /* Turn off SMI clearing watchdog */
                outl(val32, SMI_EN);
        }
 
-       /* The TCO I/O registers reside in a 32-byte range pointed to
-          by the TCOBASE value */
-       if (!request_region(TCOBASE, 0x20, "iTCO_wdt")) {
-               pr_err("I/O address 0x%04lx already in use, device disabled\n",
-                      TCOBASE);
-               ret = -EIO;
-               goto unreg_smi_en;
+       if (!request_region(iTCO_wdt_private.tco_res->start,
+                       resource_size(iTCO_wdt_private.tco_res), dev->name)) {
+               pr_err("I/O address 0x%04llx already in use, device disabled\n",
+                      (u64)TCOBASE);
+               ret = -EBUSY;
+               goto unreg_smi;
        }
 
-       pr_info("Found a %s TCO device (Version=%d, TCOBASE=0x%04lx)\n",
-               iTCO_chipset_info[ent->driver_data].name,
-               iTCO_chipset_info[ent->driver_data].iTCO_version,
-               TCOBASE);
+       pr_info("Found a %s TCO device (Version=%d, TCOBASE=0x%04llx)\n",
+               ich_info->name, ich_info->iTCO_version, (u64)TCOBASE);
 
        /* Clear out the (probably old) status */
        outw(0x0008, TCO1_STS); /* Clear the Time Out Status bit */
@@ -883,7 +619,7 @@ static int __devinit iTCO_wdt_init(struct pci_dev *pdev,
        if (ret != 0) {
                pr_err("cannot register miscdev on minor=%d (err=%d)\n",
                       WATCHDOG_MINOR, ret);
-               goto unreg_region;
+               goto unreg_tco;
        }
 
        pr_info("initialized. heartbeat=%d sec (nowayout=%d)\n",
@@ -891,62 +627,31 @@ static int __devinit iTCO_wdt_init(struct pci_dev *pdev,
 
        return 0;
 
-unreg_region:
-       release_region(TCOBASE, 0x20);
-unreg_smi_en:
-       release_region(SMI_EN, 4);
-out_unmap:
+unreg_tco:
+       release_region(iTCO_wdt_private.tco_res->start,
+                       resource_size(iTCO_wdt_private.tco_res));
+unreg_smi:
+       release_region(iTCO_wdt_private.smi_res->start,
+                       resource_size(iTCO_wdt_private.smi_res));
+unmap_gcs:
        if (iTCO_wdt_private.iTCO_version == 2)
                iounmap(iTCO_wdt_private.gcs);
-out:
-       iTCO_wdt_private.ACPIBASE = 0;
-       return ret;
-}
-
-static void __devexit iTCO_wdt_cleanup(void)
-{
-       /* Stop the timer before we leave */
-       if (!nowayout)
-               iTCO_wdt_stop();
-
-       /* Deregister */
-       misc_deregister(&iTCO_wdt_miscdev);
-       release_region(TCOBASE, 0x20);
-       release_region(SMI_EN, 4);
+unreg_gcs:
        if (iTCO_wdt_private.iTCO_version == 2)
-               iounmap(iTCO_wdt_private.gcs);
-       pci_dev_put(iTCO_wdt_private.pdev);
-       iTCO_wdt_private.ACPIBASE = 0;
-}
-
-static int __devinit iTCO_wdt_probe(struct platform_device *dev)
-{
-       int ret = -ENODEV;
-       int found = 0;
-       struct pci_dev *pdev = NULL;
-       const struct pci_device_id *ent;
-
-       spin_lock_init(&iTCO_wdt_private.io_lock);
-
-       for_each_pci_dev(pdev) {
-               ent = pci_match_id(iTCO_wdt_pci_tbl, pdev);
-               if (ent) {
-                       found++;
-                       ret = iTCO_wdt_init(pdev, ent, dev);
-                       if (!ret)
-                               break;
-               }
-       }
-
-       if (!found)
-               pr_info("No device detected\n");
+               release_mem_region(iTCO_wdt_private.gcs_res->start,
+                               resource_size(iTCO_wdt_private.gcs_res));
+out:
+       iTCO_wdt_private.tco_res = NULL;
+       iTCO_wdt_private.smi_res = NULL;
+       iTCO_wdt_private.gcs_res = NULL;
+       iTCO_wdt_private.gcs = NULL;
 
        return ret;
 }
 
 static int __devexit iTCO_wdt_remove(struct platform_device *dev)
 {
-       if (iTCO_wdt_private.ACPIBASE)
+       if (iTCO_wdt_private.tco_res || iTCO_wdt_private.smi_res)
                iTCO_wdt_cleanup();
 
        return 0;
@@ -977,23 +682,11 @@ static int __init iTCO_wdt_init_module(void)
        if (err)
                return err;
 
-       iTCO_wdt_platform_device = platform_device_register_simple(DRV_NAME,
-                                                               -1, NULL, 0);
-       if (IS_ERR(iTCO_wdt_platform_device)) {
-               err = PTR_ERR(iTCO_wdt_platform_device);
-               goto unreg_platform_driver;
-       }
-
        return 0;
-
-unreg_platform_driver:
-       platform_driver_unregister(&iTCO_wdt_driver);
-       return err;
 }
 
 static void __exit iTCO_wdt_cleanup_module(void)
 {
-       platform_device_unregister(iTCO_wdt_platform_device);
        platform_driver_unregister(&iTCO_wdt_driver);
        pr_info("Watchdog Module Unloaded\n");
 }
index a9593a3a32a09a1c44d367877d5feb9486efb609..2e74c3a8ee5840ff405ded8932f32c6be6ea9d3e 100644 (file)
 #include <linux/fs.h>
 #include <linux/miscdevice.h>
 #include <linux/watchdog.h>
-#include <linux/platform_device.h>
+#include <linux/of_platform.h>
 #include <linux/uaccess.h>
 #include <linux/clk.h>
 #include <linux/io.h>
 
-#include <lantiq.h>
+#include <lantiq_soc.h>
 
-/* Section 3.4 of the datasheet
+/*
+ * Section 3.4 of the datasheet
  * The password sequence protects the WDT control register from unintended
  * write actions, which might cause malfunction of the WDT.
  *
@@ -70,7 +71,8 @@ ltq_wdt_disable(void)
 {
        /* write the first password magic */
        ltq_w32(LTQ_WDT_PW1, ltq_wdt_membase + LTQ_WDT_CR);
-       /* write the second password magic with no config
+       /*
+        * write the second password magic with no config
         * this turns the watchdog off
         */
        ltq_w32(LTQ_WDT_PW2, ltq_wdt_membase + LTQ_WDT_CR);
@@ -184,7 +186,7 @@ static struct miscdevice ltq_wdt_miscdev = {
        .fops   = &ltq_wdt_fops,
 };
 
-static int __init
+static int __devinit
 ltq_wdt_probe(struct platform_device *pdev)
 {
        struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -194,28 +196,27 @@ ltq_wdt_probe(struct platform_device *pdev)
                dev_err(&pdev->dev, "cannot obtain I/O memory region");
                return -ENOENT;
        }
-       res = devm_request_mem_region(&pdev->dev, res->start,
-               resource_size(res), dev_name(&pdev->dev));
-       if (!res) {
-               dev_err(&pdev->dev, "cannot request I/O memory region");
-               return -EBUSY;
-       }
-       ltq_wdt_membase = devm_ioremap_nocache(&pdev->dev, res->start,
-               resource_size(res));
+
+       ltq_wdt_membase = devm_request_and_ioremap(&pdev->dev, res);
        if (!ltq_wdt_membase) {
                dev_err(&pdev->dev, "cannot remap I/O memory region\n");
                return -ENOMEM;
        }
 
        /* we do not need to enable the clock as it is always running */
-       clk = clk_get(&pdev->dev, "io");
-       WARN_ON(!clk);
+       clk = clk_get_io();
+       if (IS_ERR(clk)) {
+               dev_err(&pdev->dev, "Failed to get clock\n");
+               return -ENOENT;
+       }
        ltq_io_region_clk_rate = clk_get_rate(clk);
        clk_put(clk);
 
+       /* find out if the watchdog caused the last reboot */
        if (ltq_reset_cause() == LTQ_RST_CAUSE_WDTRST)
                ltq_wdt_bootstatus = WDIOF_CARDRESET;
 
+       dev_info(&pdev->dev, "Init done\n");
        return misc_register(&ltq_wdt_miscdev);
 }
 
@@ -227,33 +228,26 @@ ltq_wdt_remove(struct platform_device *pdev)
        return 0;
 }
 
+static const struct of_device_id ltq_wdt_match[] = {
+       { .compatible = "lantiq,wdt" },
+       {},
+};
+MODULE_DEVICE_TABLE(of, ltq_wdt_match);
 
 static struct platform_driver ltq_wdt_driver = {
+       .probe = ltq_wdt_probe,
        .remove = __devexit_p(ltq_wdt_remove),
        .driver = {
-               .name = "ltq_wdt",
+               .name = "wdt",
                .owner = THIS_MODULE,
+               .of_match_table = ltq_wdt_match,
        },
 };
 
-static int __init
-init_ltq_wdt(void)
-{
-       return platform_driver_probe(&ltq_wdt_driver, ltq_wdt_probe);
-}
-
-static void __exit
-exit_ltq_wdt(void)
-{
-       return platform_driver_unregister(&ltq_wdt_driver);
-}
-
-module_init(init_ltq_wdt);
-module_exit(exit_ltq_wdt);
+module_platform_driver(ltq_wdt_driver);
 
 module_param(nowayout, bool, 0);
 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started");
-
 MODULE_AUTHOR("John Crispin <blogic@openwrt.org>");
 MODULE_DESCRIPTION("Lantiq SoC Watchdog");
 MODULE_LICENSE("GPL");
index bbb170e50055d43e55ff783f4aa645ec45ae1d85..afcd13676542338a4d4f231e0193ad11626f5d1a 100644 (file)
 #include <linux/amba/bus.h>
 #include <linux/bitops.h>
 #include <linux/clk.h>
-#include <linux/fs.h>
 #include <linux/init.h>
 #include <linux/io.h>
 #include <linux/ioport.h>
 #include <linux/kernel.h>
 #include <linux/math64.h>
-#include <linux/miscdevice.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <linux/pm.h>
 #include <linux/slab.h>
 #include <linux/spinlock.h>
 #include <linux/types.h>
-#include <linux/uaccess.h>
 #include <linux/watchdog.h>
 
 /* default timeout in seconds */
@@ -56,6 +53,7 @@
 
 /**
  * struct sp805_wdt: sp805 wdt device structure
+ * @wdd: instance of struct watchdog_device
  * @lock: spin lock protecting dev structure and io access
  * @base: base address of wdt
  * @clk: clock structure of wdt
  * @timeout: current programmed timeout
  */
 struct sp805_wdt {
+       struct watchdog_device          wdd;
        spinlock_t                      lock;
        void __iomem                    *base;
        struct clk                      *clk;
        struct amba_device              *adev;
-       unsigned long                   status;
-       #define WDT_BUSY                0
-       #define WDT_CAN_BE_CLOSED       1
        unsigned int                    load_val;
        unsigned int                    timeout;
 };
 
-/* local variables */
-static struct sp805_wdt *wdt;
 static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
+MODULE_PARM_DESC(nowayout,
+               "Set to 1 to keep watchdog running after device release");
 
 /* This routine finds load value that will reset system in required timout */
-static void wdt_setload(unsigned int timeout)
+static int wdt_setload(struct watchdog_device *wdd, unsigned int timeout)
 {
+       struct sp805_wdt *wdt = watchdog_get_drvdata(wdd);
        u64 load, rate;
 
        rate = clk_get_rate(wdt->clk);
@@ -103,11 +101,14 @@ static void wdt_setload(unsigned int timeout)
        /* roundup timeout to closest positive integer value */
        wdt->timeout = div_u64((load + 1) * 2 + (rate / 2), rate);
        spin_unlock(&wdt->lock);
+
+       return 0;
 }
 
 /* returns number of seconds left for reset to occur */
-static u32 wdt_timeleft(void)
+static unsigned int wdt_timeleft(struct watchdog_device *wdd)
 {
+       struct sp805_wdt *wdt = watchdog_get_drvdata(wdd);
        u64 load, rate;
 
        rate = clk_get_rate(wdt->clk);
@@ -123,166 +124,96 @@ static u32 wdt_timeleft(void)
        return div_u64(load, rate);
 }
 
-/* enables watchdog timers reset */
-static void wdt_enable(void)
+static int wdt_config(struct watchdog_device *wdd, bool ping)
 {
-       spin_lock(&wdt->lock);
+       struct sp805_wdt *wdt = watchdog_get_drvdata(wdd);
+       int ret;
 
-       writel_relaxed(UNLOCK, wdt->base + WDTLOCK);
-       writel_relaxed(wdt->load_val, wdt->base + WDTLOAD);
-       writel_relaxed(INT_MASK, wdt->base + WDTINTCLR);
-       writel_relaxed(INT_ENABLE | RESET_ENABLE, wdt->base + WDTCONTROL);
-       writel_relaxed(LOCK, wdt->base + WDTLOCK);
+       if (!ping) {
+               ret = clk_prepare(wdt->clk);
+               if (ret) {
+                       dev_err(&wdt->adev->dev, "clock prepare fail");
+                       return ret;
+               }
 
-       /* Flush posted writes. */
-       readl_relaxed(wdt->base + WDTLOCK);
-       spin_unlock(&wdt->lock);
-}
+               ret = clk_enable(wdt->clk);
+               if (ret) {
+                       dev_err(&wdt->adev->dev, "clock enable fail");
+                       clk_unprepare(wdt->clk);
+                       return ret;
+               }
+       }
 
-/* disables watchdog timers reset */
-static void wdt_disable(void)
-{
        spin_lock(&wdt->lock);
 
        writel_relaxed(UNLOCK, wdt->base + WDTLOCK);
-       writel_relaxed(0, wdt->base + WDTCONTROL);
+       writel_relaxed(wdt->load_val, wdt->base + WDTLOAD);
+
+       if (!ping) {
+               writel_relaxed(INT_MASK, wdt->base + WDTINTCLR);
+               writel_relaxed(INT_ENABLE | RESET_ENABLE, wdt->base +
+                               WDTCONTROL);
+       }
+
        writel_relaxed(LOCK, wdt->base + WDTLOCK);
 
        /* Flush posted writes. */
        readl_relaxed(wdt->base + WDTLOCK);
        spin_unlock(&wdt->lock);
+
+       return 0;
 }
 
-static ssize_t sp805_wdt_write(struct file *file, const char *data,
-               size_t len, loff_t *ppos)
+static int wdt_ping(struct watchdog_device *wdd)
 {
-       if (len) {
-               if (!nowayout) {
-                       size_t i;
-
-                       clear_bit(WDT_CAN_BE_CLOSED, &wdt->status);
-
-                       for (i = 0; i != len; i++) {
-                               char c;
-
-                               if (get_user(c, data + i))
-                                       return -EFAULT;
-                               /* Check for Magic Close character */
-                               if (c == 'V') {
-                                       set_bit(WDT_CAN_BE_CLOSED,
-                                                       &wdt->status);
-                                       break;
-                               }
-                       }
-               }
-               wdt_enable();
-       }
-       return len;
+       return wdt_config(wdd, true);
 }
 
-static const struct watchdog_info ident = {
-       .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
-       .identity = MODULE_NAME,
-};
-
-static long sp805_wdt_ioctl(struct file *file, unsigned int cmd,
-               unsigned long arg)
+/* enables watchdog timers reset */
+static int wdt_enable(struct watchdog_device *wdd)
 {
-       int ret = -ENOTTY;
-       unsigned int timeout;
-
-       switch (cmd) {
-       case WDIOC_GETSUPPORT:
-               ret = copy_to_user((struct watchdog_info *)arg, &ident,
-                               sizeof(ident)) ? -EFAULT : 0;
-               break;
-
-       case WDIOC_GETSTATUS:
-               ret = put_user(0, (int *)arg);
-               break;
-
-       case WDIOC_KEEPALIVE:
-               wdt_enable();
-               ret = 0;
-               break;
-
-       case WDIOC_SETTIMEOUT:
-               ret = get_user(timeout, (unsigned int *)arg);
-               if (ret)
-                       break;
-
-               wdt_setload(timeout);
-
-               wdt_enable();
-               /* Fall through */
-
-       case WDIOC_GETTIMEOUT:
-               ret = put_user(wdt->timeout, (unsigned int *)arg);
-               break;
-       case WDIOC_GETTIMELEFT:
-               ret = put_user(wdt_timeleft(), (unsigned int *)arg);
-               break;
-       }
-       return ret;
+       return wdt_config(wdd, false);
 }
 
-static int sp805_wdt_open(struct inode *inode, struct file *file)
+/* disables watchdog timers reset */
+static int wdt_disable(struct watchdog_device *wdd)
 {
-       int ret = 0;
-
-       if (test_and_set_bit(WDT_BUSY, &wdt->status))
-               return -EBUSY;
-
-       ret = clk_enable(wdt->clk);
-       if (ret) {
-               dev_err(&wdt->adev->dev, "clock enable fail");
-               goto err;
-       }
-
-       wdt_enable();
+       struct sp805_wdt *wdt = watchdog_get_drvdata(wdd);
 
-       /* can not be closed, once enabled */
-       clear_bit(WDT_CAN_BE_CLOSED, &wdt->status);
-       return nonseekable_open(inode, file);
+       spin_lock(&wdt->lock);
 
-err:
-       clear_bit(WDT_BUSY, &wdt->status);
-       return ret;
-}
+       writel_relaxed(UNLOCK, wdt->base + WDTLOCK);
+       writel_relaxed(0, wdt->base + WDTCONTROL);
+       writel_relaxed(LOCK, wdt->base + WDTLOCK);
 
-static int sp805_wdt_release(struct inode *inode, struct file *file)
-{
-       if (!test_bit(WDT_CAN_BE_CLOSED, &wdt->status)) {
-               clear_bit(WDT_BUSY, &wdt->status);
-               dev_warn(&wdt->adev->dev, "Device closed unexpectedly\n");
-               return 0;
-       }
+       /* Flush posted writes. */
+       readl_relaxed(wdt->base + WDTLOCK);
+       spin_unlock(&wdt->lock);
 
-       wdt_disable();
        clk_disable(wdt->clk);
-       clear_bit(WDT_BUSY, &wdt->status);
+       clk_unprepare(wdt->clk);
 
        return 0;
 }
 
-static const struct file_operations sp805_wdt_fops = {
-       .owner = THIS_MODULE,
-       .llseek = no_llseek,
-       .write = sp805_wdt_write,
-       .unlocked_ioctl = sp805_wdt_ioctl,
-       .open = sp805_wdt_open,
-       .release = sp805_wdt_release,
+static const struct watchdog_info wdt_info = {
+       .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
+       .identity = MODULE_NAME,
 };
 
-static struct miscdevice sp805_wdt_miscdev = {
-       .minor = WATCHDOG_MINOR,
-       .name = "watchdog",
-       .fops = &sp805_wdt_fops,
+static const struct watchdog_ops wdt_ops = {
+       .owner          = THIS_MODULE,
+       .start          = wdt_enable,
+       .stop           = wdt_disable,
+       .ping           = wdt_ping,
+       .set_timeout    = wdt_setload,
+       .get_timeleft   = wdt_timeleft,
 };
 
 static int __devinit
 sp805_wdt_probe(struct amba_device *adev, const struct amba_id *id)
 {
+       struct sp805_wdt *wdt;
        int ret = 0;
 
        if (!devm_request_mem_region(&adev->dev, adev->res.start,
@@ -315,19 +246,26 @@ sp805_wdt_probe(struct amba_device *adev, const struct amba_id *id)
        }
 
        wdt->adev = adev;
+       wdt->wdd.info = &wdt_info;
+       wdt->wdd.ops = &wdt_ops;
+
        spin_lock_init(&wdt->lock);
-       wdt_setload(DEFAULT_TIMEOUT);
+       watchdog_set_nowayout(&wdt->wdd, nowayout);
+       watchdog_set_drvdata(&wdt->wdd, wdt);
+       wdt_setload(&wdt->wdd, DEFAULT_TIMEOUT);
 
-       ret = misc_register(&sp805_wdt_miscdev);
-       if (ret < 0) {
-               dev_warn(&adev->dev, "cannot register misc device\n");
-               goto err_misc_register;
+       ret = watchdog_register_device(&wdt->wdd);
+       if (ret) {
+               dev_err(&adev->dev, "watchdog_register_device() failed: %d\n",
+                               ret);
+               goto err_register;
        }
+       amba_set_drvdata(adev, wdt);
 
        dev_info(&adev->dev, "registration successful\n");
        return 0;
 
-err_misc_register:
+err_register:
        clk_put(wdt->clk);
 err:
        dev_err(&adev->dev, "Probe Failed!!!\n");
@@ -336,7 +274,11 @@ err:
 
 static int __devexit sp805_wdt_remove(struct amba_device *adev)
 {
-       misc_deregister(&sp805_wdt_miscdev);
+       struct sp805_wdt *wdt = amba_get_drvdata(adev);
+
+       watchdog_unregister_device(&wdt->wdd);
+       amba_set_drvdata(adev, NULL);
+       watchdog_set_drvdata(&wdt->wdd, NULL);
        clk_put(wdt->clk);
 
        return 0;
@@ -345,28 +287,22 @@ static int __devexit sp805_wdt_remove(struct amba_device *adev)
 #ifdef CONFIG_PM
 static int sp805_wdt_suspend(struct device *dev)
 {
-       if (test_bit(WDT_BUSY, &wdt->status)) {
-               wdt_disable();
-               clk_disable(wdt->clk);
-       }
+       struct sp805_wdt *wdt = dev_get_drvdata(dev);
+
+       if (watchdog_active(&wdt->wdd))
+               return wdt_disable(&wdt->wdd);
 
        return 0;
 }
 
 static int sp805_wdt_resume(struct device *dev)
 {
-       int ret = 0;
+       struct sp805_wdt *wdt = dev_get_drvdata(dev);
 
-       if (test_bit(WDT_BUSY, &wdt->status)) {
-               ret = clk_enable(wdt->clk);
-               if (ret) {
-                       dev_err(dev, "clock enable fail");
-                       return ret;
-               }
-               wdt_enable();
-       }
+       if (watchdog_active(&wdt->wdd))
+               return wdt_enable(&wdt->wdd);
 
-       return ret;
+       return 0;
 }
 #endif /* CONFIG_PM */
 
@@ -395,11 +331,6 @@ static struct amba_driver sp805_wdt_driver = {
 
 module_amba_driver(sp805_wdt_driver);
 
-module_param(nowayout, bool, 0);
-MODULE_PARM_DESC(nowayout,
-               "Set to 1 to keep watchdog running after device release");
-
 MODULE_AUTHOR("Viresh Kumar <viresh.kumar@st.com>");
 MODULE_DESCRIPTION("ARM SP805 Watchdog Driver");
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
index 5603e31afdab03e8d67ad97151f772b879dd015e..aa50da3ccfe3678f8a740eab41abcb61e7beefef 100644 (file)
@@ -91,7 +91,7 @@ static inline void wdt_reset(void)
 static void wdt_timer_tick(unsigned long data)
 {
        if (time_before(jiffies, next_heartbeat) ||
-          (!test_bit(WDOG_ACTIVE, &wdt_dev.status))) {
+          (!watchdog_active(&wdt_dev))) {
                wdt_reset();
                mod_timer(&timer, jiffies + WDT_HEARTBEAT);
        } else
index 14d768bfa267d78923a467484ed1f66275471bb2..6aa46a90ff028691f97627765f593d864bc885a1 100644 (file)
 #include <linux/kernel.h>      /* For printk/panic/... */
 #include <linux/watchdog.h>    /* For watchdog specific items */
 #include <linux/init.h>                /* For __init/__exit/... */
+#include <linux/idr.h>         /* For ida_* macros */
+#include <linux/err.h>         /* For IS_ERR macros */
 
-#include "watchdog_dev.h"      /* For watchdog_dev_register/... */
+#include "watchdog_core.h"     /* For watchdog_dev_register/... */
+
+static DEFINE_IDA(watchdog_ida);
+static struct class *watchdog_class;
 
 /**
  * watchdog_register_device() - register a watchdog device
@@ -49,7 +54,7 @@
  */
 int watchdog_register_device(struct watchdog_device *wdd)
 {
-       int ret;
+       int ret, id, devno;
 
        if (wdd == NULL || wdd->info == NULL || wdd->ops == NULL)
                return -EINVAL;
@@ -74,10 +79,38 @@ int watchdog_register_device(struct watchdog_device *wdd)
         * corrupted in a later stage then we expect a kernel panic!
         */
 
-       /* We only support 1 watchdog device via the /dev/watchdog interface */
+       mutex_init(&wdd->lock);
+       id = ida_simple_get(&watchdog_ida, 0, MAX_DOGS, GFP_KERNEL);
+       if (id < 0)
+               return id;
+       wdd->id = id;
+
        ret = watchdog_dev_register(wdd);
        if (ret) {
-               pr_err("error registering /dev/watchdog (err=%d)\n", ret);
+               ida_simple_remove(&watchdog_ida, id);
+               if (!(id == 0 && ret == -EBUSY))
+                       return ret;
+
+               /* Retry in case a legacy watchdog module exists */
+               id = ida_simple_get(&watchdog_ida, 1, MAX_DOGS, GFP_KERNEL);
+               if (id < 0)
+                       return id;
+               wdd->id = id;
+
+               ret = watchdog_dev_register(wdd);
+               if (ret) {
+                       ida_simple_remove(&watchdog_ida, id);
+                       return ret;
+               }
+       }
+
+       devno = wdd->cdev.dev;
+       wdd->dev = device_create(watchdog_class, wdd->parent, devno,
+                                       NULL, "watchdog%d", wdd->id);
+       if (IS_ERR(wdd->dev)) {
+               watchdog_dev_unregister(wdd);
+               ida_simple_remove(&watchdog_ida, id);
+               ret = PTR_ERR(wdd->dev);
                return ret;
        }
 
@@ -95,6 +128,7 @@ EXPORT_SYMBOL_GPL(watchdog_register_device);
 void watchdog_unregister_device(struct watchdog_device *wdd)
 {
        int ret;
+       int devno = wdd->cdev.dev;
 
        if (wdd == NULL)
                return;
@@ -102,9 +136,41 @@ void watchdog_unregister_device(struct watchdog_device *wdd)
        ret = watchdog_dev_unregister(wdd);
        if (ret)
                pr_err("error unregistering /dev/watchdog (err=%d)\n", ret);
+       device_destroy(watchdog_class, devno);
+       ida_simple_remove(&watchdog_ida, wdd->id);
+       wdd->dev = NULL;
 }
 EXPORT_SYMBOL_GPL(watchdog_unregister_device);
 
+static int __init watchdog_init(void)
+{
+       int err;
+
+       watchdog_class = class_create(THIS_MODULE, "watchdog");
+       if (IS_ERR(watchdog_class)) {
+               pr_err("couldn't create class\n");
+               return PTR_ERR(watchdog_class);
+       }
+
+       err = watchdog_dev_init();
+       if (err < 0) {
+               class_destroy(watchdog_class);
+               return err;
+       }
+
+       return 0;
+}
+
+static void __exit watchdog_exit(void)
+{
+       watchdog_dev_exit();
+       class_destroy(watchdog_class);
+       ida_destroy(&watchdog_ida);
+}
+
+subsys_initcall(watchdog_init);
+module_exit(watchdog_exit);
+
 MODULE_AUTHOR("Alan Cox <alan@lxorguk.ukuu.org.uk>");
 MODULE_AUTHOR("Wim Van Sebroeck <wim@iguana.be>");
 MODULE_DESCRIPTION("WatchDog Timer Driver Core");
diff --git a/drivers/watchdog/watchdog_core.h b/drivers/watchdog/watchdog_core.h
new file mode 100644 (file)
index 0000000..6c95141
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ *     watchdog_core.h
+ *
+ *     (c) Copyright 2008-2011 Alan Cox <alan@lxorguk.ukuu.org.uk>,
+ *                                             All Rights Reserved.
+ *
+ *     (c) Copyright 2008-2011 Wim Van Sebroeck <wim@iguana.be>.
+ *
+ *     This source code is part of the generic code that can be used
+ *     by all the watchdog timer drivers.
+ *
+ *     Based on source code of the following authors:
+ *       Matt Domsch <Matt_Domsch@dell.com>,
+ *       Rob Radez <rob@osinvestor.com>,
+ *       Rusty Lynch <rusty@linux.co.intel.com>
+ *       Satyam Sharma <satyam@infradead.org>
+ *       Randy Dunlap <randy.dunlap@oracle.com>
+ *
+ *     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.
+ *
+ *     Neither Alan Cox, CymruNet Ltd., Wim Van Sebroeck nor Iguana vzw.
+ *     admit liability nor provide warranty for any of this software.
+ *     This material is provided "AS-IS" and at no charge.
+ */
+
+#define MAX_DOGS       32      /* Maximum number of watchdog devices */
+
+/*
+ *     Functions/procedures to be called by the core
+ */
+extern int watchdog_dev_register(struct watchdog_device *);
+extern int watchdog_dev_unregister(struct watchdog_device *);
+extern int __init watchdog_dev_init(void);
+extern void __exit watchdog_dev_exit(void);
index 8558da912c42fd76c5bde4ddd55528ed0be48615..672d169bf1dacfa4ae0150da6109caeb266d0590 100644 (file)
 #include <linux/init.h>                /* For __init/__exit/... */
 #include <linux/uaccess.h>     /* For copy_to_user/put_user/... */
 
-/* make sure we only register one /dev/watchdog device */
-static unsigned long watchdog_dev_busy;
+#include "watchdog_core.h"
+
+/* the dev_t structure to store the dynamically allocated watchdog devices */
+static dev_t watchdog_devt;
 /* the watchdog device behind /dev/watchdog */
-static struct watchdog_device *wdd;
+static struct watchdog_device *old_wdd;
 
 /*
  *     watchdog_ping: ping the watchdog.
@@ -59,13 +61,26 @@ static struct watchdog_device *wdd;
 
 static int watchdog_ping(struct watchdog_device *wddev)
 {
-       if (test_bit(WDOG_ACTIVE, &wddev->status)) {
-               if (wddev->ops->ping)
-                       return wddev->ops->ping(wddev);  /* ping the watchdog */
-               else
-                       return wddev->ops->start(wddev); /* restart watchdog */
+       int err = 0;
+
+       mutex_lock(&wddev->lock);
+
+       if (test_bit(WDOG_UNREGISTERED, &wddev->status)) {
+               err = -ENODEV;
+               goto out_ping;
        }
-       return 0;
+
+       if (!watchdog_active(wddev))
+               goto out_ping;
+
+       if (wddev->ops->ping)
+               err = wddev->ops->ping(wddev);  /* ping the watchdog */
+       else
+               err = wddev->ops->start(wddev); /* restart watchdog */
+
+out_ping:
+       mutex_unlock(&wddev->lock);
+       return err;
 }
 
 /*
@@ -79,16 +94,25 @@ static int watchdog_ping(struct watchdog_device *wddev)
 
 static int watchdog_start(struct watchdog_device *wddev)
 {
-       int err;
+       int err = 0;
 
-       if (!test_bit(WDOG_ACTIVE, &wddev->status)) {
-               err = wddev->ops->start(wddev);
-               if (err < 0)
-                       return err;
+       mutex_lock(&wddev->lock);
 
-               set_bit(WDOG_ACTIVE, &wddev->status);
+       if (test_bit(WDOG_UNREGISTERED, &wddev->status)) {
+               err = -ENODEV;
+               goto out_start;
        }
-       return 0;
+
+       if (watchdog_active(wddev))
+               goto out_start;
+
+       err = wddev->ops->start(wddev);
+       if (err == 0)
+               set_bit(WDOG_ACTIVE, &wddev->status);
+
+out_start:
+       mutex_unlock(&wddev->lock);
+       return err;
 }
 
 /*
@@ -103,22 +127,155 @@ static int watchdog_start(struct watchdog_device *wddev)
 
 static int watchdog_stop(struct watchdog_device *wddev)
 {
-       int err = -EBUSY;
+       int err = 0;
 
-       if (test_bit(WDOG_NO_WAY_OUT, &wddev->status)) {
-               pr_info("%s: nowayout prevents watchdog to be stopped!\n",
-                                                       wddev->info->identity);
-               return err;
+       mutex_lock(&wddev->lock);
+
+       if (test_bit(WDOG_UNREGISTERED, &wddev->status)) {
+               err = -ENODEV;
+               goto out_stop;
        }
 
-       if (test_bit(WDOG_ACTIVE, &wddev->status)) {
-               err = wddev->ops->stop(wddev);
-               if (err < 0)
-                       return err;
+       if (!watchdog_active(wddev))
+               goto out_stop;
 
+       if (test_bit(WDOG_NO_WAY_OUT, &wddev->status)) {
+               dev_info(wddev->dev, "nowayout prevents watchdog being stopped!\n");
+               err = -EBUSY;
+               goto out_stop;
+       }
+
+       err = wddev->ops->stop(wddev);
+       if (err == 0)
                clear_bit(WDOG_ACTIVE, &wddev->status);
+
+out_stop:
+       mutex_unlock(&wddev->lock);
+       return err;
+}
+
+/*
+ *     watchdog_get_status: wrapper to get the watchdog status
+ *     @wddev: the watchdog device to get the status from
+ *     @status: the status of the watchdog device
+ *
+ *     Get the watchdog's status flags.
+ */
+
+static int watchdog_get_status(struct watchdog_device *wddev,
+                                                       unsigned int *status)
+{
+       int err = 0;
+
+       *status = 0;
+       if (!wddev->ops->status)
+               return -EOPNOTSUPP;
+
+       mutex_lock(&wddev->lock);
+
+       if (test_bit(WDOG_UNREGISTERED, &wddev->status)) {
+               err = -ENODEV;
+               goto out_status;
        }
-       return 0;
+
+       *status = wddev->ops->status(wddev);
+
+out_status:
+       mutex_unlock(&wddev->lock);
+       return err;
+}
+
+/*
+ *     watchdog_set_timeout: set the watchdog timer timeout
+ *     @wddev: the watchdog device to set the timeout for
+ *     @timeout: timeout to set in seconds
+ */
+
+static int watchdog_set_timeout(struct watchdog_device *wddev,
+                                                       unsigned int timeout)
+{
+       int err;
+
+       if ((wddev->ops->set_timeout == NULL) ||
+           !(wddev->info->options & WDIOF_SETTIMEOUT))
+               return -EOPNOTSUPP;
+
+       if ((wddev->max_timeout != 0) &&
+           (timeout < wddev->min_timeout || timeout > wddev->max_timeout))
+               return -EINVAL;
+
+       mutex_lock(&wddev->lock);
+
+       if (test_bit(WDOG_UNREGISTERED, &wddev->status)) {
+               err = -ENODEV;
+               goto out_timeout;
+       }
+
+       err = wddev->ops->set_timeout(wddev, timeout);
+
+out_timeout:
+       mutex_unlock(&wddev->lock);
+       return err;
+}
+
+/*
+ *     watchdog_get_timeleft: wrapper to get the time left before a reboot
+ *     @wddev: the watchdog device to get the remaining time from
+ *     @timeleft: the time that's left
+ *
+ *     Get the time before a watchdog will reboot (if not pinged).
+ */
+
+static int watchdog_get_timeleft(struct watchdog_device *wddev,
+                                                       unsigned int *timeleft)
+{
+       int err = 0;
+
+       *timeleft = 0;
+       if (!wddev->ops->get_timeleft)
+               return -EOPNOTSUPP;
+
+       mutex_lock(&wddev->lock);
+
+       if (test_bit(WDOG_UNREGISTERED, &wddev->status)) {
+               err = -ENODEV;
+               goto out_timeleft;
+       }
+
+       *timeleft = wddev->ops->get_timeleft(wddev);
+
+out_timeleft:
+       mutex_unlock(&wddev->lock);
+       return err;
+}
+
+/*
+ *     watchdog_ioctl_op: call the watchdog drivers ioctl op if defined
+ *     @wddev: the watchdog device to do the ioctl on
+ *     @cmd: watchdog command
+ *     @arg: argument pointer
+ */
+
+static int watchdog_ioctl_op(struct watchdog_device *wddev, unsigned int cmd,
+                                                       unsigned long arg)
+{
+       int err;
+
+       if (!wddev->ops->ioctl)
+               return -ENOIOCTLCMD;
+
+       mutex_lock(&wddev->lock);
+
+       if (test_bit(WDOG_UNREGISTERED, &wddev->status)) {
+               err = -ENODEV;
+               goto out_ioctl;
+       }
+
+       err = wddev->ops->ioctl(wddev, cmd, arg);
+
+out_ioctl:
+       mutex_unlock(&wddev->lock);
+       return err;
 }
 
 /*
@@ -136,6 +293,7 @@ static int watchdog_stop(struct watchdog_device *wddev)
 static ssize_t watchdog_write(struct file *file, const char __user *data,
                                                size_t len, loff_t *ppos)
 {
+       struct watchdog_device *wdd = file->private_data;
        size_t i;
        char c;
 
@@ -175,23 +333,24 @@ static ssize_t watchdog_write(struct file *file, const char __user *data,
 static long watchdog_ioctl(struct file *file, unsigned int cmd,
                                                        unsigned long arg)
 {
+       struct watchdog_device *wdd = file->private_data;
        void __user *argp = (void __user *)arg;
        int __user *p = argp;
        unsigned int val;
        int err;
 
-       if (wdd->ops->ioctl) {
-               err = wdd->ops->ioctl(wdd, cmd, arg);
-               if (err != -ENOIOCTLCMD)
-                       return err;
-       }
+       err = watchdog_ioctl_op(wdd, cmd, arg);
+       if (err != -ENOIOCTLCMD)
+               return err;
 
        switch (cmd) {
        case WDIOC_GETSUPPORT:
                return copy_to_user(argp, wdd->info,
                        sizeof(struct watchdog_info)) ? -EFAULT : 0;
        case WDIOC_GETSTATUS:
-               val = wdd->ops->status ? wdd->ops->status(wdd) : 0;
+               err = watchdog_get_status(wdd, &val);
+               if (err)
+                       return err;
                return put_user(val, p);
        case WDIOC_GETBOOTSTATUS:
                return put_user(wdd->bootstatus, p);
@@ -215,15 +374,9 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd,
                watchdog_ping(wdd);
                return 0;
        case WDIOC_SETTIMEOUT:
-               if ((wdd->ops->set_timeout == NULL) ||
-                   !(wdd->info->options & WDIOF_SETTIMEOUT))
-                       return -EOPNOTSUPP;
                if (get_user(val, p))
                        return -EFAULT;
-               if ((wdd->max_timeout != 0) &&
-                   (val < wdd->min_timeout || val > wdd->max_timeout))
-                               return -EINVAL;
-               err = wdd->ops->set_timeout(wdd, val);
+               err = watchdog_set_timeout(wdd, val);
                if (err < 0)
                        return err;
                /* If the watchdog is active then we send a keepalive ping
@@ -237,21 +390,21 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd,
                        return -EOPNOTSUPP;
                return put_user(wdd->timeout, p);
        case WDIOC_GETTIMELEFT:
-               if (!wdd->ops->get_timeleft)
-                       return -EOPNOTSUPP;
-
-               return put_user(wdd->ops->get_timeleft(wdd), p);
+               err = watchdog_get_timeleft(wdd, &val);
+               if (err)
+                       return err;
+               return put_user(val, p);
        default:
                return -ENOTTY;
        }
 }
 
 /*
- *     watchdog_open: open the /dev/watchdog device.
+ *     watchdog_open: open the /dev/watchdog* devices.
  *     @inode: inode of device
  *     @file: file handle to device
  *
- *     When the /dev/watchdog device gets opened, we start the watchdog.
+ *     When the /dev/watchdog* device gets opened, we start the watchdog.
  *     Watch out: the /dev/watchdog device is single open, so we make sure
  *     it can only be opened once.
  */
@@ -259,6 +412,13 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd,
 static int watchdog_open(struct inode *inode, struct file *file)
 {
        int err = -EBUSY;
+       struct watchdog_device *wdd;
+
+       /* Get the corresponding watchdog device */
+       if (imajor(inode) == MISC_MAJOR)
+               wdd = old_wdd;
+       else
+               wdd = container_of(inode->i_cdev, struct watchdog_device, cdev);
 
        /* the watchdog is single open! */
        if (test_and_set_bit(WDOG_DEV_OPEN, &wdd->status))
@@ -275,6 +435,11 @@ static int watchdog_open(struct inode *inode, struct file *file)
        if (err < 0)
                goto out_mod;
 
+       file->private_data = wdd;
+
+       if (wdd->ops->ref)
+               wdd->ops->ref(wdd);
+
        /* dev/watchdog is a virtual (and thus non-seekable) filesystem */
        return nonseekable_open(inode, file);
 
@@ -286,9 +451,9 @@ out:
 }
 
 /*
- *      watchdog_release: release the /dev/watchdog device.
- *      @inode: inode of device
- *      @file: file handle to device
+ *     watchdog_release: release the watchdog device.
+ *     @inode: inode of device
+ *     @file: file handle to device
  *
  *     This is the code for when /dev/watchdog gets closed. We will only
  *     stop the watchdog when we have received the magic char (and nowayout
@@ -297,6 +462,7 @@ out:
 
 static int watchdog_release(struct inode *inode, struct file *file)
 {
+       struct watchdog_device *wdd = file->private_data;
        int err = -EBUSY;
 
        /*
@@ -310,7 +476,10 @@ static int watchdog_release(struct inode *inode, struct file *file)
 
        /* If the watchdog was not stopped, send a keepalive ping */
        if (err < 0) {
-               pr_crit("%s: watchdog did not stop!\n", wdd->info->identity);
+               mutex_lock(&wdd->lock);
+               if (!test_bit(WDOG_UNREGISTERED, &wdd->status))
+                       dev_crit(wdd->dev, "watchdog did not stop!\n");
+               mutex_unlock(&wdd->lock);
                watchdog_ping(wdd);
        }
 
@@ -320,6 +489,10 @@ static int watchdog_release(struct inode *inode, struct file *file)
        /* make sure that /dev/watchdog can be re-opened */
        clear_bit(WDOG_DEV_OPEN, &wdd->status);
 
+       /* Note wdd may be gone after this, do not use after this! */
+       if (wdd->ops->unref)
+               wdd->ops->unref(wdd);
+
        return 0;
 }
 
@@ -338,62 +511,92 @@ static struct miscdevice watchdog_miscdev = {
 };
 
 /*
- *     watchdog_dev_register:
+ *     watchdog_dev_register: register a watchdog device
  *     @watchdog: watchdog device
  *
- *     Register a watchdog device as /dev/watchdog. /dev/watchdog
- *     is actually a miscdevice and thus we set it up like that.
+ *     Register a watchdog device including handling the legacy
+ *     /dev/watchdog node. /dev/watchdog is actually a miscdevice and
+ *     thus we set it up like that.
  */
 
 int watchdog_dev_register(struct watchdog_device *watchdog)
 {
-       int err;
-
-       /* Only one device can register for /dev/watchdog */
-       if (test_and_set_bit(0, &watchdog_dev_busy)) {
-               pr_err("only one watchdog can use /dev/watchdog\n");
-               return -EBUSY;
+       int err, devno;
+
+       if (watchdog->id == 0) {
+               watchdog_miscdev.parent = watchdog->parent;
+               err = misc_register(&watchdog_miscdev);
+               if (err != 0) {
+                       pr_err("%s: cannot register miscdev on minor=%d (err=%d).\n",
+                               watchdog->info->identity, WATCHDOG_MINOR, err);
+                       if (err == -EBUSY)
+                               pr_err("%s: a legacy watchdog module is probably present.\n",
+                                       watchdog->info->identity);
+                       return err;
+               }
+               old_wdd = watchdog;
        }
 
-       wdd = watchdog;
-
-       err = misc_register(&watchdog_miscdev);
-       if (err != 0) {
-               pr_err("%s: cannot register miscdev on minor=%d (err=%d)\n",
-                      watchdog->info->identity, WATCHDOG_MINOR, err);
-               goto out;
+       /* Fill in the data structures */
+       devno = MKDEV(MAJOR(watchdog_devt), watchdog->id);
+       cdev_init(&watchdog->cdev, &watchdog_fops);
+       watchdog->cdev.owner = watchdog->ops->owner;
+
+       /* Add the device */
+       err  = cdev_add(&watchdog->cdev, devno, 1);
+       if (err) {
+               pr_err("watchdog%d unable to add device %d:%d\n",
+                       watchdog->id,  MAJOR(watchdog_devt), watchdog->id);
+               if (watchdog->id == 0) {
+                       misc_deregister(&watchdog_miscdev);
+                       old_wdd = NULL;
+               }
        }
-
-       return 0;
-
-out:
-       wdd = NULL;
-       clear_bit(0, &watchdog_dev_busy);
        return err;
 }
 
 /*
- *     watchdog_dev_unregister:
+ *     watchdog_dev_unregister: unregister a watchdog device
  *     @watchdog: watchdog device
  *
- *     Deregister the /dev/watchdog device.
+ *     Unregister the watchdog and if needed the legacy /dev/watchdog device.
  */
 
 int watchdog_dev_unregister(struct watchdog_device *watchdog)
 {
-       /* Check that a watchdog device was registered in the past */
-       if (!test_bit(0, &watchdog_dev_busy) || !wdd)
-               return -ENODEV;
-
-       /* We can only unregister the watchdog device that was registered */
-       if (watchdog != wdd) {
-               pr_err("%s: watchdog was not registered as /dev/watchdog\n",
-                      watchdog->info->identity);
-               return -ENODEV;
+       mutex_lock(&watchdog->lock);
+       set_bit(WDOG_UNREGISTERED, &watchdog->status);
+       mutex_unlock(&watchdog->lock);
+
+       cdev_del(&watchdog->cdev);
+       if (watchdog->id == 0) {
+               misc_deregister(&watchdog_miscdev);
+               old_wdd = NULL;
        }
-
-       misc_deregister(&watchdog_miscdev);
-       wdd = NULL;
-       clear_bit(0, &watchdog_dev_busy);
        return 0;
 }
+
+/*
+ *     watchdog_dev_init: init dev part of watchdog core
+ *
+ *     Allocate a range of chardev nodes to use for watchdog devices
+ */
+
+int __init watchdog_dev_init(void)
+{
+       int err = alloc_chrdev_region(&watchdog_devt, 0, MAX_DOGS, "watchdog");
+       if (err < 0)
+               pr_err("watchdog: unable to allocate char dev region\n");
+       return err;
+}
+
+/*
+ *     watchdog_dev_exit: exit dev part of watchdog core
+ *
+ *     Release the range of chardev nodes used for watchdog devices
+ */
+
+void __exit watchdog_dev_exit(void)
+{
+       unregister_chrdev_region(watchdog_devt, MAX_DOGS);
+}
diff --git a/drivers/watchdog/watchdog_dev.h b/drivers/watchdog/watchdog_dev.h
deleted file mode 100644 (file)
index bc7612b..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- *     watchdog_core.h
- *
- *     (c) Copyright 2008-2011 Alan Cox <alan@lxorguk.ukuu.org.uk>,
- *                                             All Rights Reserved.
- *
- *     (c) Copyright 2008-2011 Wim Van Sebroeck <wim@iguana.be>.
- *
- *     This source code is part of the generic code that can be used
- *     by all the watchdog timer drivers.
- *
- *     Based on source code of the following authors:
- *       Matt Domsch <Matt_Domsch@dell.com>,
- *       Rob Radez <rob@osinvestor.com>,
- *       Rusty Lynch <rusty@linux.co.intel.com>
- *       Satyam Sharma <satyam@infradead.org>
- *       Randy Dunlap <randy.dunlap@oracle.com>
- *
- *     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.
- *
- *     Neither Alan Cox, CymruNet Ltd., Wim Van Sebroeck nor Iguana vzw.
- *     admit liability nor provide warranty for any of this software.
- *     This material is provided "AS-IS" and at no charge.
- */
-
-/*
- *     Functions/procedures to be called by the core
- */
-int watchdog_dev_register(struct watchdog_device *);
-int watchdog_dev_unregister(struct watchdog_device *);
index 0b48579a9cd6066c170741913875ebbe716dce0c..7ff2569e17aeae423039f6c44e23b246c549e0b3 100644 (file)
@@ -29,6 +29,7 @@
 #include <acpi/acpi_drivers.h>
 #include <acpi/processor.h>
 
+#include <xen/xen.h>
 #include <xen/interface/platform.h>
 #include <asm/xen/hypercall.h>
 
index a1e6c990cd410efded55c826f03bc5db13839d75..e3dd2a1e2bfc18e47abae82bce7ee60238527c08 100644 (file)
@@ -68,24 +68,6 @@ static gid_t v9fs_get_fsgid_for_create(struct inode *dir_inode)
        return current_fsgid();
 }
 
-/**
- * v9fs_dentry_from_dir_inode - helper function to get the dentry from
- * dir inode.
- *
- */
-
-static struct dentry *v9fs_dentry_from_dir_inode(struct inode *inode)
-{
-       struct dentry *dentry;
-
-       spin_lock(&inode->i_lock);
-       /* Directory should have only one entry. */
-       BUG_ON(S_ISDIR(inode->i_mode) && !list_is_singular(&inode->i_dentry));
-       dentry = list_entry(inode->i_dentry.next, struct dentry, d_alias);
-       spin_unlock(&inode->i_lock);
-       return dentry;
-}
-
 static int v9fs_test_inode_dotl(struct inode *inode, void *data)
 {
        struct v9fs_inode *v9inode = V9FS_I(inode);
@@ -415,7 +397,7 @@ static int v9fs_vfs_mkdir_dotl(struct inode *dir,
        if (dir->i_mode & S_ISGID)
                omode |= S_ISGID;
 
-       dir_dentry = v9fs_dentry_from_dir_inode(dir);
+       dir_dentry = dentry->d_parent;
        dfid = v9fs_fid_lookup(dir_dentry);
        if (IS_ERR(dfid)) {
                err = PTR_ERR(dfid);
@@ -793,7 +775,7 @@ v9fs_vfs_link_dotl(struct dentry *old_dentry, struct inode *dir,
                 dir->i_ino, old_dentry->d_name.name, dentry->d_name.name);
 
        v9ses = v9fs_inode2v9ses(dir);
-       dir_dentry = v9fs_dentry_from_dir_inode(dir);
+       dir_dentry = dentry->d_parent;
        dfid = v9fs_fid_lookup(dir_dentry);
        if (IS_ERR(dfid))
                return PTR_ERR(dfid);
@@ -858,7 +840,7 @@ v9fs_vfs_mknod_dotl(struct inode *dir, struct dentry *dentry, umode_t omode,
                return -EINVAL;
 
        v9ses = v9fs_inode2v9ses(dir);
-       dir_dentry = v9fs_dentry_from_dir_inode(dir);
+       dir_dentry = dentry->d_parent;
        dfid = v9fs_fid_lookup(dir_dentry);
        if (IS_ERR(dfid)) {
                err = PTR_ERR(dfid);
index 45a0ce45d7b46afa94b1290511bc1f91a9872137..1fceb320d2f22c16bc1a900cb27597d68977dbbd 100644 (file)
 #define AFFS_GET_HASHENTRY(data,hashkey) be32_to_cpu(((struct dir_front *)data)->hashtable[hashkey])
 #define AFFS_BLOCK(sb, bh, blk)                (AFFS_HEAD(bh)->table[AFFS_SB(sb)->s_hashsize-1-(blk)])
 
-#ifdef __LITTLE_ENDIAN
-#define BO_EXBITS      0x18UL
-#elif defined(__BIG_ENDIAN)
-#define BO_EXBITS      0x00UL
-#else
-#error Endianness must be known for affs to work.
-#endif
-
 #define AFFS_HEAD(bh)          ((struct affs_head *)(bh)->b_data)
 #define AFFS_TAIL(sb, bh)      ((struct affs_tail *)((bh)->b_data+(sb)->s_blocksize-sizeof(struct affs_tail)))
 #define AFFS_ROOT_HEAD(bh)     ((struct affs_root_head *)(bh)->b_data)
index e7f2fad7b4ce7cae2d334456f5d9998e795c917e..55c4c76560537f7fe72d6ff5f429eff666b86789 100644 (file)
--- a/fs/aio.c
+++ b/fs/aio.c
@@ -134,9 +134,9 @@ static int aio_setup_ring(struct kioctx *ctx)
        info->mmap_size = nr_pages * PAGE_SIZE;
        dprintk("attempting mmap of %lu bytes\n", info->mmap_size);
        down_write(&ctx->mm->mmap_sem);
-       info->mmap_base = do_mmap(NULL, 0, info->mmap_size, 
-                                 PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE,
-                                 0);
+       info->mmap_base = do_mmap_pgoff(NULL, 0, info->mmap_size, 
+                                       PROT_READ|PROT_WRITE,
+                                       MAP_ANONYMOUS|MAP_PRIVATE, 0);
        if (IS_ERR((void *)info->mmap_base)) {
                up_write(&ctx->mm->mmap_sem);
                info->mmap_size = 0;
@@ -1446,13 +1446,13 @@ static ssize_t aio_setup_vectored_rw(int type, struct kiocb *kiocb, bool compat)
                ret = compat_rw_copy_check_uvector(type,
                                (struct compat_iovec __user *)kiocb->ki_buf,
                                kiocb->ki_nbytes, 1, &kiocb->ki_inline_vec,
-                               &kiocb->ki_iovec, 1);
+                               &kiocb->ki_iovec);
        else
 #endif
                ret = rw_copy_check_uvector(type,
                                (struct iovec __user *)kiocb->ki_buf,
                                kiocb->ki_nbytes, 1, &kiocb->ki_inline_vec,
-                               &kiocb->ki_iovec, 1);
+                               &kiocb->ki_iovec);
        if (ret < 0)
                goto out;
 
index 584620e5dee52b5be4a456fb0572a5227a0ef534..0da90951d2776f827a905337938399ada79e8e69 100644 (file)
--- a/fs/attr.c
+++ b/fs/attr.c
@@ -176,6 +176,11 @@ int notify_change(struct dentry * dentry, struct iattr * attr)
                        return -EPERM;
        }
 
+       if ((ia_valid & ATTR_SIZE) && IS_I_VERSION(inode)) {
+               if (attr->ia_size != inode->i_size)
+                       inode_inc_iversion(inode);
+       }
+
        if ((ia_valid & ATTR_MODE)) {
                umode_t amode = attr->ia_mode;
                /* Flag setting protected by i_mutex */
index 37268c5bb98b2f2061a2e18134ebcdec0544fd9a..1b35d6bd06b06d071f8b24e28ece1aa97d87d1ce 100644 (file)
@@ -292,7 +292,6 @@ static const struct inode_operations bad_inode_ops =
        .getxattr       = bad_inode_getxattr,
        .listxattr      = bad_inode_listxattr,
        .removexattr    = bad_inode_removexattr,
-       /* truncate_range returns void */
 };
 
 
index e658dd134b95fb375b371a931e739baa95d249a8..1b52956afe33ab07889c3963ce2c41b32133483b 100644 (file)
@@ -329,7 +329,6 @@ static unsigned long elf_map(struct file *filep, unsigned long addr,
        if (!size)
                return addr;
 
-       down_write(&current->mm->mmap_sem);
        /*
        * total_size is the size of the ELF (interpreter) image.
        * The _first_ mmap needs to know the full size, otherwise
@@ -340,13 +339,12 @@ static unsigned long elf_map(struct file *filep, unsigned long addr,
        */
        if (total_size) {
                total_size = ELF_PAGEALIGN(total_size);
-               map_addr = do_mmap(filep, addr, total_size, prot, type, off);
+               map_addr = vm_mmap(filep, addr, total_size, prot, type, off);
                if (!BAD_ADDR(map_addr))
-                       do_munmap(current->mm, map_addr+size, total_size-size);
+                       vm_munmap(map_addr+size, total_size-size);
        } else
-               map_addr = do_mmap(filep, addr, size, prot, type, off);
+               map_addr = vm_mmap(filep, addr, size, prot, type, off);
 
-       up_write(&current->mm->mmap_sem);
        return(map_addr);
 }
 
index 6b2daf99fab8bcd91d314f0abd951b8472a092d2..178cb70acc26de80ec3db21a8455e88b7fc0360b 100644 (file)
@@ -562,7 +562,7 @@ static int load_flat_file(struct linux_binprm * bprm,
                                realdatastart = (unsigned long) -ENOMEM;
                        printk("Unable to allocate RAM for process data, errno %d\n",
                                        (int)-realdatastart);
-                       do_munmap(current->mm, textpos, text_len);
+                       vm_munmap(textpos, text_len);
                        ret = realdatastart;
                        goto err;
                }
@@ -586,8 +586,8 @@ static int load_flat_file(struct linux_binprm * bprm,
                }
                if (IS_ERR_VALUE(result)) {
                        printk("Unable to read data+bss, errno %d\n", (int)-result);
-                       do_munmap(current->mm, textpos, text_len);
-                       do_munmap(current->mm, realdatastart, len);
+                       vm_munmap(textpos, text_len);
+                       vm_munmap(realdatastart, len);
                        ret = result;
                        goto err;
                }
@@ -654,7 +654,7 @@ static int load_flat_file(struct linux_binprm * bprm,
                }
                if (IS_ERR_VALUE(result)) {
                        printk("Unable to read code+data+bss, errno %d\n",(int)-result);
-                       do_munmap(current->mm, textpos, text_len + data_len + extra +
+                       vm_munmap(textpos, text_len + data_len + extra +
                                MAX_SHARED_LIBS * sizeof(unsigned long));
                        ret = result;
                        goto err;
index 84da88539046fa21a6db865454db135df40be080..73922abba832d0a41c1cb01bbf5bcc0fec1cb78b 100644 (file)
--- a/fs/bio.c
+++ b/fs/bio.c
 #include <linux/swap.h>
 #include <linux/bio.h>
 #include <linux/blkdev.h>
+#include <linux/iocontext.h>
 #include <linux/slab.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/export.h>
 #include <linux/mempool.h>
 #include <linux/workqueue.h>
+#include <linux/cgroup.h>
 #include <scsi/sg.h>           /* for struct sg_iovec */
 
 #include <trace/events/block.h>
@@ -418,6 +420,7 @@ void bio_put(struct bio *bio)
         * last put frees it
         */
        if (atomic_dec_and_test(&bio->bi_cnt)) {
+               bio_disassociate_task(bio);
                bio->bi_next = NULL;
                bio->bi_destructor(bio);
        }
@@ -1646,6 +1649,64 @@ bad:
 }
 EXPORT_SYMBOL(bioset_create);
 
+#ifdef CONFIG_BLK_CGROUP
+/**
+ * bio_associate_current - associate a bio with %current
+ * @bio: target bio
+ *
+ * Associate @bio with %current if it hasn't been associated yet.  Block
+ * layer will treat @bio as if it were issued by %current no matter which
+ * task actually issues it.
+ *
+ * This function takes an extra reference of @task's io_context and blkcg
+ * which will be put when @bio is released.  The caller must own @bio,
+ * ensure %current->io_context exists, and is responsible for synchronizing
+ * calls to this function.
+ */
+int bio_associate_current(struct bio *bio)
+{
+       struct io_context *ioc;
+       struct cgroup_subsys_state *css;
+
+       if (bio->bi_ioc)
+               return -EBUSY;
+
+       ioc = current->io_context;
+       if (!ioc)
+               return -ENOENT;
+
+       /* acquire active ref on @ioc and associate */
+       get_io_context_active(ioc);
+       bio->bi_ioc = ioc;
+
+       /* associate blkcg if exists */
+       rcu_read_lock();
+       css = task_subsys_state(current, blkio_subsys_id);
+       if (css && css_tryget(css))
+               bio->bi_css = css;
+       rcu_read_unlock();
+
+       return 0;
+}
+
+/**
+ * bio_disassociate_task - undo bio_associate_current()
+ * @bio: target bio
+ */
+void bio_disassociate_task(struct bio *bio)
+{
+       if (bio->bi_ioc) {
+               put_io_context(bio->bi_ioc);
+               bio->bi_ioc = NULL;
+       }
+       if (bio->bi_css) {
+               css_put(bio->bi_css);
+               bio->bi_css = NULL;
+       }
+}
+
+#endif /* CONFIG_BLK_CGROUP */
+
 static void __init biovec_init_slabs(void)
 {
        int i;
index 89b156d85d63c9f29b66413e1558e85a758d0e12..761e2cd8fed16e6046951e50504b8bb9e7acd3e4 100644 (file)
@@ -227,7 +227,11 @@ int btrfs_init_acl(struct btrfs_trans_handle *trans,
                if (ret > 0) {
                        /* we need an acl */
                        ret = btrfs_set_acl(trans, inode, acl, ACL_TYPE_ACCESS);
+               } else {
+                       cache_no_acl(inode);
                }
+       } else {
+               cache_no_acl(inode);
        }
 failed:
        posix_acl_release(acl);
index bcec06750232e6cc3de09c62648201547709222b..3f75895c919bcc3b80ae63ab1fca42dcf335f95b 100644 (file)
 #include "delayed-ref.h"
 #include "locking.h"
 
+struct extent_inode_elem {
+       u64 inum;
+       u64 offset;
+       struct extent_inode_elem *next;
+};
+
+static int check_extent_in_eb(struct btrfs_key *key, struct extent_buffer *eb,
+                               struct btrfs_file_extent_item *fi,
+                               u64 extent_item_pos,
+                               struct extent_inode_elem **eie)
+{
+       u64 data_offset;
+       u64 data_len;
+       struct extent_inode_elem *e;
+
+       data_offset = btrfs_file_extent_offset(eb, fi);
+       data_len = btrfs_file_extent_num_bytes(eb, fi);
+
+       if (extent_item_pos < data_offset ||
+           extent_item_pos >= data_offset + data_len)
+               return 1;
+
+       e = kmalloc(sizeof(*e), GFP_NOFS);
+       if (!e)
+               return -ENOMEM;
+
+       e->next = *eie;
+       e->inum = key->objectid;
+       e->offset = key->offset + (extent_item_pos - data_offset);
+       *eie = e;
+
+       return 0;
+}
+
+static int find_extent_in_eb(struct extent_buffer *eb, u64 wanted_disk_byte,
+                               u64 extent_item_pos,
+                               struct extent_inode_elem **eie)
+{
+       u64 disk_byte;
+       struct btrfs_key key;
+       struct btrfs_file_extent_item *fi;
+       int slot;
+       int nritems;
+       int extent_type;
+       int ret;
+
+       /*
+        * from the shared data ref, we only have the leaf but we need
+        * the key. thus, we must look into all items and see that we
+        * find one (some) with a reference to our extent item.
+        */
+       nritems = btrfs_header_nritems(eb);
+       for (slot = 0; slot < nritems; ++slot) {
+               btrfs_item_key_to_cpu(eb, &key, slot);
+               if (key.type != BTRFS_EXTENT_DATA_KEY)
+                       continue;
+               fi = btrfs_item_ptr(eb, slot, struct btrfs_file_extent_item);
+               extent_type = btrfs_file_extent_type(eb, fi);
+               if (extent_type == BTRFS_FILE_EXTENT_INLINE)
+                       continue;
+               /* don't skip BTRFS_FILE_EXTENT_PREALLOC, we can handle that */
+               disk_byte = btrfs_file_extent_disk_bytenr(eb, fi);
+               if (disk_byte != wanted_disk_byte)
+                       continue;
+
+               ret = check_extent_in_eb(&key, eb, fi, extent_item_pos, eie);
+               if (ret < 0)
+                       return ret;
+       }
+
+       return 0;
+}
+
 /*
  * this structure records all encountered refs on the way up to the root
  */
 struct __prelim_ref {
        struct list_head list;
        u64 root_id;
-       struct btrfs_key key;
+       struct btrfs_key key_for_search;
        int level;
        int count;
+       struct extent_inode_elem *inode_list;
        u64 parent;
        u64 wanted_disk_byte;
 };
 
+/*
+ * the rules for all callers of this function are:
+ * - obtaining the parent is the goal
+ * - if you add a key, you must know that it is a correct key
+ * - if you cannot add the parent or a correct key, then we will look into the
+ *   block later to set a correct key
+ *
+ * delayed refs
+ * ============
+ *        backref type | shared | indirect | shared | indirect
+ * information         |   tree |     tree |   data |     data
+ * --------------------+--------+----------+--------+----------
+ *      parent logical |    y   |     -    |    -   |     -
+ *      key to resolve |    -   |     y    |    y   |     y
+ *  tree block logical |    -   |     -    |    -   |     -
+ *  root for resolving |    y   |     y    |    y   |     y
+ *
+ * - column 1:       we've the parent -> done
+ * - column 2, 3, 4: we use the key to find the parent
+ *
+ * on disk refs (inline or keyed)
+ * ==============================
+ *        backref type | shared | indirect | shared | indirect
+ * information         |   tree |     tree |   data |     data
+ * --------------------+--------+----------+--------+----------
+ *      parent logical |    y   |     -    |    y   |     -
+ *      key to resolve |    -   |     -    |    -   |     y
+ *  tree block logical |    y   |     y    |    y   |     y
+ *  root for resolving |    -   |     y    |    y   |     y
+ *
+ * - column 1, 3: we've the parent -> done
+ * - column 2:    we take the first key from the block to find the parent
+ *                (see __add_missing_keys)
+ * - column 4:    we use the key to find the parent
+ *
+ * additional information that's available but not required to find the parent
+ * block might help in merging entries to gain some speed.
+ */
+
 static int __add_prelim_ref(struct list_head *head, u64 root_id,
-                           struct btrfs_key *key, int level, u64 parent,
-                           u64 wanted_disk_byte, int count)
+                           struct btrfs_key *key, int level,
+                           u64 parent, u64 wanted_disk_byte, int count)
 {
        struct __prelim_ref *ref;
 
@@ -50,10 +163,11 @@ static int __add_prelim_ref(struct list_head *head, u64 root_id,
 
        ref->root_id = root_id;
        if (key)
-               ref->key = *key;
+               ref->key_for_search = *key;
        else
-               memset(&ref->key, 0, sizeof(ref->key));
+               memset(&ref->key_for_search, 0, sizeof(ref->key_for_search));
 
+       ref->inode_list = NULL;
        ref->level = level;
        ref->count = count;
        ref->parent = parent;
@@ -64,18 +178,26 @@ static int __add_prelim_ref(struct list_head *head, u64 root_id,
 }
 
 static int add_all_parents(struct btrfs_root *root, struct btrfs_path *path,
-                               struct ulist *parents,
-                               struct extent_buffer *eb, int level,
-                               u64 wanted_objectid, u64 wanted_disk_byte)
+                               struct ulist *parents, int level,
+                               struct btrfs_key *key, u64 wanted_disk_byte,
+                               const u64 *extent_item_pos)
 {
        int ret;
-       int slot;
+       int slot = path->slots[level];
+       struct extent_buffer *eb = path->nodes[level];
        struct btrfs_file_extent_item *fi;
-       struct btrfs_key key;
+       struct extent_inode_elem *eie = NULL;
        u64 disk_byte;
+       u64 wanted_objectid = key->objectid;
 
 add_parent:
-       ret = ulist_add(parents, eb->start, 0, GFP_NOFS);
+       if (level == 0 && extent_item_pos) {
+               fi = btrfs_item_ptr(eb, slot, struct btrfs_file_extent_item);
+               ret = check_extent_in_eb(key, eb, fi, *extent_item_pos, &eie);
+               if (ret < 0)
+                       return ret;
+       }
+       ret = ulist_add(parents, eb->start, (unsigned long)eie, GFP_NOFS);
        if (ret < 0)
                return ret;
 
@@ -89,6 +211,7 @@ add_parent:
         * repeat this until we don't find any additional EXTENT_DATA items.
         */
        while (1) {
+               eie = NULL;
                ret = btrfs_next_leaf(root, path);
                if (ret < 0)
                        return ret;
@@ -97,9 +220,9 @@ add_parent:
 
                eb = path->nodes[0];
                for (slot = 0; slot < btrfs_header_nritems(eb); ++slot) {
-                       btrfs_item_key_to_cpu(eb, &key, slot);
-                       if (key.objectid != wanted_objectid ||
-                           key.type != BTRFS_EXTENT_DATA_KEY)
+                       btrfs_item_key_to_cpu(eb, key, slot);
+                       if (key->objectid != wanted_objectid ||
+                           key->type != BTRFS_EXTENT_DATA_KEY)
                                return 0;
                        fi = btrfs_item_ptr(eb, slot,
                                                struct btrfs_file_extent_item);
@@ -118,8 +241,10 @@ add_parent:
  */
 static int __resolve_indirect_ref(struct btrfs_fs_info *fs_info,
                                        int search_commit_root,
+                                       u64 time_seq,
                                        struct __prelim_ref *ref,
-                                       struct ulist *parents)
+                                       struct ulist *parents,
+                                       const u64 *extent_item_pos)
 {
        struct btrfs_path *path;
        struct btrfs_root *root;
@@ -152,12 +277,13 @@ static int __resolve_indirect_ref(struct btrfs_fs_info *fs_info,
                goto out;
 
        path->lowest_level = level;
-       ret = btrfs_search_slot(NULL, root, &ref->key, path, 0, 0);
+       ret = btrfs_search_old_slot(root, &ref->key_for_search, path, time_seq);
        pr_debug("search slot in root %llu (level %d, ref count %d) returned "
                 "%d for key (%llu %u %llu)\n",
                 (unsigned long long)ref->root_id, level, ref->count, ret,
-                (unsigned long long)ref->key.objectid, ref->key.type,
-                (unsigned long long)ref->key.offset);
+                (unsigned long long)ref->key_for_search.objectid,
+                ref->key_for_search.type,
+                (unsigned long long)ref->key_for_search.offset);
        if (ret < 0)
                goto out;
 
@@ -179,9 +305,8 @@ static int __resolve_indirect_ref(struct btrfs_fs_info *fs_info,
                btrfs_item_key_to_cpu(eb, &key, path->slots[0]);
        }
 
-       /* the last two parameters will only be used for level == 0 */
-       ret = add_all_parents(root, path, parents, eb, level, key.objectid,
-                               ref->wanted_disk_byte);
+       ret = add_all_parents(root, path, parents, level, &key,
+                               ref->wanted_disk_byte, extent_item_pos);
 out:
        btrfs_free_path(path);
        return ret;
@@ -191,8 +316,9 @@ out:
  * resolve all indirect backrefs from the list
  */
 static int __resolve_indirect_refs(struct btrfs_fs_info *fs_info,
-                                  int search_commit_root,
-                                  struct list_head *head)
+                                  int search_commit_root, u64 time_seq,
+                                  struct list_head *head,
+                                  const u64 *extent_item_pos)
 {
        int err;
        int ret = 0;
@@ -201,6 +327,7 @@ static int __resolve_indirect_refs(struct btrfs_fs_info *fs_info,
        struct __prelim_ref *new_ref;
        struct ulist *parents;
        struct ulist_node *node;
+       struct ulist_iterator uiter;
 
        parents = ulist_alloc(GFP_NOFS);
        if (!parents)
@@ -217,7 +344,8 @@ static int __resolve_indirect_refs(struct btrfs_fs_info *fs_info,
                if (ref->count == 0)
                        continue;
                err = __resolve_indirect_ref(fs_info, search_commit_root,
-                                            ref, parents);
+                                            time_seq, ref, parents,
+                                            extent_item_pos);
                if (err) {
                        if (ret == 0)
                                ret = err;
@@ -225,11 +353,14 @@ static int __resolve_indirect_refs(struct btrfs_fs_info *fs_info,
                }
 
                /* we put the first parent into the ref at hand */
-               node = ulist_next(parents, NULL);
+               ULIST_ITER_INIT(&uiter);
+               node = ulist_next(parents, &uiter);
                ref->parent = node ? node->val : 0;
+               ref->inode_list =
+                       node ? (struct extent_inode_elem *)node->aux : 0;
 
                /* additional parents require new refs being added here */
-               while ((node = ulist_next(parents, node))) {
+               while ((node = ulist_next(parents, &uiter))) {
                        new_ref = kmalloc(sizeof(*new_ref), GFP_NOFS);
                        if (!new_ref) {
                                ret = -ENOMEM;
@@ -237,6 +368,8 @@ static int __resolve_indirect_refs(struct btrfs_fs_info *fs_info,
                        }
                        memcpy(new_ref, ref, sizeof(*ref));
                        new_ref->parent = node->val;
+                       new_ref->inode_list =
+                                       (struct extent_inode_elem *)node->aux;
                        list_add(&new_ref->list, &ref->list);
                }
                ulist_reinit(parents);
@@ -246,10 +379,65 @@ static int __resolve_indirect_refs(struct btrfs_fs_info *fs_info,
        return ret;
 }
 
+static inline int ref_for_same_block(struct __prelim_ref *ref1,
+                                    struct __prelim_ref *ref2)
+{
+       if (ref1->level != ref2->level)
+               return 0;
+       if (ref1->root_id != ref2->root_id)
+               return 0;
+       if (ref1->key_for_search.type != ref2->key_for_search.type)
+               return 0;
+       if (ref1->key_for_search.objectid != ref2->key_for_search.objectid)
+               return 0;
+       if (ref1->key_for_search.offset != ref2->key_for_search.offset)
+               return 0;
+       if (ref1->parent != ref2->parent)
+               return 0;
+
+       return 1;
+}
+
+/*
+ * read tree blocks and add keys where required.
+ */
+static int __add_missing_keys(struct btrfs_fs_info *fs_info,
+                             struct list_head *head)
+{
+       struct list_head *pos;
+       struct extent_buffer *eb;
+
+       list_for_each(pos, head) {
+               struct __prelim_ref *ref;
+               ref = list_entry(pos, struct __prelim_ref, list);
+
+               if (ref->parent)
+                       continue;
+               if (ref->key_for_search.type)
+                       continue;
+               BUG_ON(!ref->wanted_disk_byte);
+               eb = read_tree_block(fs_info->tree_root, ref->wanted_disk_byte,
+                                    fs_info->tree_root->leafsize, 0);
+               BUG_ON(!eb);
+               btrfs_tree_read_lock(eb);
+               if (btrfs_header_level(eb) == 0)
+                       btrfs_item_key_to_cpu(eb, &ref->key_for_search, 0);
+               else
+                       btrfs_node_key_to_cpu(eb, &ref->key_for_search, 0);
+               btrfs_tree_read_unlock(eb);
+               free_extent_buffer(eb);
+       }
+       return 0;
+}
+
 /*
  * merge two lists of backrefs and adjust counts accordingly
  *
  * mode = 1: merge identical keys, if key is set
+ *    FIXME: if we add more keys in __add_prelim_ref, we can merge more here.
+ *           additionally, we could even add a key range for the blocks we
+ *           looked into to merge even more (-> replace unresolved refs by those
+ *           having a parent).
  * mode = 2: merge identical parents
  */
 static int __merge_refs(struct list_head *head, int mode)
@@ -263,20 +451,21 @@ static int __merge_refs(struct list_head *head, int mode)
 
                ref1 = list_entry(pos1, struct __prelim_ref, list);
 
-               if (mode == 1 && ref1->key.type == 0)
-                       continue;
                for (pos2 = pos1->next, n2 = pos2->next; pos2 != head;
                     pos2 = n2, n2 = pos2->next) {
                        struct __prelim_ref *ref2;
+                       struct __prelim_ref *xchg;
 
                        ref2 = list_entry(pos2, struct __prelim_ref, list);
 
                        if (mode == 1) {
-                               if (memcmp(&ref1->key, &ref2->key,
-                                          sizeof(ref1->key)) ||
-                                   ref1->level != ref2->level ||
-                                   ref1->root_id != ref2->root_id)
+                               if (!ref_for_same_block(ref1, ref2))
                                        continue;
+                               if (!ref1->parent && ref2->parent) {
+                                       xchg = ref1;
+                                       ref1 = ref2;
+                                       ref2 = xchg;
+                               }
                                ref1->count += ref2->count;
                        } else {
                                if (ref1->parent != ref2->parent)
@@ -296,16 +485,17 @@ static int __merge_refs(struct list_head *head, int mode)
  * smaller or equal that seq to the list
  */
 static int __add_delayed_refs(struct btrfs_delayed_ref_head *head, u64 seq,
-                             struct btrfs_key *info_key,
                              struct list_head *prefs)
 {
        struct btrfs_delayed_extent_op *extent_op = head->extent_op;
        struct rb_node *n = &head->node.rb_node;
+       struct btrfs_key key;
+       struct btrfs_key op_key = {0};
        int sgn;
        int ret = 0;
 
        if (extent_op && extent_op->update_key)
-               btrfs_disk_key_to_cpu(info_key, &extent_op->key);
+               btrfs_disk_key_to_cpu(&op_key, &extent_op->key);
 
        while ((n = rb_prev(n))) {
                struct btrfs_delayed_ref_node *node;
@@ -337,7 +527,7 @@ static int __add_delayed_refs(struct btrfs_delayed_ref_head *head, u64 seq,
                        struct btrfs_delayed_tree_ref *ref;
 
                        ref = btrfs_delayed_node_to_tree_ref(node);
-                       ret = __add_prelim_ref(prefs, ref->root, info_key,
+                       ret = __add_prelim_ref(prefs, ref->root, &op_key,
                                               ref->level + 1, 0, node->bytenr,
                                               node->ref_mod * sgn);
                        break;
@@ -346,7 +536,7 @@ static int __add_delayed_refs(struct btrfs_delayed_ref_head *head, u64 seq,
                        struct btrfs_delayed_tree_ref *ref;
 
                        ref = btrfs_delayed_node_to_tree_ref(node);
-                       ret = __add_prelim_ref(prefs, ref->root, info_key,
+                       ret = __add_prelim_ref(prefs, ref->root, NULL,
                                               ref->level + 1, ref->parent,
                                               node->bytenr,
                                               node->ref_mod * sgn);
@@ -354,8 +544,6 @@ static int __add_delayed_refs(struct btrfs_delayed_ref_head *head, u64 seq,
                }
                case BTRFS_EXTENT_DATA_REF_KEY: {
                        struct btrfs_delayed_data_ref *ref;
-                       struct btrfs_key key;
-
                        ref = btrfs_delayed_node_to_data_ref(node);
 
                        key.objectid = ref->objectid;
@@ -368,7 +556,6 @@ static int __add_delayed_refs(struct btrfs_delayed_ref_head *head, u64 seq,
                }
                case BTRFS_SHARED_DATA_REF_KEY: {
                        struct btrfs_delayed_data_ref *ref;
-                       struct btrfs_key key;
 
                        ref = btrfs_delayed_node_to_data_ref(node);
 
@@ -394,8 +581,7 @@ static int __add_delayed_refs(struct btrfs_delayed_ref_head *head, u64 seq,
  */
 static int __add_inline_refs(struct btrfs_fs_info *fs_info,
                             struct btrfs_path *path, u64 bytenr,
-                            struct btrfs_key *info_key, int *info_level,
-                            struct list_head *prefs)
+                            int *info_level, struct list_head *prefs)
 {
        int ret = 0;
        int slot;
@@ -411,7 +597,7 @@ static int __add_inline_refs(struct btrfs_fs_info *fs_info,
         * enumerate all inline refs
         */
        leaf = path->nodes[0];
-       slot = path->slots[0] - 1;
+       slot = path->slots[0];
 
        item_size = btrfs_item_size_nr(leaf, slot);
        BUG_ON(item_size < sizeof(*ei));
@@ -424,12 +610,9 @@ static int __add_inline_refs(struct btrfs_fs_info *fs_info,
 
        if (flags & BTRFS_EXTENT_FLAG_TREE_BLOCK) {
                struct btrfs_tree_block_info *info;
-               struct btrfs_disk_key disk_key;
 
                info = (struct btrfs_tree_block_info *)ptr;
                *info_level = btrfs_tree_block_level(leaf, info);
-               btrfs_tree_block_key(leaf, info, &disk_key);
-               btrfs_disk_key_to_cpu(info_key, &disk_key);
                ptr += sizeof(struct btrfs_tree_block_info);
                BUG_ON(ptr > end);
        } else {
@@ -447,7 +630,7 @@ static int __add_inline_refs(struct btrfs_fs_info *fs_info,
 
                switch (type) {
                case BTRFS_SHARED_BLOCK_REF_KEY:
-                       ret = __add_prelim_ref(prefs, 0, info_key,
+                       ret = __add_prelim_ref(prefs, 0, NULL,
                                                *info_level + 1, offset,
                                                bytenr, 1);
                        break;
@@ -462,8 +645,9 @@ static int __add_inline_refs(struct btrfs_fs_info *fs_info,
                        break;
                }
                case BTRFS_TREE_BLOCK_REF_KEY:
-                       ret = __add_prelim_ref(prefs, offset, info_key,
-                                              *info_level + 1, 0, bytenr, 1);
+                       ret = __add_prelim_ref(prefs, offset, NULL,
+                                              *info_level + 1, 0,
+                                              bytenr, 1);
                        break;
                case BTRFS_EXTENT_DATA_REF_KEY: {
                        struct btrfs_extent_data_ref *dref;
@@ -477,8 +661,8 @@ static int __add_inline_refs(struct btrfs_fs_info *fs_info,
                        key.type = BTRFS_EXTENT_DATA_KEY;
                        key.offset = btrfs_extent_data_ref_offset(leaf, dref);
                        root = btrfs_extent_data_ref_root(leaf, dref);
-                       ret = __add_prelim_ref(prefs, root, &key, 0, 0, bytenr,
-                                               count);
+                       ret = __add_prelim_ref(prefs, root, &key, 0, 0,
+                                              bytenr, count);
                        break;
                }
                default:
@@ -496,8 +680,7 @@ static int __add_inline_refs(struct btrfs_fs_info *fs_info,
  */
 static int __add_keyed_refs(struct btrfs_fs_info *fs_info,
                            struct btrfs_path *path, u64 bytenr,
-                           struct btrfs_key *info_key, int info_level,
-                           struct list_head *prefs)
+                           int info_level, struct list_head *prefs)
 {
        struct btrfs_root *extent_root = fs_info->extent_root;
        int ret;
@@ -527,7 +710,7 @@ static int __add_keyed_refs(struct btrfs_fs_info *fs_info,
 
                switch (key.type) {
                case BTRFS_SHARED_BLOCK_REF_KEY:
-                       ret = __add_prelim_ref(prefs, 0, info_key,
+                       ret = __add_prelim_ref(prefs, 0, NULL,
                                                info_level + 1, key.offset,
                                                bytenr, 1);
                        break;
@@ -543,8 +726,9 @@ static int __add_keyed_refs(struct btrfs_fs_info *fs_info,
                        break;
                }
                case BTRFS_TREE_BLOCK_REF_KEY:
-                       ret = __add_prelim_ref(prefs, key.offset, info_key,
-                                               info_level + 1, 0, bytenr, 1);
+                       ret = __add_prelim_ref(prefs, key.offset, NULL,
+                                              info_level + 1, 0,
+                                              bytenr, 1);
                        break;
                case BTRFS_EXTENT_DATA_REF_KEY: {
                        struct btrfs_extent_data_ref *dref;
@@ -560,7 +744,7 @@ static int __add_keyed_refs(struct btrfs_fs_info *fs_info,
                        key.offset = btrfs_extent_data_ref_offset(leaf, dref);
                        root = btrfs_extent_data_ref_root(leaf, dref);
                        ret = __add_prelim_ref(prefs, root, &key, 0, 0,
-                                               bytenr, count);
+                                              bytenr, count);
                        break;
                }
                default:
@@ -582,11 +766,12 @@ static int __add_keyed_refs(struct btrfs_fs_info *fs_info,
  */
 static int find_parent_nodes(struct btrfs_trans_handle *trans,
                             struct btrfs_fs_info *fs_info, u64 bytenr,
-                            u64 seq, struct ulist *refs, struct ulist *roots)
+                            u64 delayed_ref_seq, u64 time_seq,
+                            struct ulist *refs, struct ulist *roots,
+                            const u64 *extent_item_pos)
 {
        struct btrfs_key key;
        struct btrfs_path *path;
-       struct btrfs_key info_key = { 0 };
        struct btrfs_delayed_ref_root *delayed_refs = NULL;
        struct btrfs_delayed_ref_head *head;
        int info_level = 0;
@@ -645,7 +830,7 @@ again:
                                btrfs_put_delayed_ref(&head->node);
                                goto again;
                        }
-                       ret = __add_delayed_refs(head, seq, &info_key,
+                       ret = __add_delayed_refs(head, delayed_ref_seq,
                                                 &prefs_delayed);
                        if (ret) {
                                spin_unlock(&delayed_refs->lock);
@@ -659,16 +844,17 @@ again:
                struct extent_buffer *leaf;
                int slot;
 
+               path->slots[0]--;
                leaf = path->nodes[0];
-               slot = path->slots[0] - 1;
+               slot = path->slots[0];
                btrfs_item_key_to_cpu(leaf, &key, slot);
                if (key.objectid == bytenr &&
                    key.type == BTRFS_EXTENT_ITEM_KEY) {
                        ret = __add_inline_refs(fs_info, path, bytenr,
-                                               &info_key, &info_level, &prefs);
+                                               &info_level, &prefs);
                        if (ret)
                                goto out;
-                       ret = __add_keyed_refs(fs_info, path, bytenr, &info_key,
+                       ret = __add_keyed_refs(fs_info, path, bytenr,
                                               info_level, &prefs);
                        if (ret)
                                goto out;
@@ -676,21 +862,18 @@ again:
        }
        btrfs_release_path(path);
 
-       /*
-        * when adding the delayed refs above, the info_key might not have
-        * been known yet. Go over the list and replace the missing keys
-        */
-       list_for_each_entry(ref, &prefs_delayed, list) {
-               if ((ref->key.offset | ref->key.type | ref->key.objectid) == 0)
-                       memcpy(&ref->key, &info_key, sizeof(ref->key));
-       }
        list_splice_init(&prefs_delayed, &prefs);
 
+       ret = __add_missing_keys(fs_info, &prefs);
+       if (ret)
+               goto out;
+
        ret = __merge_refs(&prefs, 1);
        if (ret)
                goto out;
 
-       ret = __resolve_indirect_refs(fs_info, search_commit_root, &prefs);
+       ret = __resolve_indirect_refs(fs_info, search_commit_root, time_seq,
+                                     &prefs, extent_item_pos);
        if (ret)
                goto out;
 
@@ -709,7 +892,33 @@ again:
                        BUG_ON(ret < 0);
                }
                if (ref->count && ref->parent) {
-                       ret = ulist_add(refs, ref->parent, 0, GFP_NOFS);
+                       struct extent_inode_elem *eie = NULL;
+                       if (extent_item_pos && !ref->inode_list) {
+                               u32 bsz;
+                               struct extent_buffer *eb;
+                               bsz = btrfs_level_size(fs_info->extent_root,
+                                                       info_level);
+                               eb = read_tree_block(fs_info->extent_root,
+                                                          ref->parent, bsz, 0);
+                               BUG_ON(!eb);
+                               ret = find_extent_in_eb(eb, bytenr,
+                                                       *extent_item_pos, &eie);
+                               ref->inode_list = eie;
+                               free_extent_buffer(eb);
+                       }
+                       ret = ulist_add_merge(refs, ref->parent,
+                                             (unsigned long)ref->inode_list,
+                                             (unsigned long *)&eie, GFP_NOFS);
+                       if (!ret && extent_item_pos) {
+                               /*
+                                * we've recorded that parent, so we must extend
+                                * its inode list here
+                                */
+                               BUG_ON(!eie);
+                               while (eie->next)
+                                       eie = eie->next;
+                               eie->next = ref->inode_list;
+                       }
                        BUG_ON(ret < 0);
                }
                kfree(ref);
@@ -734,6 +943,28 @@ out:
        return ret;
 }
 
+static void free_leaf_list(struct ulist *blocks)
+{
+       struct ulist_node *node = NULL;
+       struct extent_inode_elem *eie;
+       struct extent_inode_elem *eie_next;
+       struct ulist_iterator uiter;
+
+       ULIST_ITER_INIT(&uiter);
+       while ((node = ulist_next(blocks, &uiter))) {
+               if (!node->aux)
+                       continue;
+               eie = (struct extent_inode_elem *)node->aux;
+               for (; eie; eie = eie_next) {
+                       eie_next = eie->next;
+                       kfree(eie);
+               }
+               node->aux = 0;
+       }
+
+       ulist_free(blocks);
+}
+
 /*
  * Finds all leafs with a reference to the specified combination of bytenr and
  * offset. key_list_head will point to a list of corresponding keys (caller must
@@ -744,7 +975,9 @@ out:
  */
 static int btrfs_find_all_leafs(struct btrfs_trans_handle *trans,
                                struct btrfs_fs_info *fs_info, u64 bytenr,
-                               u64 num_bytes, u64 seq, struct ulist **leafs)
+                               u64 delayed_ref_seq, u64 time_seq,
+                               struct ulist **leafs,
+                               const u64 *extent_item_pos)
 {
        struct ulist *tmp;
        int ret;
@@ -758,11 +991,12 @@ static int btrfs_find_all_leafs(struct btrfs_trans_handle *trans,
                return -ENOMEM;
        }
 
-       ret = find_parent_nodes(trans, fs_info, bytenr, seq, *leafs, tmp);
+       ret = find_parent_nodes(trans, fs_info, bytenr, delayed_ref_seq,
+                               time_seq, *leafs, tmp, extent_item_pos);
        ulist_free(tmp);
 
        if (ret < 0 && ret != -ENOENT) {
-               ulist_free(*leafs);
+               free_leaf_list(*leafs);
                return ret;
        }
 
@@ -784,10 +1018,12 @@ static int btrfs_find_all_leafs(struct btrfs_trans_handle *trans,
  */
 int btrfs_find_all_roots(struct btrfs_trans_handle *trans,
                                struct btrfs_fs_info *fs_info, u64 bytenr,
-                               u64 num_bytes, u64 seq, struct ulist **roots)
+                               u64 delayed_ref_seq, u64 time_seq,
+                               struct ulist **roots)
 {
        struct ulist *tmp;
        struct ulist_node *node = NULL;
+       struct ulist_iterator uiter;
        int ret;
 
        tmp = ulist_alloc(GFP_NOFS);
@@ -799,15 +1035,16 @@ int btrfs_find_all_roots(struct btrfs_trans_handle *trans,
                return -ENOMEM;
        }
 
+       ULIST_ITER_INIT(&uiter);
        while (1) {
-               ret = find_parent_nodes(trans, fs_info, bytenr, seq,
-                                       tmp, *roots);
+               ret = find_parent_nodes(trans, fs_info, bytenr, delayed_ref_seq,
+                                       time_seq, tmp, *roots, NULL);
                if (ret < 0 && ret != -ENOENT) {
                        ulist_free(tmp);
                        ulist_free(*roots);
                        return ret;
                }
-               node = ulist_next(tmp, node);
+               node = ulist_next(tmp, &uiter);
                if (!node)
                        break;
                bytenr = node->val;
@@ -1093,67 +1330,25 @@ int tree_backref_for_extent(unsigned long *ptr, struct extent_buffer *eb,
        return 0;
 }
 
-static int iterate_leaf_refs(struct btrfs_fs_info *fs_info, u64 logical,
-                               u64 orig_extent_item_objectid,
-                               u64 extent_item_pos, u64 root,
+static int iterate_leaf_refs(struct extent_inode_elem *inode_list,
+                               u64 root, u64 extent_item_objectid,
                                iterate_extent_inodes_t *iterate, void *ctx)
 {
-       u64 disk_byte;
-       struct btrfs_key key;
-       struct btrfs_file_extent_item *fi;
-       struct extent_buffer *eb;
-       int slot;
-       int nritems;
+       struct extent_inode_elem *eie;
        int ret = 0;
-       int extent_type;
-       u64 data_offset;
-       u64 data_len;
-
-       eb = read_tree_block(fs_info->tree_root, logical,
-                               fs_info->tree_root->leafsize, 0);
-       if (!eb)
-               return -EIO;
-
-       /*
-        * from the shared data ref, we only have the leaf but we need
-        * the key. thus, we must look into all items and see that we
-        * find one (some) with a reference to our extent item.
-        */
-       nritems = btrfs_header_nritems(eb);
-       for (slot = 0; slot < nritems; ++slot) {
-               btrfs_item_key_to_cpu(eb, &key, slot);
-               if (key.type != BTRFS_EXTENT_DATA_KEY)
-                       continue;
-               fi = btrfs_item_ptr(eb, slot, struct btrfs_file_extent_item);
-               extent_type = btrfs_file_extent_type(eb, fi);
-               if (extent_type == BTRFS_FILE_EXTENT_INLINE)
-                       continue;
-               /* don't skip BTRFS_FILE_EXTENT_PREALLOC, we can handle that */
-               disk_byte = btrfs_file_extent_disk_bytenr(eb, fi);
-               if (disk_byte != orig_extent_item_objectid)
-                       continue;
-
-               data_offset = btrfs_file_extent_offset(eb, fi);
-               data_len = btrfs_file_extent_num_bytes(eb, fi);
-
-               if (extent_item_pos < data_offset ||
-                   extent_item_pos >= data_offset + data_len)
-                       continue;
 
+       for (eie = inode_list; eie; eie = eie->next) {
                pr_debug("ref for %llu resolved, key (%llu EXTEND_DATA %llu), "
-                               "root %llu\n", orig_extent_item_objectid,
-                               key.objectid, key.offset, root);
-               ret = iterate(key.objectid,
-                               key.offset + (extent_item_pos - data_offset),
-                               root, ctx);
+                        "root %llu\n", extent_item_objectid,
+                        eie->inum, eie->offset, root);
+               ret = iterate(eie->inum, eie->offset, root, ctx);
                if (ret) {
-                       pr_debug("stopping iteration because ret=%d\n", ret);
+                       pr_debug("stopping iteration for %llu due to ret=%d\n",
+                                extent_item_objectid, ret);
                        break;
                }
        }
 
-       free_extent_buffer(eb);
-
        return ret;
 }
 
@@ -1175,7 +1370,10 @@ int iterate_extent_inodes(struct btrfs_fs_info *fs_info,
        struct ulist *roots = NULL;
        struct ulist_node *ref_node = NULL;
        struct ulist_node *root_node = NULL;
-       struct seq_list seq_elem;
+       struct seq_list seq_elem = {};
+       struct seq_list tree_mod_seq_elem = {};
+       struct ulist_iterator ref_uiter;
+       struct ulist_iterator root_uiter;
        struct btrfs_delayed_ref_root *delayed_refs = NULL;
 
        pr_debug("resolving all inodes for extent %llu\n",
@@ -1192,34 +1390,41 @@ int iterate_extent_inodes(struct btrfs_fs_info *fs_info,
                spin_lock(&delayed_refs->lock);
                btrfs_get_delayed_seq(delayed_refs, &seq_elem);
                spin_unlock(&delayed_refs->lock);
+               btrfs_get_tree_mod_seq(fs_info, &tree_mod_seq_elem);
        }
 
        ret = btrfs_find_all_leafs(trans, fs_info, extent_item_objectid,
-                                  extent_item_pos, seq_elem.seq,
-                                  &refs);
-
+                                  seq_elem.seq, tree_mod_seq_elem.seq, &refs,
+                                  &extent_item_pos);
        if (ret)
                goto out;
 
-       while (!ret && (ref_node = ulist_next(refs, ref_node))) {
-               ret = btrfs_find_all_roots(trans, fs_info, ref_node->val, -1,
-                                               seq_elem.seq, &roots);
+       ULIST_ITER_INIT(&ref_uiter);
+       while (!ret && (ref_node = ulist_next(refs, &ref_uiter))) {
+               ret = btrfs_find_all_roots(trans, fs_info, ref_node->val,
+                                               seq_elem.seq,
+                                               tree_mod_seq_elem.seq, &roots);
                if (ret)
                        break;
-               while (!ret && (root_node = ulist_next(roots, root_node))) {
-                       pr_debug("root %llu references leaf %llu\n",
-                                       root_node->val, ref_node->val);
-                       ret = iterate_leaf_refs(fs_info, ref_node->val,
-                                               extent_item_objectid,
-                                               extent_item_pos, root_node->val,
-                                               iterate, ctx);
+               ULIST_ITER_INIT(&root_uiter);
+               while (!ret && (root_node = ulist_next(roots, &root_uiter))) {
+                       pr_debug("root %llu references leaf %llu, data list "
+                                "%#lx\n", root_node->val, ref_node->val,
+                                ref_node->aux);
+                       ret = iterate_leaf_refs(
+                               (struct extent_inode_elem *)ref_node->aux,
+                               root_node->val, extent_item_objectid,
+                               iterate, ctx);
                }
+               ulist_free(roots);
+               roots = NULL;
        }
 
-       ulist_free(refs);
+       free_leaf_list(refs);
        ulist_free(roots);
 out:
        if (!search_commit_root) {
+               btrfs_put_tree_mod_seq(fs_info, &tree_mod_seq_elem);
                btrfs_put_delayed_seq(delayed_refs, &seq_elem);
                btrfs_end_transaction(trans, fs_info->extent_root);
        }
index 57ea2e959e4dcfaba89e4ee0b833f5744c3639d3..c18d8ac7b795da487c4a526979954e91cbddf52b 100644 (file)
@@ -58,7 +58,8 @@ int paths_from_inode(u64 inum, struct inode_fs_paths *ipath);
 
 int btrfs_find_all_roots(struct btrfs_trans_handle *trans,
                                struct btrfs_fs_info *fs_info, u64 bytenr,
-                               u64 num_bytes, u64 seq, struct ulist **roots);
+                               u64 delayed_ref_seq, u64 time_seq,
+                               struct ulist **roots);
 
 struct btrfs_data_container *init_data_container(u32 total_bytes);
 struct inode_fs_paths *init_ipath(s32 total_bytes, struct btrfs_root *fs_root,
index 9b9b15fd5204347c5ef2931fb186af679cb0d369..e616f8872e69bb0cf3b9a3f369ba49eeca57f2de 100644 (file)
 #include "ordered-data.h"
 #include "delayed-inode.h"
 
+/*
+ * ordered_data_close is set by truncate when a file that used
+ * to have good data has been truncated to zero.  When it is set
+ * the btrfs file release call will add this inode to the
+ * ordered operations list so that we make sure to flush out any
+ * new data the application may have written before commit.
+ */
+#define BTRFS_INODE_ORDERED_DATA_CLOSE         0
+#define BTRFS_INODE_ORPHAN_META_RESERVED       1
+#define BTRFS_INODE_DUMMY                      2
+#define BTRFS_INODE_IN_DEFRAG                  3
+#define BTRFS_INODE_DELALLOC_META_RESERVED     4
+#define BTRFS_INODE_HAS_ORPHAN_ITEM            5
+
 /* in memory btrfs inode */
 struct btrfs_inode {
        /* which subvolume this inode belongs to */
@@ -57,9 +71,6 @@ struct btrfs_inode {
        /* used to order data wrt metadata */
        struct btrfs_ordered_inode_tree ordered_tree;
 
-       /* for keeping track of orphaned inodes */
-       struct list_head i_orphan;
-
        /* list of all the delalloc inodes in the FS.  There are times we need
         * to write all the delalloc pages to disk, and this list is used
         * to walk them all.
@@ -78,14 +89,13 @@ struct btrfs_inode {
        /* the space_info for where this inode's data allocations are done */
        struct btrfs_space_info *space_info;
 
+       unsigned long runtime_flags;
+
        /* full 64 bit generation number, struct vfs_inode doesn't have a big
         * enough field for this.
         */
        u64 generation;
 
-       /* sequence number for NFS changes */
-       u64 sequence;
-
        /*
         * transid of the trans_handle that last modified this inode
         */
@@ -144,23 +154,10 @@ struct btrfs_inode {
        unsigned outstanding_extents;
        unsigned reserved_extents;
 
-       /*
-        * ordered_data_close is set by truncate when a file that used
-        * to have good data has been truncated to zero.  When it is set
-        * the btrfs file release call will add this inode to the
-        * ordered operations list so that we make sure to flush out any
-        * new data the application may have written before commit.
-        */
-       unsigned ordered_data_close:1;
-       unsigned orphan_meta_reserved:1;
-       unsigned dummy_inode:1;
-       unsigned in_defrag:1;
-       unsigned delalloc_meta_reserved:1;
-
        /*
         * always compress this one file
         */
-       unsigned force_compress:4;
+       unsigned force_compress;
 
        struct btrfs_delayed_node *delayed_node;
 
@@ -202,4 +199,17 @@ static inline bool btrfs_is_free_space_inode(struct btrfs_root *root,
        return false;
 }
 
+static inline int btrfs_inode_in_log(struct inode *inode, u64 generation)
+{
+       struct btrfs_root *root = BTRFS_I(inode)->root;
+       int ret = 0;
+
+       mutex_lock(&root->log_mutex);
+       if (BTRFS_I(inode)->logged_trans == generation &&
+           BTRFS_I(inode)->last_sub_trans <= root->last_log_commit)
+               ret = 1;
+       mutex_unlock(&root->log_mutex);
+       return ret;
+}
+
 #endif
index c053e90f2006f580ed4f8a4440fb520639c3edd6..9cebb1fd6a3cc59919c7c990d3016caee52b5849 100644 (file)
 #define BTRFSIC_BLOCK_STACK_FRAME_MAGIC_NUMBER 20111300
 #define BTRFSIC_TREE_DUMP_MAX_INDENT_LEVEL (200 - 6)   /* in characters,
                                                         * excluding " [...]" */
-#define BTRFSIC_BLOCK_SIZE PAGE_SIZE
-
 #define BTRFSIC_GENERATION_UNKNOWN ((u64)-1)
 
 /*
@@ -210,8 +208,9 @@ struct btrfsic_block_data_ctx {
        u64 dev_bytenr;         /* physical bytenr on device */
        u32 len;
        struct btrfsic_dev_state *dev;
-       char *data;
-       struct buffer_head *bh; /* do not use if set to NULL */
+       char **datav;
+       struct page **pagev;
+       void *mem_to_free;
 };
 
 /* This structure is used to implement recursion without occupying
@@ -243,6 +242,8 @@ struct btrfsic_state {
        struct btrfs_root *root;
        u64 max_superblock_generation;
        struct btrfsic_block *latest_superblock;
+       u32 metablock_size;
+       u32 datablock_size;
 };
 
 static void btrfsic_block_init(struct btrfsic_block *b);
@@ -290,8 +291,10 @@ static int btrfsic_process_superblock(struct btrfsic_state *state,
 static int btrfsic_process_metablock(struct btrfsic_state *state,
                                     struct btrfsic_block *block,
                                     struct btrfsic_block_data_ctx *block_ctx,
-                                    struct btrfs_header *hdr,
                                     int limit_nesting, int force_iodone_flag);
+static void btrfsic_read_from_block_data(
+       struct btrfsic_block_data_ctx *block_ctx,
+       void *dst, u32 offset, size_t len);
 static int btrfsic_create_link_to_next_block(
                struct btrfsic_state *state,
                struct btrfsic_block *block,
@@ -318,12 +321,13 @@ static void btrfsic_release_block_ctx(struct btrfsic_block_data_ctx *block_ctx);
 static int btrfsic_read_block(struct btrfsic_state *state,
                              struct btrfsic_block_data_ctx *block_ctx);
 static void btrfsic_dump_database(struct btrfsic_state *state);
+static void btrfsic_complete_bio_end_io(struct bio *bio, int err);
 static int btrfsic_test_for_metadata(struct btrfsic_state *state,
-                                    const u8 *data, unsigned int size);
+                                    char **datav, unsigned int num_pages);
 static void btrfsic_process_written_block(struct btrfsic_dev_state *dev_state,
-                                         u64 dev_bytenr, u8 *mapped_data,
-                                         unsigned int len, struct bio *bio,
-                                         int *bio_is_patched,
+                                         u64 dev_bytenr, char **mapped_datav,
+                                         unsigned int num_pages,
+                                         struct bio *bio, int *bio_is_patched,
                                          struct buffer_head *bh,
                                          int submit_bio_bh_rw);
 static int btrfsic_process_written_superblock(
@@ -375,7 +379,7 @@ static struct btrfsic_dev_state *btrfsic_dev_state_lookup(
 static void btrfsic_cmp_log_and_dev_bytenr(struct btrfsic_state *state,
                                           u64 bytenr,
                                           struct btrfsic_dev_state *dev_state,
-                                          u64 dev_bytenr, char *data);
+                                          u64 dev_bytenr);
 
 static struct mutex btrfsic_mutex;
 static int btrfsic_is_initialized;
@@ -651,7 +655,7 @@ static int btrfsic_process_superblock(struct btrfsic_state *state,
        int pass;
 
        BUG_ON(NULL == state);
-       selected_super = kmalloc(sizeof(*selected_super), GFP_NOFS);
+       selected_super = kzalloc(sizeof(*selected_super), GFP_NOFS);
        if (NULL == selected_super) {
                printk(KERN_INFO "btrfsic: error, kmalloc failed!\n");
                return -1;
@@ -718,7 +722,7 @@ static int btrfsic_process_superblock(struct btrfsic_state *state,
 
                num_copies =
                    btrfs_num_copies(&state->root->fs_info->mapping_tree,
-                                    next_bytenr, PAGE_SIZE);
+                                    next_bytenr, state->metablock_size);
                if (state->print_mask & BTRFSIC_PRINT_MASK_NUM_COPIES)
                        printk(KERN_INFO "num_copies(log_bytenr=%llu) = %d\n",
                               (unsigned long long)next_bytenr, num_copies);
@@ -727,9 +731,9 @@ static int btrfsic_process_superblock(struct btrfsic_state *state,
                        struct btrfsic_block *next_block;
                        struct btrfsic_block_data_ctx tmp_next_block_ctx;
                        struct btrfsic_block_link *l;
-                       struct btrfs_header *hdr;
 
-                       ret = btrfsic_map_block(state, next_bytenr, PAGE_SIZE,
+                       ret = btrfsic_map_block(state, next_bytenr,
+                                               state->metablock_size,
                                                &tmp_next_block_ctx,
                                                mirror_num);
                        if (ret) {
@@ -758,7 +762,7 @@ static int btrfsic_process_superblock(struct btrfsic_state *state,
                        BUG_ON(NULL == l);
 
                        ret = btrfsic_read_block(state, &tmp_next_block_ctx);
-                       if (ret < (int)BTRFSIC_BLOCK_SIZE) {
+                       if (ret < (int)PAGE_CACHE_SIZE) {
                                printk(KERN_INFO
                                       "btrfsic: read @logical %llu failed!\n",
                                       (unsigned long long)
@@ -768,11 +772,9 @@ static int btrfsic_process_superblock(struct btrfsic_state *state,
                                return -1;
                        }
 
-                       hdr = (struct btrfs_header *)tmp_next_block_ctx.data;
                        ret = btrfsic_process_metablock(state,
                                                        next_block,
                                                        &tmp_next_block_ctx,
-                                                       hdr,
                                                        BTRFS_MAX_LEVEL + 3, 1);
                        btrfsic_release_block_ctx(&tmp_next_block_ctx);
                }
@@ -799,7 +801,10 @@ static int btrfsic_process_superblock_dev_mirror(
 
        /* super block bytenr is always the unmapped device bytenr */
        dev_bytenr = btrfs_sb_offset(superblock_mirror_num);
-       bh = __bread(superblock_bdev, dev_bytenr / 4096, 4096);
+       if (dev_bytenr + BTRFS_SUPER_INFO_SIZE > device->total_bytes)
+               return -1;
+       bh = __bread(superblock_bdev, dev_bytenr / 4096,
+                    BTRFS_SUPER_INFO_SIZE);
        if (NULL == bh)
                return -1;
        super_tmp = (struct btrfs_super_block *)
@@ -808,7 +813,10 @@ static int btrfsic_process_superblock_dev_mirror(
        if (btrfs_super_bytenr(super_tmp) != dev_bytenr ||
            strncmp((char *)(&(super_tmp->magic)), BTRFS_MAGIC,
                    sizeof(super_tmp->magic)) ||
-           memcmp(device->uuid, super_tmp->dev_item.uuid, BTRFS_UUID_SIZE)) {
+           memcmp(device->uuid, super_tmp->dev_item.uuid, BTRFS_UUID_SIZE) ||
+           btrfs_super_nodesize(super_tmp) != state->metablock_size ||
+           btrfs_super_leafsize(super_tmp) != state->metablock_size ||
+           btrfs_super_sectorsize(super_tmp) != state->datablock_size) {
                brelse(bh);
                return 0;
        }
@@ -893,7 +901,7 @@ static int btrfsic_process_superblock_dev_mirror(
 
                num_copies =
                    btrfs_num_copies(&state->root->fs_info->mapping_tree,
-                                    next_bytenr, PAGE_SIZE);
+                                    next_bytenr, state->metablock_size);
                if (state->print_mask & BTRFSIC_PRINT_MASK_NUM_COPIES)
                        printk(KERN_INFO "num_copies(log_bytenr=%llu) = %d\n",
                               (unsigned long long)next_bytenr, num_copies);
@@ -902,7 +910,8 @@ static int btrfsic_process_superblock_dev_mirror(
                        struct btrfsic_block_data_ctx tmp_next_block_ctx;
                        struct btrfsic_block_link *l;
 
-                       if (btrfsic_map_block(state, next_bytenr, PAGE_SIZE,
+                       if (btrfsic_map_block(state, next_bytenr,
+                                             state->metablock_size,
                                              &tmp_next_block_ctx,
                                              mirror_num)) {
                                printk(KERN_INFO "btrfsic: btrfsic_map_block("
@@ -966,13 +975,15 @@ static int btrfsic_process_metablock(
                struct btrfsic_state *state,
                struct btrfsic_block *const first_block,
                struct btrfsic_block_data_ctx *const first_block_ctx,
-               struct btrfs_header *const first_hdr,
                int first_limit_nesting, int force_iodone_flag)
 {
        struct btrfsic_stack_frame initial_stack_frame = { 0 };
        struct btrfsic_stack_frame *sf;
        struct btrfsic_stack_frame *next_stack;
+       struct btrfs_header *const first_hdr =
+               (struct btrfs_header *)first_block_ctx->datav[0];
 
+       BUG_ON(!first_hdr);
        sf = &initial_stack_frame;
        sf->error = 0;
        sf->i = -1;
@@ -1012,21 +1023,47 @@ continue_with_current_leaf_stack_frame:
                }
 
                if (sf->i < sf->nr) {
-                       struct btrfs_item *disk_item = leafhdr->items + sf->i;
-                       struct btrfs_disk_key *disk_key = &disk_item->key;
+                       struct btrfs_item disk_item;
+                       u32 disk_item_offset =
+                               (uintptr_t)(leafhdr->items + sf->i) -
+                               (uintptr_t)leafhdr;
+                       struct btrfs_disk_key *disk_key;
                        u8 type;
-                       const u32 item_offset = le32_to_cpu(disk_item->offset);
+                       u32 item_offset;
 
+                       if (disk_item_offset + sizeof(struct btrfs_item) >
+                           sf->block_ctx->len) {
+leaf_item_out_of_bounce_error:
+                               printk(KERN_INFO
+                                      "btrfsic: leaf item out of bounce at logical %llu, dev %s\n",
+                                      sf->block_ctx->start,
+                                      sf->block_ctx->dev->name);
+                               goto one_stack_frame_backwards;
+                       }
+                       btrfsic_read_from_block_data(sf->block_ctx,
+                                                    &disk_item,
+                                                    disk_item_offset,
+                                                    sizeof(struct btrfs_item));
+                       item_offset = le32_to_cpu(disk_item.offset);
+                       disk_key = &disk_item.key;
                        type = disk_key->type;
 
                        if (BTRFS_ROOT_ITEM_KEY == type) {
-                               const struct btrfs_root_item *const root_item =
-                                   (struct btrfs_root_item *)
-                                   (sf->block_ctx->data +
-                                    offsetof(struct btrfs_leaf, items) +
-                                    item_offset);
-                               const u64 next_bytenr =
-                                   le64_to_cpu(root_item->bytenr);
+                               struct btrfs_root_item root_item;
+                               u32 root_item_offset;
+                               u64 next_bytenr;
+
+                               root_item_offset = item_offset +
+                                       offsetof(struct btrfs_leaf, items);
+                               if (root_item_offset +
+                                   sizeof(struct btrfs_root_item) >
+                                   sf->block_ctx->len)
+                                       goto leaf_item_out_of_bounce_error;
+                               btrfsic_read_from_block_data(
+                                       sf->block_ctx, &root_item,
+                                       root_item_offset,
+                                       sizeof(struct btrfs_root_item));
+                               next_bytenr = le64_to_cpu(root_item.bytenr);
 
                                sf->error =
                                    btrfsic_create_link_to_next_block(
@@ -1041,7 +1078,7 @@ continue_with_current_leaf_stack_frame:
                                                &sf->num_copies,
                                                &sf->mirror_num,
                                                disk_key,
-                                               le64_to_cpu(root_item->
+                                               le64_to_cpu(root_item.
                                                generation));
                                if (sf->error)
                                        goto one_stack_frame_backwards;
@@ -1049,7 +1086,7 @@ continue_with_current_leaf_stack_frame:
                                if (NULL != sf->next_block) {
                                        struct btrfs_header *const next_hdr =
                                            (struct btrfs_header *)
-                                           sf->next_block_ctx.data;
+                                           sf->next_block_ctx.datav[0];
 
                                        next_stack =
                                            btrfsic_stack_frame_alloc();
@@ -1111,10 +1148,24 @@ continue_with_current_node_stack_frame:
                }
 
                if (sf->i < sf->nr) {
-                       struct btrfs_key_ptr *disk_key_ptr =
-                           nodehdr->ptrs + sf->i;
-                       const u64 next_bytenr =
-                           le64_to_cpu(disk_key_ptr->blockptr);
+                       struct btrfs_key_ptr key_ptr;
+                       u32 key_ptr_offset;
+                       u64 next_bytenr;
+
+                       key_ptr_offset = (uintptr_t)(nodehdr->ptrs + sf->i) -
+                                         (uintptr_t)nodehdr;
+                       if (key_ptr_offset + sizeof(struct btrfs_key_ptr) >
+                           sf->block_ctx->len) {
+                               printk(KERN_INFO
+                                      "btrfsic: node item out of bounce at logical %llu, dev %s\n",
+                                      sf->block_ctx->start,
+                                      sf->block_ctx->dev->name);
+                               goto one_stack_frame_backwards;
+                       }
+                       btrfsic_read_from_block_data(
+                               sf->block_ctx, &key_ptr, key_ptr_offset,
+                               sizeof(struct btrfs_key_ptr));
+                       next_bytenr = le64_to_cpu(key_ptr.blockptr);
 
                        sf->error = btrfsic_create_link_to_next_block(
                                        state,
@@ -1127,15 +1178,15 @@ continue_with_current_node_stack_frame:
                                        force_iodone_flag,
                                        &sf->num_copies,
                                        &sf->mirror_num,
-                                       &disk_key_ptr->key,
-                                       le64_to_cpu(disk_key_ptr->generation));
+                                       &key_ptr.key,
+                                       le64_to_cpu(key_ptr.generation));
                        if (sf->error)
                                goto one_stack_frame_backwards;
 
                        if (NULL != sf->next_block) {
                                struct btrfs_header *const next_hdr =
                                    (struct btrfs_header *)
-                                   sf->next_block_ctx.data;
+                                   sf->next_block_ctx.datav[0];
 
                                next_stack = btrfsic_stack_frame_alloc();
                                if (NULL == next_stack)
@@ -1181,6 +1232,35 @@ one_stack_frame_backwards:
        return sf->error;
 }
 
+static void btrfsic_read_from_block_data(
+       struct btrfsic_block_data_ctx *block_ctx,
+       void *dstv, u32 offset, size_t len)
+{
+       size_t cur;
+       size_t offset_in_page;
+       char *kaddr;
+       char *dst = (char *)dstv;
+       size_t start_offset = block_ctx->start & ((u64)PAGE_CACHE_SIZE - 1);
+       unsigned long i = (start_offset + offset) >> PAGE_CACHE_SHIFT;
+
+       WARN_ON(offset + len > block_ctx->len);
+       offset_in_page = (start_offset + offset) &
+                        ((unsigned long)PAGE_CACHE_SIZE - 1);
+
+       while (len > 0) {
+               cur = min(len, ((size_t)PAGE_CACHE_SIZE - offset_in_page));
+               BUG_ON(i >= (block_ctx->len + PAGE_CACHE_SIZE - 1) >>
+                           PAGE_CACHE_SHIFT);
+               kaddr = block_ctx->datav[i];
+               memcpy(dst, kaddr + offset_in_page, cur);
+
+               dst += cur;
+               len -= cur;
+               offset_in_page = 0;
+               i++;
+       }
+}
+
 static int btrfsic_create_link_to_next_block(
                struct btrfsic_state *state,
                struct btrfsic_block *block,
@@ -1204,7 +1284,7 @@ static int btrfsic_create_link_to_next_block(
        if (0 == *num_copiesp) {
                *num_copiesp =
                    btrfs_num_copies(&state->root->fs_info->mapping_tree,
-                                    next_bytenr, PAGE_SIZE);
+                                    next_bytenr, state->metablock_size);
                if (state->print_mask & BTRFSIC_PRINT_MASK_NUM_COPIES)
                        printk(KERN_INFO "num_copies(log_bytenr=%llu) = %d\n",
                               (unsigned long long)next_bytenr, *num_copiesp);
@@ -1219,7 +1299,7 @@ static int btrfsic_create_link_to_next_block(
                       "btrfsic_create_link_to_next_block(mirror_num=%d)\n",
                       *mirror_nump);
        ret = btrfsic_map_block(state, next_bytenr,
-                               BTRFSIC_BLOCK_SIZE,
+                               state->metablock_size,
                                next_block_ctx, *mirror_nump);
        if (ret) {
                printk(KERN_INFO
@@ -1314,7 +1394,7 @@ static int btrfsic_create_link_to_next_block(
 
        if (limit_nesting > 0 && did_alloc_block_link) {
                ret = btrfsic_read_block(state, next_block_ctx);
-               if (ret < (int)BTRFSIC_BLOCK_SIZE) {
+               if (ret < (int)next_block_ctx->len) {
                        printk(KERN_INFO
                               "btrfsic: read block @logical %llu failed!\n",
                               (unsigned long long)next_bytenr);
@@ -1339,43 +1419,74 @@ static int btrfsic_handle_extent_data(
                u32 item_offset, int force_iodone_flag)
 {
        int ret;
-       struct btrfs_file_extent_item *file_extent_item =
-           (struct btrfs_file_extent_item *)(block_ctx->data +
-                                             offsetof(struct btrfs_leaf,
-                                                      items) + item_offset);
-       u64 next_bytenr =
-           le64_to_cpu(file_extent_item->disk_bytenr) +
-           le64_to_cpu(file_extent_item->offset);
-       u64 num_bytes = le64_to_cpu(file_extent_item->num_bytes);
-       u64 generation = le64_to_cpu(file_extent_item->generation);
+       struct btrfs_file_extent_item file_extent_item;
+       u64 file_extent_item_offset;
+       u64 next_bytenr;
+       u64 num_bytes;
+       u64 generation;
        struct btrfsic_block_link *l;
 
+       file_extent_item_offset = offsetof(struct btrfs_leaf, items) +
+                                 item_offset;
+       if (file_extent_item_offset +
+           offsetof(struct btrfs_file_extent_item, disk_num_bytes) >
+           block_ctx->len) {
+               printk(KERN_INFO
+                      "btrfsic: file item out of bounce at logical %llu, dev %s\n",
+                      block_ctx->start, block_ctx->dev->name);
+               return -1;
+       }
+
+       btrfsic_read_from_block_data(block_ctx, &file_extent_item,
+               file_extent_item_offset,
+               offsetof(struct btrfs_file_extent_item, disk_num_bytes));
+       if (BTRFS_FILE_EXTENT_REG != file_extent_item.type ||
+           ((u64)0) == le64_to_cpu(file_extent_item.disk_bytenr)) {
+               if (state->print_mask & BTRFSIC_PRINT_MASK_VERY_VERBOSE)
+                       printk(KERN_INFO "extent_data: type %u, disk_bytenr = %llu\n",
+                              file_extent_item.type,
+                              (unsigned long long)
+                              le64_to_cpu(file_extent_item.disk_bytenr));
+               return 0;
+       }
+
+       if (file_extent_item_offset + sizeof(struct btrfs_file_extent_item) >
+           block_ctx->len) {
+               printk(KERN_INFO
+                      "btrfsic: file item out of bounce at logical %llu, dev %s\n",
+                      block_ctx->start, block_ctx->dev->name);
+               return -1;
+       }
+       btrfsic_read_from_block_data(block_ctx, &file_extent_item,
+                                    file_extent_item_offset,
+                                    sizeof(struct btrfs_file_extent_item));
+       next_bytenr = le64_to_cpu(file_extent_item.disk_bytenr) +
+                     le64_to_cpu(file_extent_item.offset);
+       generation = le64_to_cpu(file_extent_item.generation);
+       num_bytes = le64_to_cpu(file_extent_item.num_bytes);
+       generation = le64_to_cpu(file_extent_item.generation);
+
        if (state->print_mask & BTRFSIC_PRINT_MASK_VERY_VERBOSE)
                printk(KERN_INFO "extent_data: type %u, disk_bytenr = %llu,"
                       " offset = %llu, num_bytes = %llu\n",
-                      file_extent_item->type,
+                      file_extent_item.type,
                       (unsigned long long)
-                      le64_to_cpu(file_extent_item->disk_bytenr),
-                      (unsigned long long)
-                      le64_to_cpu(file_extent_item->offset),
-                      (unsigned long long)
-                      le64_to_cpu(file_extent_item->num_bytes));
-       if (BTRFS_FILE_EXTENT_REG != file_extent_item->type ||
-           ((u64)0) == le64_to_cpu(file_extent_item->disk_bytenr))
-               return 0;
+                      le64_to_cpu(file_extent_item.disk_bytenr),
+                      (unsigned long long)le64_to_cpu(file_extent_item.offset),
+                      (unsigned long long)num_bytes);
        while (num_bytes > 0) {
                u32 chunk_len;
                int num_copies;
                int mirror_num;
 
-               if (num_bytes > BTRFSIC_BLOCK_SIZE)
-                       chunk_len = BTRFSIC_BLOCK_SIZE;
+               if (num_bytes > state->datablock_size)
+                       chunk_len = state->datablock_size;
                else
                        chunk_len = num_bytes;
 
                num_copies =
                    btrfs_num_copies(&state->root->fs_info->mapping_tree,
-                                    next_bytenr, PAGE_SIZE);
+                                    next_bytenr, state->datablock_size);
                if (state->print_mask & BTRFSIC_PRINT_MASK_NUM_COPIES)
                        printk(KERN_INFO "num_copies(log_bytenr=%llu) = %d\n",
                               (unsigned long long)next_bytenr, num_copies);
@@ -1475,8 +1586,9 @@ static int btrfsic_map_block(struct btrfsic_state *state, u64 bytenr, u32 len,
        block_ctx_out->dev_bytenr = multi->stripes[0].physical;
        block_ctx_out->start = bytenr;
        block_ctx_out->len = len;
-       block_ctx_out->data = NULL;
-       block_ctx_out->bh = NULL;
+       block_ctx_out->datav = NULL;
+       block_ctx_out->pagev = NULL;
+       block_ctx_out->mem_to_free = NULL;
 
        if (0 == ret)
                kfree(multi);
@@ -1496,8 +1608,9 @@ static int btrfsic_map_superblock(struct btrfsic_state *state, u64 bytenr,
        block_ctx_out->dev_bytenr = bytenr;
        block_ctx_out->start = bytenr;
        block_ctx_out->len = len;
-       block_ctx_out->data = NULL;
-       block_ctx_out->bh = NULL;
+       block_ctx_out->datav = NULL;
+       block_ctx_out->pagev = NULL;
+       block_ctx_out->mem_to_free = NULL;
        if (NULL != block_ctx_out->dev) {
                return 0;
        } else {
@@ -1508,38 +1621,127 @@ static int btrfsic_map_superblock(struct btrfsic_state *state, u64 bytenr,
 
 static void btrfsic_release_block_ctx(struct btrfsic_block_data_ctx *block_ctx)
 {
-       if (NULL != block_ctx->bh) {
-               brelse(block_ctx->bh);
-               block_ctx->bh = NULL;
+       if (block_ctx->mem_to_free) {
+               unsigned int num_pages;
+
+               BUG_ON(!block_ctx->datav);
+               BUG_ON(!block_ctx->pagev);
+               num_pages = (block_ctx->len + (u64)PAGE_CACHE_SIZE - 1) >>
+                           PAGE_CACHE_SHIFT;
+               while (num_pages > 0) {
+                       num_pages--;
+                       if (block_ctx->datav[num_pages]) {
+                               kunmap(block_ctx->pagev[num_pages]);
+                               block_ctx->datav[num_pages] = NULL;
+                       }
+                       if (block_ctx->pagev[num_pages]) {
+                               __free_page(block_ctx->pagev[num_pages]);
+                               block_ctx->pagev[num_pages] = NULL;
+                       }
+               }
+
+               kfree(block_ctx->mem_to_free);
+               block_ctx->mem_to_free = NULL;
+               block_ctx->pagev = NULL;
+               block_ctx->datav = NULL;
        }
 }
 
 static int btrfsic_read_block(struct btrfsic_state *state,
                              struct btrfsic_block_data_ctx *block_ctx)
 {
-       block_ctx->bh = NULL;
-       if (block_ctx->dev_bytenr & 4095) {
+       unsigned int num_pages;
+       unsigned int i;
+       u64 dev_bytenr;
+       int ret;
+
+       BUG_ON(block_ctx->datav);
+       BUG_ON(block_ctx->pagev);
+       BUG_ON(block_ctx->mem_to_free);
+       if (block_ctx->dev_bytenr & ((u64)PAGE_CACHE_SIZE - 1)) {
                printk(KERN_INFO
                       "btrfsic: read_block() with unaligned bytenr %llu\n",
                       (unsigned long long)block_ctx->dev_bytenr);
                return -1;
        }
-       if (block_ctx->len > 4096) {
-               printk(KERN_INFO
-                      "btrfsic: read_block() with too huge size %d\n",
-                      block_ctx->len);
+
+       num_pages = (block_ctx->len + (u64)PAGE_CACHE_SIZE - 1) >>
+                   PAGE_CACHE_SHIFT;
+       block_ctx->mem_to_free = kzalloc((sizeof(*block_ctx->datav) +
+                                         sizeof(*block_ctx->pagev)) *
+                                        num_pages, GFP_NOFS);
+       if (!block_ctx->mem_to_free)
                return -1;
+       block_ctx->datav = block_ctx->mem_to_free;
+       block_ctx->pagev = (struct page **)(block_ctx->datav + num_pages);
+       for (i = 0; i < num_pages; i++) {
+               block_ctx->pagev[i] = alloc_page(GFP_NOFS);
+               if (!block_ctx->pagev[i])
+                       return -1;
        }
 
-       block_ctx->bh = __bread(block_ctx->dev->bdev,
-                               block_ctx->dev_bytenr >> 12, 4096);
-       if (NULL == block_ctx->bh)
-               return -1;
-       block_ctx->data = block_ctx->bh->b_data;
+       dev_bytenr = block_ctx->dev_bytenr;
+       for (i = 0; i < num_pages;) {
+               struct bio *bio;
+               unsigned int j;
+               DECLARE_COMPLETION_ONSTACK(complete);
+
+               bio = bio_alloc(GFP_NOFS, num_pages - i);
+               if (!bio) {
+                       printk(KERN_INFO
+                              "btrfsic: bio_alloc() for %u pages failed!\n",
+                              num_pages - i);
+                       return -1;
+               }
+               bio->bi_bdev = block_ctx->dev->bdev;
+               bio->bi_sector = dev_bytenr >> 9;
+               bio->bi_end_io = btrfsic_complete_bio_end_io;
+               bio->bi_private = &complete;
+
+               for (j = i; j < num_pages; j++) {
+                       ret = bio_add_page(bio, block_ctx->pagev[j],
+                                          PAGE_CACHE_SIZE, 0);
+                       if (PAGE_CACHE_SIZE != ret)
+                               break;
+               }
+               if (j == i) {
+                       printk(KERN_INFO
+                              "btrfsic: error, failed to add a single page!\n");
+                       return -1;
+               }
+               submit_bio(READ, bio);
+
+               /* this will also unplug the queue */
+               wait_for_completion(&complete);
+
+               if (!test_bit(BIO_UPTODATE, &bio->bi_flags)) {
+                       printk(KERN_INFO
+                              "btrfsic: read error at logical %llu dev %s!\n",
+                              block_ctx->start, block_ctx->dev->name);
+                       bio_put(bio);
+                       return -1;
+               }
+               bio_put(bio);
+               dev_bytenr += (j - i) * PAGE_CACHE_SIZE;
+               i = j;
+       }
+       for (i = 0; i < num_pages; i++) {
+               block_ctx->datav[i] = kmap(block_ctx->pagev[i]);
+               if (!block_ctx->datav[i]) {
+                       printk(KERN_INFO "btrfsic: kmap() failed (dev %s)!\n",
+                              block_ctx->dev->name);
+                       return -1;
+               }
+       }
 
        return block_ctx->len;
 }
 
+static void btrfsic_complete_bio_end_io(struct bio *bio, int err)
+{
+       complete((struct completion *)bio->bi_private);
+}
+
 static void btrfsic_dump_database(struct btrfsic_state *state)
 {
        struct list_head *elem_all;
@@ -1617,32 +1819,39 @@ static void btrfsic_dump_database(struct btrfsic_state *state)
  * (note that this test fails for the super block)
  */
 static int btrfsic_test_for_metadata(struct btrfsic_state *state,
-                                    const u8 *data, unsigned int size)
+                                    char **datav, unsigned int num_pages)
 {
        struct btrfs_header *h;
        u8 csum[BTRFS_CSUM_SIZE];
        u32 crc = ~(u32)0;
-       int fail = 0;
-       int crc_fail = 0;
+       unsigned int i;
 
-       h = (struct btrfs_header *)data;
+       if (num_pages * PAGE_CACHE_SIZE < state->metablock_size)
+               return 1; /* not metadata */
+       num_pages = state->metablock_size >> PAGE_CACHE_SHIFT;
+       h = (struct btrfs_header *)datav[0];
 
        if (memcmp(h->fsid, state->root->fs_info->fsid, BTRFS_UUID_SIZE))
-               fail++;
+               return 1;
+
+       for (i = 0; i < num_pages; i++) {
+               u8 *data = i ? datav[i] : (datav[i] + BTRFS_CSUM_SIZE);
+               size_t sublen = i ? PAGE_CACHE_SIZE :
+                                   (PAGE_CACHE_SIZE - BTRFS_CSUM_SIZE);
 
-       crc = crc32c(crc, data + BTRFS_CSUM_SIZE, PAGE_SIZE - BTRFS_CSUM_SIZE);
+               crc = crc32c(crc, data, sublen);
+       }
        btrfs_csum_final(crc, csum);
        if (memcmp(csum, h->csum, state->csum_size))
-               crc_fail++;
+               return 1;
 
-       return fail || crc_fail;
+       return 0; /* is metadata */
 }
 
 static void btrfsic_process_written_block(struct btrfsic_dev_state *dev_state,
-                                         u64 dev_bytenr,
-                                         u8 *mapped_data, unsigned int len,
-                                         struct bio *bio,
-                                         int *bio_is_patched,
+                                         u64 dev_bytenr, char **mapped_datav,
+                                         unsigned int num_pages,
+                                         struct bio *bio, int *bio_is_patched,
                                          struct buffer_head *bh,
                                          int submit_bio_bh_rw)
 {
@@ -1652,12 +1861,19 @@ static void btrfsic_process_written_block(struct btrfsic_dev_state *dev_state,
        int ret;
        struct btrfsic_state *state = dev_state->state;
        struct block_device *bdev = dev_state->bdev;
+       unsigned int processed_len;
 
-       WARN_ON(len > PAGE_SIZE);
-       is_metadata = (0 == btrfsic_test_for_metadata(state, mapped_data, len));
        if (NULL != bio_is_patched)
                *bio_is_patched = 0;
 
+again:
+       if (num_pages == 0)
+               return;
+
+       processed_len = 0;
+       is_metadata = (0 == btrfsic_test_for_metadata(state, mapped_datav,
+                                                     num_pages));
+
        block = btrfsic_block_hashtable_lookup(bdev, dev_bytenr,
                                               &state->block_hashtable);
        if (NULL != block) {
@@ -1667,8 +1883,16 @@ static void btrfsic_process_written_block(struct btrfsic_dev_state *dev_state,
 
                if (block->is_superblock) {
                        bytenr = le64_to_cpu(((struct btrfs_super_block *)
-                                             mapped_data)->bytenr);
+                                             mapped_datav[0])->bytenr);
+                       if (num_pages * PAGE_CACHE_SIZE <
+                           BTRFS_SUPER_INFO_SIZE) {
+                               printk(KERN_INFO
+                                      "btrfsic: cannot work with too short bios!\n");
+                               return;
+                       }
                        is_metadata = 1;
+                       BUG_ON(BTRFS_SUPER_INFO_SIZE & (PAGE_CACHE_SIZE - 1));
+                       processed_len = BTRFS_SUPER_INFO_SIZE;
                        if (state->print_mask &
                            BTRFSIC_PRINT_MASK_TREE_BEFORE_SB_WRITE) {
                                printk(KERN_INFO
@@ -1678,12 +1902,18 @@ static void btrfsic_process_written_block(struct btrfsic_dev_state *dev_state,
                }
                if (is_metadata) {
                        if (!block->is_superblock) {
+                               if (num_pages * PAGE_CACHE_SIZE <
+                                   state->metablock_size) {
+                                       printk(KERN_INFO
+                                              "btrfsic: cannot work with too short bios!\n");
+                                       return;
+                               }
+                               processed_len = state->metablock_size;
                                bytenr = le64_to_cpu(((struct btrfs_header *)
-                                                     mapped_data)->bytenr);
+                                                     mapped_datav[0])->bytenr);
                                btrfsic_cmp_log_and_dev_bytenr(state, bytenr,
                                                               dev_state,
-                                                              dev_bytenr,
-                                                              mapped_data);
+                                                              dev_bytenr);
                        }
                        if (block->logical_bytenr != bytenr) {
                                printk(KERN_INFO
@@ -1710,6 +1940,13 @@ static void btrfsic_process_written_block(struct btrfsic_dev_state *dev_state,
                                       block->mirror_num,
                                       btrfsic_get_block_type(state, block));
                } else {
+                       if (num_pages * PAGE_CACHE_SIZE <
+                           state->datablock_size) {
+                               printk(KERN_INFO
+                                      "btrfsic: cannot work with too short bios!\n");
+                               return;
+                       }
+                       processed_len = state->datablock_size;
                        bytenr = block->logical_bytenr;
                        if (state->print_mask & BTRFSIC_PRINT_MASK_VERBOSE)
                                printk(KERN_INFO
@@ -1747,7 +1984,7 @@ static void btrfsic_process_written_block(struct btrfsic_dev_state *dev_state,
                               le64_to_cpu(block->disk_key.offset),
                               (unsigned long long)
                               le64_to_cpu(((struct btrfs_header *)
-                                           mapped_data)->generation),
+                                           mapped_datav[0])->generation),
                               (unsigned long long)
                               state->max_superblock_generation);
                        btrfsic_dump_tree(state);
@@ -1765,10 +2002,10 @@ static void btrfsic_process_written_block(struct btrfsic_dev_state *dev_state,
                               (unsigned long long)block->generation,
                               (unsigned long long)
                               le64_to_cpu(((struct btrfs_header *)
-                                           mapped_data)->generation));
+                                           mapped_datav[0])->generation));
                        /* it would not be safe to go on */
                        btrfsic_dump_tree(state);
-                       return;
+                       goto continue_loop;
                }
 
                /*
@@ -1796,18 +2033,19 @@ static void btrfsic_process_written_block(struct btrfsic_dev_state *dev_state,
                }
 
                if (block->is_superblock)
-                       ret = btrfsic_map_superblock(state, bytenr, len,
+                       ret = btrfsic_map_superblock(state, bytenr,
+                                                    processed_len,
                                                     bdev, &block_ctx);
                else
-                       ret = btrfsic_map_block(state, bytenr, len,
+                       ret = btrfsic_map_block(state, bytenr, processed_len,
                                                &block_ctx, 0);
                if (ret) {
                        printk(KERN_INFO
                               "btrfsic: btrfsic_map_block(root @%llu)"
                               " failed!\n", (unsigned long long)bytenr);
-                       return;
+                       goto continue_loop;
                }
-               block_ctx.data = mapped_data;
+               block_ctx.datav = mapped_datav;
                /* the following is required in case of writes to mirrors,
                 * use the same that was used for the lookup */
                block_ctx.dev = dev_state;
@@ -1863,11 +2101,13 @@ static void btrfsic_process_written_block(struct btrfsic_dev_state *dev_state,
                        block->logical_bytenr = bytenr;
                        block->is_metadata = 1;
                        if (block->is_superblock) {
+                               BUG_ON(PAGE_CACHE_SIZE !=
+                                      BTRFS_SUPER_INFO_SIZE);
                                ret = btrfsic_process_written_superblock(
                                                state,
                                                block,
                                                (struct btrfs_super_block *)
-                                               mapped_data);
+                                               mapped_datav[0]);
                                if (state->print_mask &
                                    BTRFSIC_PRINT_MASK_TREE_AFTER_SB_WRITE) {
                                        printk(KERN_INFO
@@ -1880,8 +2120,6 @@ static void btrfsic_process_written_block(struct btrfsic_dev_state *dev_state,
                                                state,
                                                block,
                                                &block_ctx,
-                                               (struct btrfs_header *)
-                                               block_ctx.data,
                                                0, 0);
                        }
                        if (ret)
@@ -1912,26 +2150,30 @@ static void btrfsic_process_written_block(struct btrfsic_dev_state *dev_state,
                u64 bytenr;
 
                if (!is_metadata) {
+                       processed_len = state->datablock_size;
                        if (state->print_mask & BTRFSIC_PRINT_MASK_VERBOSE)
                                printk(KERN_INFO "Written block (%s/%llu/?)"
                                       " !found in hash table, D.\n",
                                       dev_state->name,
                                       (unsigned long long)dev_bytenr);
-                       if (!state->include_extent_data)
-                               return; /* ignore that written D block */
+                       if (!state->include_extent_data) {
+                               /* ignore that written D block */
+                               goto continue_loop;
+                       }
 
                        /* this is getting ugly for the
                         * include_extent_data case... */
                        bytenr = 0;     /* unknown */
                        block_ctx.start = bytenr;
-                       block_ctx.len = len;
-                       block_ctx.bh = NULL;
+                       block_ctx.len = processed_len;
+                       block_ctx.mem_to_free = NULL;
+                       block_ctx.pagev = NULL;
                } else {
+                       processed_len = state->metablock_size;
                        bytenr = le64_to_cpu(((struct btrfs_header *)
-                                             mapped_data)->bytenr);
+                                             mapped_datav[0])->bytenr);
                        btrfsic_cmp_log_and_dev_bytenr(state, bytenr, dev_state,
-                                                      dev_bytenr,
-                                                      mapped_data);
+                                                      dev_bytenr);
                        if (state->print_mask & BTRFSIC_PRINT_MASK_VERBOSE)
                                printk(KERN_INFO
                                       "Written block @%llu (%s/%llu/?)"
@@ -1940,17 +2182,17 @@ static void btrfsic_process_written_block(struct btrfsic_dev_state *dev_state,
                                       dev_state->name,
                                       (unsigned long long)dev_bytenr);
 
-                       ret = btrfsic_map_block(state, bytenr, len, &block_ctx,
-                                               0);
+                       ret = btrfsic_map_block(state, bytenr, processed_len,
+                                               &block_ctx, 0);
                        if (ret) {
                                printk(KERN_INFO
                                       "btrfsic: btrfsic_map_block(root @%llu)"
                                       " failed!\n",
                                       (unsigned long long)dev_bytenr);
-                               return;
+                               goto continue_loop;
                        }
                }
-               block_ctx.data = mapped_data;
+               block_ctx.datav = mapped_datav;
                /* the following is required in case of writes to mirrors,
                 * use the same that was used for the lookup */
                block_ctx.dev = dev_state;
@@ -1960,7 +2202,7 @@ static void btrfsic_process_written_block(struct btrfsic_dev_state *dev_state,
                if (NULL == block) {
                        printk(KERN_INFO "btrfsic: error, kmalloc failed!\n");
                        btrfsic_release_block_ctx(&block_ctx);
-                       return;
+                       goto continue_loop;
                }
                block->dev_state = dev_state;
                block->dev_bytenr = dev_bytenr;
@@ -2020,9 +2262,7 @@ static void btrfsic_process_written_block(struct btrfsic_dev_state *dev_state,
 
                if (is_metadata) {
                        ret = btrfsic_process_metablock(state, block,
-                                                       &block_ctx,
-                                                       (struct btrfs_header *)
-                                                       block_ctx.data, 0, 0);
+                                                       &block_ctx, 0, 0);
                        if (ret)
                                printk(KERN_INFO
                                       "btrfsic: process_metablock(root @%llu)"
@@ -2031,6 +2271,13 @@ static void btrfsic_process_written_block(struct btrfsic_dev_state *dev_state,
                }
                btrfsic_release_block_ctx(&block_ctx);
        }
+
+continue_loop:
+       BUG_ON(!processed_len);
+       dev_bytenr += processed_len;
+       mapped_datav += processed_len >> PAGE_CACHE_SHIFT;
+       num_pages -= processed_len >> PAGE_CACHE_SHIFT;
+       goto again;
 }
 
 static void btrfsic_bio_end_io(struct bio *bp, int bio_error_status)
@@ -2213,7 +2460,7 @@ static int btrfsic_process_written_superblock(
 
                num_copies =
                    btrfs_num_copies(&state->root->fs_info->mapping_tree,
-                                    next_bytenr, PAGE_SIZE);
+                                    next_bytenr, BTRFS_SUPER_INFO_SIZE);
                if (state->print_mask & BTRFSIC_PRINT_MASK_NUM_COPIES)
                        printk(KERN_INFO "num_copies(log_bytenr=%llu) = %d\n",
                               (unsigned long long)next_bytenr, num_copies);
@@ -2224,7 +2471,8 @@ static int btrfsic_process_written_superblock(
                                printk(KERN_INFO
                                       "btrfsic_process_written_superblock("
                                       "mirror_num=%d)\n", mirror_num);
-                       ret = btrfsic_map_block(state, next_bytenr, PAGE_SIZE,
+                       ret = btrfsic_map_block(state, next_bytenr,
+                                               BTRFS_SUPER_INFO_SIZE,
                                                &tmp_next_block_ctx,
                                                mirror_num);
                        if (ret) {
@@ -2689,7 +2937,7 @@ static struct btrfsic_block *btrfsic_block_lookup_or_add(
 static void btrfsic_cmp_log_and_dev_bytenr(struct btrfsic_state *state,
                                           u64 bytenr,
                                           struct btrfsic_dev_state *dev_state,
-                                          u64 dev_bytenr, char *data)
+                                          u64 dev_bytenr)
 {
        int num_copies;
        int mirror_num;
@@ -2698,10 +2946,10 @@ static void btrfsic_cmp_log_and_dev_bytenr(struct btrfsic_state *state,
        int match = 0;
 
        num_copies = btrfs_num_copies(&state->root->fs_info->mapping_tree,
-                                     bytenr, PAGE_SIZE);
+                                     bytenr, state->metablock_size);
 
        for (mirror_num = 1; mirror_num <= num_copies; mirror_num++) {
-               ret = btrfsic_map_block(state, bytenr, PAGE_SIZE,
+               ret = btrfsic_map_block(state, bytenr, state->metablock_size,
                                        &block_ctx, mirror_num);
                if (ret) {
                        printk(KERN_INFO "btrfsic:"
@@ -2727,7 +2975,8 @@ static void btrfsic_cmp_log_and_dev_bytenr(struct btrfsic_state *state,
                       (unsigned long long)bytenr, dev_state->name,
                       (unsigned long long)dev_bytenr);
                for (mirror_num = 1; mirror_num <= num_copies; mirror_num++) {
-                       ret = btrfsic_map_block(state, bytenr, PAGE_SIZE,
+                       ret = btrfsic_map_block(state, bytenr,
+                                               state->metablock_size,
                                                &block_ctx, mirror_num);
                        if (ret)
                                continue;
@@ -2781,13 +3030,13 @@ int btrfsic_submit_bh(int rw, struct buffer_head *bh)
                               (unsigned long)bh->b_size, bh->b_data,
                               bh->b_bdev);
                btrfsic_process_written_block(dev_state, dev_bytenr,
-                                             bh->b_data, bh->b_size, NULL,
+                                             &bh->b_data, 1, NULL,
                                              NULL, bh, rw);
        } else if (NULL != dev_state && (rw & REQ_FLUSH)) {
                if (dev_state->state->print_mask &
                    BTRFSIC_PRINT_MASK_SUBMIT_BIO_BH)
                        printk(KERN_INFO
-                              "submit_bh(rw=0x%x) FLUSH, bdev=%p)\n",
+                              "submit_bh(rw=0x%x FLUSH, bdev=%p)\n",
                               rw, bh->b_bdev);
                if (!dev_state->dummy_block_for_bio_bh_flush.is_iodone) {
                        if ((dev_state->state->print_mask &
@@ -2836,6 +3085,7 @@ void btrfsic_submit_bio(int rw, struct bio *bio)
                unsigned int i;
                u64 dev_bytenr;
                int bio_is_patched;
+               char **mapped_datav;
 
                dev_bytenr = 512 * bio->bi_sector;
                bio_is_patched = 0;
@@ -2848,35 +3098,46 @@ void btrfsic_submit_bio(int rw, struct bio *bio)
                               (unsigned long long)dev_bytenr,
                               bio->bi_bdev);
 
+               mapped_datav = kmalloc(sizeof(*mapped_datav) * bio->bi_vcnt,
+                                      GFP_NOFS);
+               if (!mapped_datav)
+                       goto leave;
                for (i = 0; i < bio->bi_vcnt; i++) {
-                       u8 *mapped_data;
-
-                       mapped_data = kmap(bio->bi_io_vec[i].bv_page);
+                       BUG_ON(bio->bi_io_vec[i].bv_len != PAGE_CACHE_SIZE);
+                       mapped_datav[i] = kmap(bio->bi_io_vec[i].bv_page);
+                       if (!mapped_datav[i]) {
+                               while (i > 0) {
+                                       i--;
+                                       kunmap(bio->bi_io_vec[i].bv_page);
+                               }
+                               kfree(mapped_datav);
+                               goto leave;
+                       }
                        if ((BTRFSIC_PRINT_MASK_SUBMIT_BIO_BH |
                             BTRFSIC_PRINT_MASK_VERBOSE) ==
                            (dev_state->state->print_mask &
                             (BTRFSIC_PRINT_MASK_SUBMIT_BIO_BH |
                              BTRFSIC_PRINT_MASK_VERBOSE)))
                                printk(KERN_INFO
-                                      "#%u: page=%p, mapped=%p, len=%u,"
-                                      " offset=%u\n",
+                                      "#%u: page=%p, len=%u, offset=%u\n",
                                       i, bio->bi_io_vec[i].bv_page,
-                                      mapped_data,
                                       bio->bi_io_vec[i].bv_len,
                                       bio->bi_io_vec[i].bv_offset);
-                       btrfsic_process_written_block(dev_state, dev_bytenr,
-                                                     mapped_data,
-                                                     bio->bi_io_vec[i].bv_len,
-                                                     bio, &bio_is_patched,
-                                                     NULL, rw);
+               }
+               btrfsic_process_written_block(dev_state, dev_bytenr,
+                                             mapped_datav, bio->bi_vcnt,
+                                             bio, &bio_is_patched,
+                                             NULL, rw);
+               while (i > 0) {
+                       i--;
                        kunmap(bio->bi_io_vec[i].bv_page);
-                       dev_bytenr += bio->bi_io_vec[i].bv_len;
                }
+               kfree(mapped_datav);
        } else if (NULL != dev_state && (rw & REQ_FLUSH)) {
                if (dev_state->state->print_mask &
                    BTRFSIC_PRINT_MASK_SUBMIT_BIO_BH)
                        printk(KERN_INFO
-                              "submit_bio(rw=0x%x) FLUSH, bdev=%p)\n",
+                              "submit_bio(rw=0x%x FLUSH, bdev=%p)\n",
                               rw, bio->bi_bdev);
                if (!dev_state->dummy_block_for_bio_bh_flush.is_iodone) {
                        if ((dev_state->state->print_mask &
@@ -2903,6 +3164,7 @@ void btrfsic_submit_bio(int rw, struct bio *bio)
                        bio->bi_end_io = btrfsic_bio_end_io;
                }
        }
+leave:
        mutex_unlock(&btrfsic_mutex);
 
        submit_bio(rw, bio);
@@ -2917,6 +3179,30 @@ int btrfsic_mount(struct btrfs_root *root,
        struct list_head *dev_head = &fs_devices->devices;
        struct btrfs_device *device;
 
+       if (root->nodesize != root->leafsize) {
+               printk(KERN_INFO
+                      "btrfsic: cannot handle nodesize %d != leafsize %d!\n",
+                      root->nodesize, root->leafsize);
+               return -1;
+       }
+       if (root->nodesize & ((u64)PAGE_CACHE_SIZE - 1)) {
+               printk(KERN_INFO
+                      "btrfsic: cannot handle nodesize %d not being a multiple of PAGE_CACHE_SIZE %ld!\n",
+                      root->nodesize, (unsigned long)PAGE_CACHE_SIZE);
+               return -1;
+       }
+       if (root->leafsize & ((u64)PAGE_CACHE_SIZE - 1)) {
+               printk(KERN_INFO
+                      "btrfsic: cannot handle leafsize %d not being a multiple of PAGE_CACHE_SIZE %ld!\n",
+                      root->leafsize, (unsigned long)PAGE_CACHE_SIZE);
+               return -1;
+       }
+       if (root->sectorsize & ((u64)PAGE_CACHE_SIZE - 1)) {
+               printk(KERN_INFO
+                      "btrfsic: cannot handle sectorsize %d not being a multiple of PAGE_CACHE_SIZE %ld!\n",
+                      root->sectorsize, (unsigned long)PAGE_CACHE_SIZE);
+               return -1;
+       }
        state = kzalloc(sizeof(*state), GFP_NOFS);
        if (NULL == state) {
                printk(KERN_INFO "btrfs check-integrity: kmalloc() failed!\n");
@@ -2933,6 +3219,8 @@ int btrfsic_mount(struct btrfs_root *root,
        state->print_mask = print_mask;
        state->include_extent_data = including_extent_data;
        state->csum_size = 0;
+       state->metablock_size = root->nodesize;
+       state->datablock_size = root->sectorsize;
        INIT_LIST_HEAD(&state->all_blocks_list);
        btrfsic_block_hashtable_init(&state->block_hashtable);
        btrfsic_block_link_hashtable_init(&state->block_link_hashtable);
@@ -3049,7 +3337,7 @@ void btrfsic_unmount(struct btrfs_root *root,
                                btrfsic_block_link_free(l);
                }
 
-               if (b_all->is_iodone)
+               if (b_all->is_iodone || b_all->never_written)
                        btrfsic_block_free(b_all);
                else
                        printk(KERN_INFO "btrfs: attempt to free %c-block"
index 4106264fbc655ac79b26efa1177384ea92b72988..d7a96cfdc50ae6a2d8afef1dad7ca3642248bbb8 100644 (file)
@@ -18,6 +18,7 @@
 
 #include <linux/sched.h>
 #include <linux/slab.h>
+#include <linux/rbtree.h>
 #include "ctree.h"
 #include "disk-io.h"
 #include "transaction.h"
@@ -37,7 +38,16 @@ static int balance_node_right(struct btrfs_trans_handle *trans,
                              struct extent_buffer *dst_buf,
                              struct extent_buffer *src_buf);
 static void del_ptr(struct btrfs_trans_handle *trans, struct btrfs_root *root,
-                  struct btrfs_path *path, int level, int slot);
+                   struct btrfs_path *path, int level, int slot,
+                   int tree_mod_log);
+static void tree_mod_log_free_eb(struct btrfs_fs_info *fs_info,
+                                struct extent_buffer *eb);
+struct extent_buffer *read_old_tree_block(struct btrfs_root *root, u64 bytenr,
+                                         u32 blocksize, u64 parent_transid,
+                                         u64 time_seq);
+struct extent_buffer *btrfs_find_old_tree_block(struct btrfs_root *root,
+                                               u64 bytenr, u32 blocksize,
+                                               u64 time_seq);
 
 struct btrfs_path *btrfs_alloc_path(void)
 {
@@ -255,7 +265,7 @@ int btrfs_copy_root(struct btrfs_trans_handle *trans,
 
        cow = btrfs_alloc_free_block(trans, root, buf->len, 0,
                                     new_root_objectid, &disk_key, level,
-                                    buf->start, 0, 1);
+                                    buf->start, 0);
        if (IS_ERR(cow))
                return PTR_ERR(cow);
 
@@ -288,6 +298,434 @@ int btrfs_copy_root(struct btrfs_trans_handle *trans,
        return 0;
 }
 
+enum mod_log_op {
+       MOD_LOG_KEY_REPLACE,
+       MOD_LOG_KEY_ADD,
+       MOD_LOG_KEY_REMOVE,
+       MOD_LOG_KEY_REMOVE_WHILE_FREEING,
+       MOD_LOG_KEY_REMOVE_WHILE_MOVING,
+       MOD_LOG_MOVE_KEYS,
+       MOD_LOG_ROOT_REPLACE,
+};
+
+struct tree_mod_move {
+       int dst_slot;
+       int nr_items;
+};
+
+struct tree_mod_root {
+       u64 logical;
+       u8 level;
+};
+
+struct tree_mod_elem {
+       struct rb_node node;
+       u64 index;              /* shifted logical */
+       struct seq_list elem;
+       enum mod_log_op op;
+
+       /* this is used for MOD_LOG_KEY_* and MOD_LOG_MOVE_KEYS operations */
+       int slot;
+
+       /* this is used for MOD_LOG_KEY* and MOD_LOG_ROOT_REPLACE */
+       u64 generation;
+
+       /* those are used for op == MOD_LOG_KEY_{REPLACE,REMOVE} */
+       struct btrfs_disk_key key;
+       u64 blockptr;
+
+       /* this is used for op == MOD_LOG_MOVE_KEYS */
+       struct tree_mod_move move;
+
+       /* this is used for op == MOD_LOG_ROOT_REPLACE */
+       struct tree_mod_root old_root;
+};
+
+static inline void
+__get_tree_mod_seq(struct btrfs_fs_info *fs_info, struct seq_list *elem)
+{
+       elem->seq = atomic_inc_return(&fs_info->tree_mod_seq);
+       list_add_tail(&elem->list, &fs_info->tree_mod_seq_list);
+}
+
+void btrfs_get_tree_mod_seq(struct btrfs_fs_info *fs_info,
+                           struct seq_list *elem)
+{
+       elem->flags = 1;
+       spin_lock(&fs_info->tree_mod_seq_lock);
+       __get_tree_mod_seq(fs_info, elem);
+       spin_unlock(&fs_info->tree_mod_seq_lock);
+}
+
+void btrfs_put_tree_mod_seq(struct btrfs_fs_info *fs_info,
+                           struct seq_list *elem)
+{
+       struct rb_root *tm_root;
+       struct rb_node *node;
+       struct rb_node *next;
+       struct seq_list *cur_elem;
+       struct tree_mod_elem *tm;
+       u64 min_seq = (u64)-1;
+       u64 seq_putting = elem->seq;
+
+       if (!seq_putting)
+               return;
+
+       BUG_ON(!(elem->flags & 1));
+       spin_lock(&fs_info->tree_mod_seq_lock);
+       list_del(&elem->list);
+
+       list_for_each_entry(cur_elem, &fs_info->tree_mod_seq_list, list) {
+               if ((cur_elem->flags & 1) && cur_elem->seq < min_seq) {
+                       if (seq_putting > cur_elem->seq) {
+                               /*
+                                * blocker with lower sequence number exists, we
+                                * cannot remove anything from the log
+                                */
+                               goto out;
+                       }
+                       min_seq = cur_elem->seq;
+               }
+       }
+
+       /*
+        * anything that's lower than the lowest existing (read: blocked)
+        * sequence number can be removed from the tree.
+        */
+       write_lock(&fs_info->tree_mod_log_lock);
+       tm_root = &fs_info->tree_mod_log;
+       for (node = rb_first(tm_root); node; node = next) {
+               next = rb_next(node);
+               tm = container_of(node, struct tree_mod_elem, node);
+               if (tm->elem.seq > min_seq)
+                       continue;
+               rb_erase(node, tm_root);
+               list_del(&tm->elem.list);
+               kfree(tm);
+       }
+       write_unlock(&fs_info->tree_mod_log_lock);
+out:
+       spin_unlock(&fs_info->tree_mod_seq_lock);
+}
+
+/*
+ * key order of the log:
+ *       index -> sequence
+ *
+ * the index is the shifted logical of the *new* root node for root replace
+ * operations, or the shifted logical of the affected block for all other
+ * operations.
+ */
+static noinline int
+__tree_mod_log_insert(struct btrfs_fs_info *fs_info, struct tree_mod_elem *tm)
+{
+       struct rb_root *tm_root;
+       struct rb_node **new;
+       struct rb_node *parent = NULL;
+       struct tree_mod_elem *cur;
+       int ret = 0;
+
+       BUG_ON(!tm || !tm->elem.seq);
+
+       write_lock(&fs_info->tree_mod_log_lock);
+       tm_root = &fs_info->tree_mod_log;
+       new = &tm_root->rb_node;
+       while (*new) {
+               cur = container_of(*new, struct tree_mod_elem, node);
+               parent = *new;
+               if (cur->index < tm->index)
+                       new = &((*new)->rb_left);
+               else if (cur->index > tm->index)
+                       new = &((*new)->rb_right);
+               else if (cur->elem.seq < tm->elem.seq)
+                       new = &((*new)->rb_left);
+               else if (cur->elem.seq > tm->elem.seq)
+                       new = &((*new)->rb_right);
+               else {
+                       kfree(tm);
+                       ret = -EEXIST;
+                       goto unlock;
+               }
+       }
+
+       rb_link_node(&tm->node, parent, new);
+       rb_insert_color(&tm->node, tm_root);
+unlock:
+       write_unlock(&fs_info->tree_mod_log_lock);
+       return ret;
+}
+
+static inline int tree_mod_dont_log(struct btrfs_fs_info *fs_info,
+                                   struct extent_buffer *eb) {
+       smp_mb();
+       if (list_empty(&(fs_info)->tree_mod_seq_list))
+               return 1;
+       if (!eb)
+               return 0;
+       if (btrfs_header_level(eb) == 0)
+               return 1;
+       return 0;
+}
+
+static inline int tree_mod_alloc(struct btrfs_fs_info *fs_info, gfp_t flags,
+                                struct tree_mod_elem **tm_ret)
+{
+       struct tree_mod_elem *tm;
+       int seq;
+
+       if (tree_mod_dont_log(fs_info, NULL))
+               return 0;
+
+       tm = *tm_ret = kzalloc(sizeof(*tm), flags);
+       if (!tm)
+               return -ENOMEM;
+
+       tm->elem.flags = 0;
+       spin_lock(&fs_info->tree_mod_seq_lock);
+       if (list_empty(&fs_info->tree_mod_seq_list)) {
+               /*
+                * someone emptied the list while we were waiting for the lock.
+                * we must not add to the list, because no blocker exists. items
+                * are removed from the list only when the existing blocker is
+                * removed from the list.
+                */
+               kfree(tm);
+               seq = 0;
+       } else {
+               __get_tree_mod_seq(fs_info, &tm->elem);
+               seq = tm->elem.seq;
+       }
+       spin_unlock(&fs_info->tree_mod_seq_lock);
+
+       return seq;
+}
+
+static noinline int
+tree_mod_log_insert_key_mask(struct btrfs_fs_info *fs_info,
+                            struct extent_buffer *eb, int slot,
+                            enum mod_log_op op, gfp_t flags)
+{
+       struct tree_mod_elem *tm;
+       int ret;
+
+       ret = tree_mod_alloc(fs_info, flags, &tm);
+       if (ret <= 0)
+               return ret;
+
+       tm->index = eb->start >> PAGE_CACHE_SHIFT;
+       if (op != MOD_LOG_KEY_ADD) {
+               btrfs_node_key(eb, &tm->key, slot);
+               tm->blockptr = btrfs_node_blockptr(eb, slot);
+       }
+       tm->op = op;
+       tm->slot = slot;
+       tm->generation = btrfs_node_ptr_generation(eb, slot);
+
+       return __tree_mod_log_insert(fs_info, tm);
+}
+
+static noinline int
+tree_mod_log_insert_key(struct btrfs_fs_info *fs_info, struct extent_buffer *eb,
+                       int slot, enum mod_log_op op)
+{
+       return tree_mod_log_insert_key_mask(fs_info, eb, slot, op, GFP_NOFS);
+}
+
+static noinline int
+tree_mod_log_insert_move(struct btrfs_fs_info *fs_info,
+                        struct extent_buffer *eb, int dst_slot, int src_slot,
+                        int nr_items, gfp_t flags)
+{
+       struct tree_mod_elem *tm;
+       int ret;
+       int i;
+
+       if (tree_mod_dont_log(fs_info, eb))
+               return 0;
+
+       for (i = 0; i + dst_slot < src_slot && i < nr_items; i++) {
+               ret = tree_mod_log_insert_key(fs_info, eb, i + dst_slot,
+                                             MOD_LOG_KEY_REMOVE_WHILE_MOVING);
+               BUG_ON(ret < 0);
+       }
+
+       ret = tree_mod_alloc(fs_info, flags, &tm);
+       if (ret <= 0)
+               return ret;
+
+       tm->index = eb->start >> PAGE_CACHE_SHIFT;
+       tm->slot = src_slot;
+       tm->move.dst_slot = dst_slot;
+       tm->move.nr_items = nr_items;
+       tm->op = MOD_LOG_MOVE_KEYS;
+
+       return __tree_mod_log_insert(fs_info, tm);
+}
+
+static noinline int
+tree_mod_log_insert_root(struct btrfs_fs_info *fs_info,
+                        struct extent_buffer *old_root,
+                        struct extent_buffer *new_root, gfp_t flags)
+{
+       struct tree_mod_elem *tm;
+       int ret;
+
+       ret = tree_mod_alloc(fs_info, flags, &tm);
+       if (ret <= 0)
+               return ret;
+
+       tm->index = new_root->start >> PAGE_CACHE_SHIFT;
+       tm->old_root.logical = old_root->start;
+       tm->old_root.level = btrfs_header_level(old_root);
+       tm->generation = btrfs_header_generation(old_root);
+       tm->op = MOD_LOG_ROOT_REPLACE;
+
+       return __tree_mod_log_insert(fs_info, tm);
+}
+
+static struct tree_mod_elem *
+__tree_mod_log_search(struct btrfs_fs_info *fs_info, u64 start, u64 min_seq,
+                     int smallest)
+{
+       struct rb_root *tm_root;
+       struct rb_node *node;
+       struct tree_mod_elem *cur = NULL;
+       struct tree_mod_elem *found = NULL;
+       u64 index = start >> PAGE_CACHE_SHIFT;
+
+       read_lock(&fs_info->tree_mod_log_lock);
+       tm_root = &fs_info->tree_mod_log;
+       node = tm_root->rb_node;
+       while (node) {
+               cur = container_of(node, struct tree_mod_elem, node);
+               if (cur->index < index) {
+                       node = node->rb_left;
+               } else if (cur->index > index) {
+                       node = node->rb_right;
+               } else if (cur->elem.seq < min_seq) {
+                       node = node->rb_left;
+               } else if (!smallest) {
+                       /* we want the node with the highest seq */
+                       if (found)
+                               BUG_ON(found->elem.seq > cur->elem.seq);
+                       found = cur;
+                       node = node->rb_left;
+               } else if (cur->elem.seq > min_seq) {
+                       /* we want the node with the smallest seq */
+                       if (found)
+                               BUG_ON(found->elem.seq < cur->elem.seq);
+                       found = cur;
+                       node = node->rb_right;
+               } else {
+                       found = cur;
+                       break;
+               }
+       }
+       read_unlock(&fs_info->tree_mod_log_lock);
+
+       return found;
+}
+
+/*
+ * this returns the element from the log with the smallest time sequence
+ * value that's in the log (the oldest log item). any element with a time
+ * sequence lower than min_seq will be ignored.
+ */
+static struct tree_mod_elem *
+tree_mod_log_search_oldest(struct btrfs_fs_info *fs_info, u64 start,
+                          u64 min_seq)
+{
+       return __tree_mod_log_search(fs_info, start, min_seq, 1);
+}
+
+/*
+ * this returns the element from the log with the largest time sequence
+ * value that's in the log (the most recent log item). any element with
+ * a time sequence lower than min_seq will be ignored.
+ */
+static struct tree_mod_elem *
+tree_mod_log_search(struct btrfs_fs_info *fs_info, u64 start, u64 min_seq)
+{
+       return __tree_mod_log_search(fs_info, start, min_seq, 0);
+}
+
+static inline void
+tree_mod_log_eb_copy(struct btrfs_fs_info *fs_info, struct extent_buffer *dst,
+                    struct extent_buffer *src, unsigned long dst_offset,
+                    unsigned long src_offset, int nr_items)
+{
+       int ret;
+       int i;
+
+       if (tree_mod_dont_log(fs_info, NULL))
+               return;
+
+       if (btrfs_header_level(dst) == 0 && btrfs_header_level(src) == 0)
+               return;
+
+       /* speed this up by single seq for all operations? */
+       for (i = 0; i < nr_items; i++) {
+               ret = tree_mod_log_insert_key(fs_info, src, i + src_offset,
+                                             MOD_LOG_KEY_REMOVE);
+               BUG_ON(ret < 0);
+               ret = tree_mod_log_insert_key(fs_info, dst, i + dst_offset,
+                                             MOD_LOG_KEY_ADD);
+               BUG_ON(ret < 0);
+       }
+}
+
+static inline void
+tree_mod_log_eb_move(struct btrfs_fs_info *fs_info, struct extent_buffer *dst,
+                    int dst_offset, int src_offset, int nr_items)
+{
+       int ret;
+       ret = tree_mod_log_insert_move(fs_info, dst, dst_offset, src_offset,
+                                      nr_items, GFP_NOFS);
+       BUG_ON(ret < 0);
+}
+
+static inline void
+tree_mod_log_set_node_key(struct btrfs_fs_info *fs_info,
+                         struct extent_buffer *eb,
+                         struct btrfs_disk_key *disk_key, int slot, int atomic)
+{
+       int ret;
+
+       ret = tree_mod_log_insert_key_mask(fs_info, eb, slot,
+                                          MOD_LOG_KEY_REPLACE,
+                                          atomic ? GFP_ATOMIC : GFP_NOFS);
+       BUG_ON(ret < 0);
+}
+
+static void tree_mod_log_free_eb(struct btrfs_fs_info *fs_info,
+                                struct extent_buffer *eb)
+{
+       int i;
+       int ret;
+       u32 nritems;
+
+       if (tree_mod_dont_log(fs_info, eb))
+               return;
+
+       nritems = btrfs_header_nritems(eb);
+       for (i = nritems - 1; i >= 0; i--) {
+               ret = tree_mod_log_insert_key(fs_info, eb, i,
+                                             MOD_LOG_KEY_REMOVE_WHILE_FREEING);
+               BUG_ON(ret < 0);
+       }
+}
+
+static inline void
+tree_mod_log_set_root_pointer(struct btrfs_root *root,
+                             struct extent_buffer *new_root_node)
+{
+       int ret;
+       tree_mod_log_free_eb(root->fs_info, root->node);
+       ret = tree_mod_log_insert_root(root->fs_info, root->node,
+                                      new_root_node, GFP_NOFS);
+       BUG_ON(ret < 0);
+}
+
 /*
  * check if the tree block can be shared by multiple trees
  */
@@ -409,6 +847,12 @@ static noinline int update_ref_for_cow(struct btrfs_trans_handle *trans,
                        ret = btrfs_dec_ref(trans, root, buf, 1, 1);
                        BUG_ON(ret); /* -ENOMEM */
                }
+               /*
+                * don't log freeing in case we're freeing the root node, this
+                * is done by tree_mod_log_set_root_pointer later
+                */
+               if (buf != root->node && btrfs_header_level(buf) != 0)
+                       tree_mod_log_free_eb(root->fs_info, buf);
                clean_tree_block(trans, root, buf);
                *last_ref = 1;
        }
@@ -467,7 +911,7 @@ static noinline int __btrfs_cow_block(struct btrfs_trans_handle *trans,
 
        cow = btrfs_alloc_free_block(trans, root, buf->len, parent_start,
                                     root->root_key.objectid, &disk_key,
-                                    level, search_start, empty_size, 1);
+                                    level, search_start, empty_size);
        if (IS_ERR(cow))
                return PTR_ERR(cow);
 
@@ -506,10 +950,11 @@ static noinline int __btrfs_cow_block(struct btrfs_trans_handle *trans,
                        parent_start = 0;
 
                extent_buffer_get(cow);
+               tree_mod_log_set_root_pointer(root, cow);
                rcu_assign_pointer(root->node, cow);
 
                btrfs_free_tree_block(trans, root, buf, parent_start,
-                                     last_ref, 1);
+                                     last_ref);
                free_extent_buffer(buf);
                add_root_to_dirty_list(root);
        } else {
@@ -519,13 +964,15 @@ static noinline int __btrfs_cow_block(struct btrfs_trans_handle *trans,
                        parent_start = 0;
 
                WARN_ON(trans->transid != btrfs_header_generation(parent));
+               tree_mod_log_insert_key(root->fs_info, parent, parent_slot,
+                                       MOD_LOG_KEY_REPLACE);
                btrfs_set_node_blockptr(parent, parent_slot,
                                        cow->start);
                btrfs_set_node_ptr_generation(parent, parent_slot,
                                              trans->transid);
                btrfs_mark_buffer_dirty(parent);
                btrfs_free_tree_block(trans, root, buf, parent_start,
-                                     last_ref, 1);
+                                     last_ref);
        }
        if (unlock_orig)
                btrfs_tree_unlock(buf);
@@ -535,6 +982,210 @@ static noinline int __btrfs_cow_block(struct btrfs_trans_handle *trans,
        return 0;
 }
 
+/*
+ * returns the logical address of the oldest predecessor of the given root.
+ * entries older than time_seq are ignored.
+ */
+static struct tree_mod_elem *
+__tree_mod_log_oldest_root(struct btrfs_fs_info *fs_info,
+                          struct btrfs_root *root, u64 time_seq)
+{
+       struct tree_mod_elem *tm;
+       struct tree_mod_elem *found = NULL;
+       u64 root_logical = root->node->start;
+       int looped = 0;
+
+       if (!time_seq)
+               return 0;
+
+       /*
+        * the very last operation that's logged for a root is the replacement
+        * operation (if it is replaced at all). this has the index of the *new*
+        * root, making it the very first operation that's logged for this root.
+        */
+       while (1) {
+               tm = tree_mod_log_search_oldest(fs_info, root_logical,
+                                               time_seq);
+               if (!looped && !tm)
+                       return 0;
+               /*
+                * we must have key remove operations in the log before the
+                * replace operation.
+                */
+               BUG_ON(!tm);
+
+               if (tm->op != MOD_LOG_ROOT_REPLACE)
+                       break;
+
+               found = tm;
+               root_logical = tm->old_root.logical;
+               BUG_ON(root_logical == root->node->start);
+               looped = 1;
+       }
+
+       return found;
+}
+
+/*
+ * tm is a pointer to the first operation to rewind within eb. then, all
+ * previous operations will be rewinded (until we reach something older than
+ * time_seq).
+ */
+static void
+__tree_mod_log_rewind(struct extent_buffer *eb, u64 time_seq,
+                     struct tree_mod_elem *first_tm)
+{
+       u32 n;
+       struct rb_node *next;
+       struct tree_mod_elem *tm = first_tm;
+       unsigned long o_dst;
+       unsigned long o_src;
+       unsigned long p_size = sizeof(struct btrfs_key_ptr);
+
+       n = btrfs_header_nritems(eb);
+       while (tm && tm->elem.seq >= time_seq) {
+               /*
+                * all the operations are recorded with the operator used for
+                * the modification. as we're going backwards, we do the
+                * opposite of each operation here.
+                */
+               switch (tm->op) {
+               case MOD_LOG_KEY_REMOVE_WHILE_FREEING:
+                       BUG_ON(tm->slot < n);
+               case MOD_LOG_KEY_REMOVE_WHILE_MOVING:
+               case MOD_LOG_KEY_REMOVE:
+                       btrfs_set_node_key(eb, &tm->key, tm->slot);
+                       btrfs_set_node_blockptr(eb, tm->slot, tm->blockptr);
+                       btrfs_set_node_ptr_generation(eb, tm->slot,
+                                                     tm->generation);
+                       n++;
+                       break;
+               case MOD_LOG_KEY_REPLACE:
+                       BUG_ON(tm->slot >= n);
+                       btrfs_set_node_key(eb, &tm->key, tm->slot);
+                       btrfs_set_node_blockptr(eb, tm->slot, tm->blockptr);
+                       btrfs_set_node_ptr_generation(eb, tm->slot,
+                                                     tm->generation);
+                       break;
+               case MOD_LOG_KEY_ADD:
+                       if (tm->slot != n - 1) {
+                               o_dst = btrfs_node_key_ptr_offset(tm->slot);
+                               o_src = btrfs_node_key_ptr_offset(tm->slot + 1);
+                               memmove_extent_buffer(eb, o_dst, o_src, p_size);
+                       }
+                       n--;
+                       break;
+               case MOD_LOG_MOVE_KEYS:
+                       o_dst = btrfs_node_key_ptr_offset(tm->slot);
+                       o_src = btrfs_node_key_ptr_offset(tm->move.dst_slot);
+                       memmove_extent_buffer(eb, o_dst, o_src,
+                                             tm->move.nr_items * p_size);
+                       break;
+               case MOD_LOG_ROOT_REPLACE:
+                       /*
+                        * this operation is special. for roots, this must be
+                        * handled explicitly before rewinding.
+                        * for non-roots, this operation may exist if the node
+                        * was a root: root A -> child B; then A gets empty and
+                        * B is promoted to the new root. in the mod log, we'll
+                        * have a root-replace operation for B, a tree block
+                        * that is no root. we simply ignore that operation.
+                        */
+                       break;
+               }
+               next = rb_next(&tm->node);
+               if (!next)
+                       break;
+               tm = container_of(next, struct tree_mod_elem, node);
+               if (tm->index != first_tm->index)
+                       break;
+       }
+       btrfs_set_header_nritems(eb, n);
+}
+
+static struct extent_buffer *
+tree_mod_log_rewind(struct btrfs_fs_info *fs_info, struct extent_buffer *eb,
+                   u64 time_seq)
+{
+       struct extent_buffer *eb_rewin;
+       struct tree_mod_elem *tm;
+
+       if (!time_seq)
+               return eb;
+
+       if (btrfs_header_level(eb) == 0)
+               return eb;
+
+       tm = tree_mod_log_search(fs_info, eb->start, time_seq);
+       if (!tm)
+               return eb;
+
+       if (tm->op == MOD_LOG_KEY_REMOVE_WHILE_FREEING) {
+               BUG_ON(tm->slot != 0);
+               eb_rewin = alloc_dummy_extent_buffer(eb->start,
+                                               fs_info->tree_root->nodesize);
+               BUG_ON(!eb_rewin);
+               btrfs_set_header_bytenr(eb_rewin, eb->start);
+               btrfs_set_header_backref_rev(eb_rewin,
+                                            btrfs_header_backref_rev(eb));
+               btrfs_set_header_owner(eb_rewin, btrfs_header_owner(eb));
+               btrfs_set_header_level(eb_rewin, btrfs_header_level(eb));
+       } else {
+               eb_rewin = btrfs_clone_extent_buffer(eb);
+               BUG_ON(!eb_rewin);
+       }
+
+       extent_buffer_get(eb_rewin);
+       free_extent_buffer(eb);
+
+       __tree_mod_log_rewind(eb_rewin, time_seq, tm);
+
+       return eb_rewin;
+}
+
+static inline struct extent_buffer *
+get_old_root(struct btrfs_root *root, u64 time_seq)
+{
+       struct tree_mod_elem *tm;
+       struct extent_buffer *eb;
+       struct tree_mod_root *old_root;
+       u64 old_generation;
+
+       tm = __tree_mod_log_oldest_root(root->fs_info, root, time_seq);
+       if (!tm)
+               return root->node;
+
+       old_root = &tm->old_root;
+       old_generation = tm->generation;
+
+       tm = tree_mod_log_search(root->fs_info, old_root->logical, time_seq);
+       /*
+        * there was an item in the log when __tree_mod_log_oldest_root
+        * returned. this one must not go away, because the time_seq passed to
+        * us must be blocking its removal.
+        */
+       BUG_ON(!tm);
+
+       if (old_root->logical == root->node->start) {
+               /* there are logged operations for the current root */
+               eb = btrfs_clone_extent_buffer(root->node);
+       } else {
+               /* there's a root replace operation for the current root */
+               eb = alloc_dummy_extent_buffer(tm->index << PAGE_CACHE_SHIFT,
+                                              root->nodesize);
+               btrfs_set_header_bytenr(eb, eb->start);
+               btrfs_set_header_backref_rev(eb, BTRFS_MIXED_BACKREF_REV);
+               btrfs_set_header_owner(eb, root->root_key.objectid);
+       }
+       if (!eb)
+               return NULL;
+       btrfs_set_header_level(eb, old_root->level);
+       btrfs_set_header_generation(eb, old_generation);
+       __tree_mod_log_rewind(eb, time_seq, tm);
+
+       return eb;
+}
+
 static inline int should_cow_block(struct btrfs_trans_handle *trans,
                                   struct btrfs_root *root,
                                   struct extent_buffer *buf)
@@ -739,7 +1390,11 @@ int btrfs_realloc_node(struct btrfs_trans_handle *trans,
                                if (!cur)
                                        return -EIO;
                        } else if (!uptodate) {
-                               btrfs_read_buffer(cur, gen);
+                               err = btrfs_read_buffer(cur, gen);
+                               if (err) {
+                                       free_extent_buffer(cur);
+                                       return err;
+                               }
                        }
                }
                if (search_start == 0)
@@ -854,20 +1509,18 @@ static noinline int generic_bin_search(struct extent_buffer *eb,
 static int bin_search(struct extent_buffer *eb, struct btrfs_key *key,
                      int level, int *slot)
 {
-       if (level == 0) {
+       if (level == 0)
                return generic_bin_search(eb,
                                          offsetof(struct btrfs_leaf, items),
                                          sizeof(struct btrfs_item),
                                          key, btrfs_header_nritems(eb),
                                          slot);
-       } else {
+       else
                return generic_bin_search(eb,
                                          offsetof(struct btrfs_node, ptrs),
                                          sizeof(struct btrfs_key_ptr),
                                          key, btrfs_header_nritems(eb),
                                          slot);
-       }
-       return -1;
 }
 
 int btrfs_bin_search(struct extent_buffer *eb, struct btrfs_key *key,
@@ -974,6 +1627,7 @@ static noinline int balance_level(struct btrfs_trans_handle *trans,
                        goto enospc;
                }
 
+               tree_mod_log_set_root_pointer(root, child);
                rcu_assign_pointer(root->node, child);
 
                add_root_to_dirty_list(root);
@@ -987,7 +1641,7 @@ static noinline int balance_level(struct btrfs_trans_handle *trans,
                free_extent_buffer(mid);
 
                root_sub_used(root, mid->len);
-               btrfs_free_tree_block(trans, root, mid, 0, 1, 0);
+               btrfs_free_tree_block(trans, root, mid, 0, 1);
                /* once for the root ptr */
                free_extent_buffer_stale(mid);
                return 0;
@@ -1040,14 +1694,16 @@ static noinline int balance_level(struct btrfs_trans_handle *trans,
                if (btrfs_header_nritems(right) == 0) {
                        clean_tree_block(trans, root, right);
                        btrfs_tree_unlock(right);
-                       del_ptr(trans, root, path, level + 1, pslot + 1);
+                       del_ptr(trans, root, path, level + 1, pslot + 1, 1);
                        root_sub_used(root, right->len);
-                       btrfs_free_tree_block(trans, root, right, 0, 1, 0);
+                       btrfs_free_tree_block(trans, root, right, 0, 1);
                        free_extent_buffer_stale(right);
                        right = NULL;
                } else {
                        struct btrfs_disk_key right_key;
                        btrfs_node_key(right, &right_key, 0);
+                       tree_mod_log_set_node_key(root->fs_info, parent,
+                                                 &right_key, pslot + 1, 0);
                        btrfs_set_node_key(parent, &right_key, pslot + 1);
                        btrfs_mark_buffer_dirty(parent);
                }
@@ -1082,15 +1738,17 @@ static noinline int balance_level(struct btrfs_trans_handle *trans,
        if (btrfs_header_nritems(mid) == 0) {
                clean_tree_block(trans, root, mid);
                btrfs_tree_unlock(mid);
-               del_ptr(trans, root, path, level + 1, pslot);
+               del_ptr(trans, root, path, level + 1, pslot, 1);
                root_sub_used(root, mid->len);
-               btrfs_free_tree_block(trans, root, mid, 0, 1, 0);
+               btrfs_free_tree_block(trans, root, mid, 0, 1);
                free_extent_buffer_stale(mid);
                mid = NULL;
        } else {
                /* update the parent key to reflect our changes */
                struct btrfs_disk_key mid_key;
                btrfs_node_key(mid, &mid_key, 0);
+               tree_mod_log_set_node_key(root->fs_info, parent, &mid_key,
+                                         pslot, 0);
                btrfs_set_node_key(parent, &mid_key, pslot);
                btrfs_mark_buffer_dirty(parent);
        }
@@ -1188,6 +1846,8 @@ static noinline int push_nodes_for_insert(struct btrfs_trans_handle *trans,
                        struct btrfs_disk_key disk_key;
                        orig_slot += left_nr;
                        btrfs_node_key(mid, &disk_key, 0);
+                       tree_mod_log_set_node_key(root->fs_info, parent,
+                                                 &disk_key, pslot, 0);
                        btrfs_set_node_key(parent, &disk_key, pslot);
                        btrfs_mark_buffer_dirty(parent);
                        if (btrfs_header_nritems(left) > orig_slot) {
@@ -1239,6 +1899,8 @@ static noinline int push_nodes_for_insert(struct btrfs_trans_handle *trans,
                        struct btrfs_disk_key disk_key;
 
                        btrfs_node_key(right, &disk_key, 0);
+                       tree_mod_log_set_node_key(root->fs_info, parent,
+                                                 &disk_key, pslot + 1, 0);
                        btrfs_set_node_key(parent, &disk_key, pslot + 1);
                        btrfs_mark_buffer_dirty(parent);
 
@@ -1496,7 +2158,7 @@ static int
 read_block_for_search(struct btrfs_trans_handle *trans,
                       struct btrfs_root *root, struct btrfs_path *p,
                       struct extent_buffer **eb_ret, int level, int slot,
-                      struct btrfs_key *key)
+                      struct btrfs_key *key, u64 time_seq)
 {
        u64 blocknr;
        u64 gen;
@@ -1850,7 +2512,7 @@ cow_done:
                        }
 
                        err = read_block_for_search(trans, root, p,
-                                                   &b, level, slot, key);
+                                                   &b, level, slot, key, 0);
                        if (err == -EAGAIN)
                                goto again;
                        if (err) {
@@ -1921,6 +2583,115 @@ done:
        return ret;
 }
 
+/*
+ * Like btrfs_search_slot, this looks for a key in the given tree. It uses the
+ * current state of the tree together with the operations recorded in the tree
+ * modification log to search for the key in a previous version of this tree, as
+ * denoted by the time_seq parameter.
+ *
+ * Naturally, there is no support for insert, delete or cow operations.
+ *
+ * The resulting path and return value will be set up as if we called
+ * btrfs_search_slot at that point in time with ins_len and cow both set to 0.
+ */
+int btrfs_search_old_slot(struct btrfs_root *root, struct btrfs_key *key,
+                         struct btrfs_path *p, u64 time_seq)
+{
+       struct extent_buffer *b;
+       int slot;
+       int ret;
+       int err;
+       int level;
+       int lowest_unlock = 1;
+       u8 lowest_level = 0;
+
+       lowest_level = p->lowest_level;
+       WARN_ON(p->nodes[0] != NULL);
+
+       if (p->search_commit_root) {
+               BUG_ON(time_seq);
+               return btrfs_search_slot(NULL, root, key, p, 0, 0);
+       }
+
+again:
+       b = get_old_root(root, time_seq);
+       extent_buffer_get(b);
+       level = btrfs_header_level(b);
+       btrfs_tree_read_lock(b);
+       p->locks[level] = BTRFS_READ_LOCK;
+
+       while (b) {
+               level = btrfs_header_level(b);
+               p->nodes[level] = b;
+               btrfs_clear_path_blocking(p, NULL, 0);
+
+               /*
+                * we have a lock on b and as long as we aren't changing
+                * the tree, there is no way to for the items in b to change.
+                * It is safe to drop the lock on our parent before we
+                * go through the expensive btree search on b.
+                */
+               btrfs_unlock_up_safe(p, level + 1);
+
+               ret = bin_search(b, key, level, &slot);
+
+               if (level != 0) {
+                       int dec = 0;
+                       if (ret && slot > 0) {
+                               dec = 1;
+                               slot -= 1;
+                       }
+                       p->slots[level] = slot;
+                       unlock_up(p, level, lowest_unlock, 0, NULL);
+
+                       if (level == lowest_level) {
+                               if (dec)
+                                       p->slots[level]++;
+                               goto done;
+                       }
+
+                       err = read_block_for_search(NULL, root, p, &b, level,
+                                                   slot, key, time_seq);
+                       if (err == -EAGAIN)
+                               goto again;
+                       if (err) {
+                               ret = err;
+                               goto done;
+                       }
+
+                       level = btrfs_header_level(b);
+                       err = btrfs_try_tree_read_lock(b);
+                       if (!err) {
+                               btrfs_set_path_blocking(p);
+                               btrfs_tree_read_lock(b);
+                               btrfs_clear_path_blocking(p, b,
+                                                         BTRFS_READ_LOCK);
+                       }
+                       p->locks[level] = BTRFS_READ_LOCK;
+                       p->nodes[level] = b;
+                       b = tree_mod_log_rewind(root->fs_info, b, time_seq);
+                       if (b != p->nodes[level]) {
+                               btrfs_tree_unlock_rw(p->nodes[level],
+                                                    p->locks[level]);
+                               p->locks[level] = 0;
+                               p->nodes[level] = b;
+                       }
+               } else {
+                       p->slots[level] = slot;
+                       unlock_up(p, level, lowest_unlock, 0, NULL);
+                       goto done;
+               }
+       }
+       ret = 1;
+done:
+       if (!p->leave_spinning)
+               btrfs_set_path_blocking(p);
+       if (ret < 0)
+               btrfs_release_path(p);
+
+       return ret;
+}
+
 /*
  * adjust the pointers going up the tree, starting at level
  * making sure the right key of each node is points to 'key'.
@@ -1941,6 +2712,7 @@ static void fixup_low_keys(struct btrfs_trans_handle *trans,
                if (!path->nodes[i])
                        break;
                t = path->nodes[i];
+               tree_mod_log_set_node_key(root->fs_info, t, key, tslot, 1);
                btrfs_set_node_key(t, key, tslot);
                btrfs_mark_buffer_dirty(path->nodes[i]);
                if (tslot != 0)
@@ -2023,12 +2795,16 @@ static int push_node_left(struct btrfs_trans_handle *trans,
        } else
                push_items = min(src_nritems - 8, push_items);
 
+       tree_mod_log_eb_copy(root->fs_info, dst, src, dst_nritems, 0,
+                            push_items);
        copy_extent_buffer(dst, src,
                           btrfs_node_key_ptr_offset(dst_nritems),
                           btrfs_node_key_ptr_offset(0),
                           push_items * sizeof(struct btrfs_key_ptr));
 
        if (push_items < src_nritems) {
+               tree_mod_log_eb_move(root->fs_info, src, 0, push_items,
+                                    src_nritems - push_items);
                memmove_extent_buffer(src, btrfs_node_key_ptr_offset(0),
                                      btrfs_node_key_ptr_offset(push_items),
                                      (src_nritems - push_items) *
@@ -2082,11 +2858,14 @@ static int balance_node_right(struct btrfs_trans_handle *trans,
        if (max_push < push_items)
                push_items = max_push;
 
+       tree_mod_log_eb_move(root->fs_info, dst, push_items, 0, dst_nritems);
        memmove_extent_buffer(dst, btrfs_node_key_ptr_offset(push_items),
                                      btrfs_node_key_ptr_offset(0),
                                      (dst_nritems) *
                                      sizeof(struct btrfs_key_ptr));
 
+       tree_mod_log_eb_copy(root->fs_info, dst, src, 0,
+                            src_nritems - push_items, push_items);
        copy_extent_buffer(dst, src,
                           btrfs_node_key_ptr_offset(0),
                           btrfs_node_key_ptr_offset(src_nritems - push_items),
@@ -2129,7 +2908,7 @@ static noinline int insert_new_root(struct btrfs_trans_handle *trans,
 
        c = btrfs_alloc_free_block(trans, root, root->nodesize, 0,
                                   root->root_key.objectid, &lower_key,
-                                  level, root->node->start, 0, 0);
+                                  level, root->node->start, 0);
        if (IS_ERR(c))
                return PTR_ERR(c);
 
@@ -2161,6 +2940,7 @@ static noinline int insert_new_root(struct btrfs_trans_handle *trans,
        btrfs_mark_buffer_dirty(c);
 
        old = root->node;
+       tree_mod_log_set_root_pointer(root, c);
        rcu_assign_pointer(root->node, c);
 
        /* the super has an extra ref to root->node */
@@ -2184,10 +2964,11 @@ static noinline int insert_new_root(struct btrfs_trans_handle *trans,
 static void insert_ptr(struct btrfs_trans_handle *trans,
                       struct btrfs_root *root, struct btrfs_path *path,
                       struct btrfs_disk_key *key, u64 bytenr,
-                      int slot, int level)
+                      int slot, int level, int tree_mod_log)
 {
        struct extent_buffer *lower;
        int nritems;
+       int ret;
 
        BUG_ON(!path->nodes[level]);
        btrfs_assert_tree_locked(path->nodes[level]);
@@ -2196,11 +2977,19 @@ static void insert_ptr(struct btrfs_trans_handle *trans,
        BUG_ON(slot > nritems);
        BUG_ON(nritems == BTRFS_NODEPTRS_PER_BLOCK(root));
        if (slot != nritems) {
+               if (tree_mod_log && level)
+                       tree_mod_log_eb_move(root->fs_info, lower, slot + 1,
+                                            slot, nritems - slot);
                memmove_extent_buffer(lower,
                              btrfs_node_key_ptr_offset(slot + 1),
                              btrfs_node_key_ptr_offset(slot),
                              (nritems - slot) * sizeof(struct btrfs_key_ptr));
        }
+       if (tree_mod_log && level) {
+               ret = tree_mod_log_insert_key(root->fs_info, lower, slot,
+                                             MOD_LOG_KEY_ADD);
+               BUG_ON(ret < 0);
+       }
        btrfs_set_node_key(lower, key, slot);
        btrfs_set_node_blockptr(lower, slot, bytenr);
        WARN_ON(trans->transid == 0);
@@ -2252,7 +3041,7 @@ static noinline int split_node(struct btrfs_trans_handle *trans,
 
        split = btrfs_alloc_free_block(trans, root, root->nodesize, 0,
                                        root->root_key.objectid,
-                                       &disk_key, level, c->start, 0, 0);
+                                       &disk_key, level, c->start, 0);
        if (IS_ERR(split))
                return PTR_ERR(split);
 
@@ -2271,7 +3060,7 @@ static noinline int split_node(struct btrfs_trans_handle *trans,
                            (unsigned long)btrfs_header_chunk_tree_uuid(split),
                            BTRFS_UUID_SIZE);
 
-
+       tree_mod_log_eb_copy(root->fs_info, split, c, 0, mid, c_nritems - mid);
        copy_extent_buffer(split, c,
                           btrfs_node_key_ptr_offset(0),
                           btrfs_node_key_ptr_offset(mid),
@@ -2284,7 +3073,7 @@ static noinline int split_node(struct btrfs_trans_handle *trans,
        btrfs_mark_buffer_dirty(split);
 
        insert_ptr(trans, root, path, &disk_key, split->start,
-                  path->slots[level + 1] + 1, level + 1);
+                  path->slots[level + 1] + 1, level + 1, 1);
 
        if (path->slots[level] >= mid) {
                path->slots[level] -= mid;
@@ -2821,7 +3610,7 @@ static noinline void copy_for_split(struct btrfs_trans_handle *trans,
        btrfs_set_header_nritems(l, mid);
        btrfs_item_key(right, &disk_key, 0);
        insert_ptr(trans, root, path, &disk_key, right->start,
-                  path->slots[1] + 1, 1);
+                  path->slots[1] + 1, 1, 0);
 
        btrfs_mark_buffer_dirty(right);
        btrfs_mark_buffer_dirty(l);
@@ -3004,7 +3793,7 @@ again:
 
        right = btrfs_alloc_free_block(trans, root, root->leafsize, 0,
                                        root->root_key.objectid,
-                                       &disk_key, 0, l->start, 0, 0);
+                                       &disk_key, 0, l->start, 0);
        if (IS_ERR(right))
                return PTR_ERR(right);
 
@@ -3028,7 +3817,7 @@ again:
                if (mid <= slot) {
                        btrfs_set_header_nritems(right, 0);
                        insert_ptr(trans, root, path, &disk_key, right->start,
-                                  path->slots[1] + 1, 1);
+                                  path->slots[1] + 1, 1, 0);
                        btrfs_tree_unlock(path->nodes[0]);
                        free_extent_buffer(path->nodes[0]);
                        path->nodes[0] = right;
@@ -3037,7 +3826,7 @@ again:
                } else {
                        btrfs_set_header_nritems(right, 0);
                        insert_ptr(trans, root, path, &disk_key, right->start,
-                                         path->slots[1], 1);
+                                         path->slots[1], 1, 0);
                        btrfs_tree_unlock(path->nodes[0]);
                        free_extent_buffer(path->nodes[0]);
                        path->nodes[0] = right;
@@ -3749,19 +4538,29 @@ int btrfs_insert_item(struct btrfs_trans_handle *trans, struct btrfs_root
  * empty a node.
  */
 static void del_ptr(struct btrfs_trans_handle *trans, struct btrfs_root *root,
-                   struct btrfs_path *path, int level, int slot)
+                   struct btrfs_path *path, int level, int slot,
+                   int tree_mod_log)
 {
        struct extent_buffer *parent = path->nodes[level];
        u32 nritems;
+       int ret;
 
        nritems = btrfs_header_nritems(parent);
        if (slot != nritems - 1) {
+               if (tree_mod_log && level)
+                       tree_mod_log_eb_move(root->fs_info, parent, slot,
+                                            slot + 1, nritems - slot - 1);
                memmove_extent_buffer(parent,
                              btrfs_node_key_ptr_offset(slot),
                              btrfs_node_key_ptr_offset(slot + 1),
                              sizeof(struct btrfs_key_ptr) *
                              (nritems - slot - 1));
+       } else if (tree_mod_log && level) {
+               ret = tree_mod_log_insert_key(root->fs_info, parent, slot,
+                                             MOD_LOG_KEY_REMOVE);
+               BUG_ON(ret < 0);
        }
+
        nritems--;
        btrfs_set_header_nritems(parent, nritems);
        if (nritems == 0 && parent == root->node) {
@@ -3793,7 +4592,7 @@ static noinline void btrfs_del_leaf(struct btrfs_trans_handle *trans,
                                    struct extent_buffer *leaf)
 {
        WARN_ON(btrfs_header_generation(leaf) != trans->transid);
-       del_ptr(trans, root, path, 1, path->slots[1]);
+       del_ptr(trans, root, path, 1, path->slots[1], 1);
 
        /*
         * btrfs_free_extent is expensive, we want to make sure we
@@ -3804,7 +4603,7 @@ static noinline void btrfs_del_leaf(struct btrfs_trans_handle *trans,
        root_sub_used(root, leaf->len);
 
        extent_buffer_get(leaf);
-       btrfs_free_tree_block(trans, root, leaf, 0, 1, 0);
+       btrfs_free_tree_block(trans, root, leaf, 0, 1);
        free_extent_buffer_stale(leaf);
 }
 /*
@@ -4271,7 +5070,7 @@ again:
                next = c;
                next_rw_lock = path->locks[level];
                ret = read_block_for_search(NULL, root, path, &next, level,
-                                           slot, &key);
+                                           slot, &key, 0);
                if (ret == -EAGAIN)
                        goto again;
 
@@ -4308,7 +5107,7 @@ again:
                        break;
 
                ret = read_block_for_search(NULL, root, path, &next, level,
-                                           0, &key);
+                                           0, &key, 0);
                if (ret == -EAGAIN)
                        goto again;
 
index 8fd72331d6008c100e48db1c808566eb382187b2..0236d03c6732569a48a561049ea5a861d473da65 100644 (file)
@@ -173,6 +173,9 @@ static int btrfs_csum_sizes[] = { 4, 0 };
 #define BTRFS_FT_XATTR         8
 #define BTRFS_FT_MAX           9
 
+/* ioprio of readahead is set to idle */
+#define BTRFS_IOPRIO_READA (IOPRIO_PRIO_VALUE(IOPRIO_CLASS_IDLE, 0))
+
 /*
  * The key defines the order in the tree, and so it also defines (optimal)
  * block layout.
@@ -823,6 +826,14 @@ struct btrfs_csum_item {
        u8 csum;
 } __attribute__ ((__packed__));
 
+struct btrfs_dev_stats_item {
+       /*
+        * grow this item struct at the end for future enhancements and keep
+        * the existing values unchanged
+        */
+       __le64 values[BTRFS_DEV_STAT_VALUES_MAX];
+} __attribute__ ((__packed__));
+
 /* different types of block groups (and chunks) */
 #define BTRFS_BLOCK_GROUP_DATA         (1ULL << 0)
 #define BTRFS_BLOCK_GROUP_SYSTEM       (1ULL << 1)
@@ -1129,6 +1140,15 @@ struct btrfs_fs_info {
        spinlock_t delayed_iput_lock;
        struct list_head delayed_iputs;
 
+       /* this protects tree_mod_seq_list */
+       spinlock_t tree_mod_seq_lock;
+       atomic_t tree_mod_seq;
+       struct list_head tree_mod_seq_list;
+
+       /* this protects tree_mod_log */
+       rwlock_t tree_mod_log_lock;
+       struct rb_root tree_mod_log;
+
        atomic_t nr_async_submits;
        atomic_t async_submit_draining;
        atomic_t nr_async_bios;
@@ -1375,7 +1395,7 @@ struct btrfs_root {
        struct list_head root_list;
 
        spinlock_t orphan_lock;
-       struct list_head orphan_list;
+       atomic_t orphan_inodes;
        struct btrfs_block_rsv *orphan_block_rsv;
        int orphan_item_inserted;
        int orphan_cleanup_state;
@@ -1507,6 +1527,12 @@ struct btrfs_ioctl_defrag_range_args {
 
 #define BTRFS_BALANCE_ITEM_KEY 248
 
+/*
+ * Persistantly stores the io stats in the device tree.
+ * One key for all stats, (0, BTRFS_DEV_STATS_KEY, devid).
+ */
+#define BTRFS_DEV_STATS_KEY    249
+
 /*
  * string items are for debugging.  They just store a short string of
  * data in the FS
@@ -2415,6 +2441,30 @@ static inline u32 btrfs_file_extent_inline_item_len(struct extent_buffer *eb,
        return btrfs_item_size(eb, e) - offset;
 }
 
+/* btrfs_dev_stats_item */
+static inline u64 btrfs_dev_stats_value(struct extent_buffer *eb,
+                                       struct btrfs_dev_stats_item *ptr,
+                                       int index)
+{
+       u64 val;
+
+       read_extent_buffer(eb, &val,
+                          offsetof(struct btrfs_dev_stats_item, values) +
+                           ((unsigned long)ptr) + (index * sizeof(u64)),
+                          sizeof(val));
+       return val;
+}
+
+static inline void btrfs_set_dev_stats_value(struct extent_buffer *eb,
+                                            struct btrfs_dev_stats_item *ptr,
+                                            int index, u64 val)
+{
+       write_extent_buffer(eb, &val,
+                           offsetof(struct btrfs_dev_stats_item, values) +
+                            ((unsigned long)ptr) + (index * sizeof(u64)),
+                           sizeof(val));
+}
+
 static inline struct btrfs_fs_info *btrfs_sb(struct super_block *sb)
 {
        return sb->s_fs_info;
@@ -2496,11 +2546,11 @@ struct extent_buffer *btrfs_alloc_free_block(struct btrfs_trans_handle *trans,
                                        struct btrfs_root *root, u32 blocksize,
                                        u64 parent, u64 root_objectid,
                                        struct btrfs_disk_key *key, int level,
-                                       u64 hint, u64 empty_size, int for_cow);
+                                       u64 hint, u64 empty_size);
 void btrfs_free_tree_block(struct btrfs_trans_handle *trans,
                           struct btrfs_root *root,
                           struct extent_buffer *buf,
-                          u64 parent, int last_ref, int for_cow);
+                          u64 parent, int last_ref);
 struct extent_buffer *btrfs_init_new_buffer(struct btrfs_trans_handle *trans,
                                            struct btrfs_root *root,
                                            u64 bytenr, u32 blocksize,
@@ -2659,6 +2709,8 @@ int btrfs_duplicate_item(struct btrfs_trans_handle *trans,
 int btrfs_search_slot(struct btrfs_trans_handle *trans, struct btrfs_root
                      *root, struct btrfs_key *key, struct btrfs_path *p, int
                      ins_len, int cow);
+int btrfs_search_old_slot(struct btrfs_root *root, struct btrfs_key *key,
+                         struct btrfs_path *p, u64 time_seq);
 int btrfs_realloc_node(struct btrfs_trans_handle *trans,
                       struct btrfs_root *root, struct extent_buffer *parent,
                       int start_slot, int cache_only, u64 *last_ret,
@@ -2922,7 +2974,6 @@ int btrfs_readpage(struct file *file, struct page *page);
 void btrfs_evict_inode(struct inode *inode);
 int btrfs_write_inode(struct inode *inode, struct writeback_control *wbc);
 int btrfs_dirty_inode(struct inode *inode);
-int btrfs_update_time(struct file *file);
 struct inode *btrfs_alloc_inode(struct super_block *sb);
 void btrfs_destroy_inode(struct inode *inode);
 int btrfs_drop_inode(struct inode *inode);
@@ -3098,4 +3149,23 @@ void btrfs_reada_detach(void *handle);
 int btree_readahead_hook(struct btrfs_root *root, struct extent_buffer *eb,
                         u64 start, int err);
 
+/* delayed seq elem */
+struct seq_list {
+       struct list_head list;
+       u64 seq;
+       u32 flags;
+};
+
+void btrfs_get_tree_mod_seq(struct btrfs_fs_info *fs_info,
+                           struct seq_list *elem);
+void btrfs_put_tree_mod_seq(struct btrfs_fs_info *fs_info,
+                           struct seq_list *elem);
+
+static inline int is_fstree(u64 rootid)
+{
+       if (rootid == BTRFS_FS_TREE_OBJECTID ||
+           (s64)rootid >= (s64)BTRFS_FIRST_FREE_OBJECTID)
+               return 1;
+       return 0;
+}
 #endif
index 03e3748d84d02407c19c6d46648667a56f13ba3e..c18d0442ae6daa69a564ebba400f9ad09573ea1d 100644 (file)
@@ -669,8 +669,8 @@ static int btrfs_delayed_inode_reserve_metadata(
                return ret;
        } else if (src_rsv == &root->fs_info->delalloc_block_rsv) {
                spin_lock(&BTRFS_I(inode)->lock);
-               if (BTRFS_I(inode)->delalloc_meta_reserved) {
-                       BTRFS_I(inode)->delalloc_meta_reserved = 0;
+               if (test_and_clear_bit(BTRFS_INODE_DELALLOC_META_RESERVED,
+                                      &BTRFS_I(inode)->runtime_flags)) {
                        spin_unlock(&BTRFS_I(inode)->lock);
                        release = true;
                        goto migrate;
@@ -1706,7 +1706,7 @@ static void fill_stack_inode_item(struct btrfs_trans_handle *trans,
        btrfs_set_stack_inode_nbytes(inode_item, inode_get_bytes(inode));
        btrfs_set_stack_inode_generation(inode_item,
                                         BTRFS_I(inode)->generation);
-       btrfs_set_stack_inode_sequence(inode_item, BTRFS_I(inode)->sequence);
+       btrfs_set_stack_inode_sequence(inode_item, inode->i_version);
        btrfs_set_stack_inode_transid(inode_item, trans->transid);
        btrfs_set_stack_inode_rdev(inode_item, inode->i_rdev);
        btrfs_set_stack_inode_flags(inode_item, BTRFS_I(inode)->flags);
@@ -1754,7 +1754,7 @@ int btrfs_fill_inode(struct inode *inode, u32 *rdev)
        set_nlink(inode, btrfs_stack_inode_nlink(inode_item));
        inode_set_bytes(inode, btrfs_stack_inode_nbytes(inode_item));
        BTRFS_I(inode)->generation = btrfs_stack_inode_generation(inode_item);
-       BTRFS_I(inode)->sequence = btrfs_stack_inode_sequence(inode_item);
+       inode->i_version = btrfs_stack_inode_sequence(inode_item);
        inode->i_rdev = 0;
        *rdev = btrfs_stack_inode_rdev(inode_item);
        BTRFS_I(inode)->flags = btrfs_stack_inode_flags(inode_item);
index 69f22e3ab3bc307974b5cae14f99310a498b54cf..13ae7b04790eaff72e8c23fb145fca8bfae88175 100644 (file)
@@ -525,7 +525,7 @@ static noinline void add_delayed_tree_ref(struct btrfs_fs_info *fs_info,
        ref->is_head = 0;
        ref->in_tree = 1;
 
-       if (need_ref_seq(for_cow, ref_root))
+       if (is_fstree(ref_root))
                seq = inc_delayed_seq(delayed_refs);
        ref->seq = seq;
 
@@ -584,7 +584,7 @@ static noinline void add_delayed_data_ref(struct btrfs_fs_info *fs_info,
        ref->is_head = 0;
        ref->in_tree = 1;
 
-       if (need_ref_seq(for_cow, ref_root))
+       if (is_fstree(ref_root))
                seq = inc_delayed_seq(delayed_refs);
        ref->seq = seq;
 
@@ -658,10 +658,11 @@ int btrfs_add_delayed_tree_ref(struct btrfs_fs_info *fs_info,
        add_delayed_tree_ref(fs_info, trans, &ref->node, bytenr,
                                   num_bytes, parent, ref_root, level, action,
                                   for_cow);
-       if (!need_ref_seq(for_cow, ref_root) &&
+       if (!is_fstree(ref_root) &&
            waitqueue_active(&delayed_refs->seq_wait))
                wake_up(&delayed_refs->seq_wait);
        spin_unlock(&delayed_refs->lock);
+
        return 0;
 }
 
@@ -706,10 +707,11 @@ int btrfs_add_delayed_data_ref(struct btrfs_fs_info *fs_info,
        add_delayed_data_ref(fs_info, trans, &ref->node, bytenr,
                                   num_bytes, parent, ref_root, owner, offset,
                                   action, for_cow);
-       if (!need_ref_seq(for_cow, ref_root) &&
+       if (!is_fstree(ref_root) &&
            waitqueue_active(&delayed_refs->seq_wait))
                wake_up(&delayed_refs->seq_wait);
        spin_unlock(&delayed_refs->lock);
+
        return 0;
 }
 
index d8f244d9492511e3b108b26bcf4da1bc9fbf6826..413927fb9957e41fdcfb82511e63d416b8a36c76 100644 (file)
@@ -195,11 +195,6 @@ int btrfs_delayed_ref_lock(struct btrfs_trans_handle *trans,
 int btrfs_find_ref_cluster(struct btrfs_trans_handle *trans,
                           struct list_head *cluster, u64 search_start);
 
-struct seq_list {
-       struct list_head list;
-       u64 seq;
-};
-
 static inline u64 inc_delayed_seq(struct btrfs_delayed_ref_root *delayed_refs)
 {
        assert_spin_locked(&delayed_refs->lock);
@@ -229,25 +224,6 @@ btrfs_put_delayed_seq(struct btrfs_delayed_ref_root *delayed_refs,
 int btrfs_check_delayed_seq(struct btrfs_delayed_ref_root *delayed_refs,
                            u64 seq);
 
-/*
- * delayed refs with a ref_seq > 0 must be held back during backref walking.
- * this only applies to items in one of the fs-trees. for_cow items never need
- * to be held back, so they won't get a ref_seq number.
- */
-static inline int need_ref_seq(int for_cow, u64 rootid)
-{
-       if (for_cow)
-               return 0;
-
-       if (rootid == BTRFS_FS_TREE_OBJECTID)
-               return 1;
-
-       if ((s64)rootid >= (s64)BTRFS_FIRST_FREE_OBJECTID)
-               return 1;
-
-       return 0;
-}
-
 /*
  * a node might live in a head or a regular ref, this lets you
  * test for the proper type to use.
index e1fe74a2ce16e6a4e0b38129160f484e642c42fa..7ae51decf6d3d0fb5c3d44bb7791f843c74aa376 100644 (file)
@@ -1153,7 +1153,6 @@ static void __setup_root(u32 nodesize, u32 leafsize, u32 sectorsize,
        root->orphan_block_rsv = NULL;
 
        INIT_LIST_HEAD(&root->dirty_list);
-       INIT_LIST_HEAD(&root->orphan_list);
        INIT_LIST_HEAD(&root->root_list);
        spin_lock_init(&root->orphan_lock);
        spin_lock_init(&root->inode_lock);
@@ -1166,6 +1165,7 @@ static void __setup_root(u32 nodesize, u32 leafsize, u32 sectorsize,
        atomic_set(&root->log_commit[0], 0);
        atomic_set(&root->log_commit[1], 0);
        atomic_set(&root->log_writers, 0);
+       atomic_set(&root->orphan_inodes, 0);
        root->log_batch = 0;
        root->log_transid = 0;
        root->last_log_commit = 0;
@@ -1252,7 +1252,7 @@ static struct btrfs_root *alloc_log_tree(struct btrfs_trans_handle *trans,
 
        leaf = btrfs_alloc_free_block(trans, root, root->leafsize, 0,
                                      BTRFS_TREE_LOG_OBJECTID, NULL,
-                                     0, 0, 0, 0);
+                                     0, 0, 0);
        if (IS_ERR(leaf)) {
                kfree(root);
                return ERR_CAST(leaf);
@@ -1914,11 +1914,14 @@ int open_ctree(struct super_block *sb,
        spin_lock_init(&fs_info->delayed_iput_lock);
        spin_lock_init(&fs_info->defrag_inodes_lock);
        spin_lock_init(&fs_info->free_chunk_lock);
+       spin_lock_init(&fs_info->tree_mod_seq_lock);
+       rwlock_init(&fs_info->tree_mod_log_lock);
        mutex_init(&fs_info->reloc_mutex);
 
        init_completion(&fs_info->kobj_unregister);
        INIT_LIST_HEAD(&fs_info->dirty_cowonly_roots);
        INIT_LIST_HEAD(&fs_info->space_info);
+       INIT_LIST_HEAD(&fs_info->tree_mod_seq_list);
        btrfs_mapping_init(&fs_info->mapping_tree);
        btrfs_init_block_rsv(&fs_info->global_block_rsv);
        btrfs_init_block_rsv(&fs_info->delalloc_block_rsv);
@@ -1931,12 +1934,14 @@ int open_ctree(struct super_block *sb,
        atomic_set(&fs_info->async_submit_draining, 0);
        atomic_set(&fs_info->nr_async_bios, 0);
        atomic_set(&fs_info->defrag_running, 0);
+       atomic_set(&fs_info->tree_mod_seq, 0);
        fs_info->sb = sb;
        fs_info->max_inline = 8192 * 1024;
        fs_info->metadata_ratio = 0;
        fs_info->defrag_inodes = RB_ROOT;
        fs_info->trans_no_join = 0;
        fs_info->free_chunk_space = 0;
+       fs_info->tree_mod_log = RB_ROOT;
 
        /* readahead state */
        INIT_RADIX_TREE(&fs_info->reada_tree, GFP_NOFS & ~__GFP_WAIT);
@@ -2001,7 +2006,8 @@ int open_ctree(struct super_block *sb,
        BTRFS_I(fs_info->btree_inode)->root = tree_root;
        memset(&BTRFS_I(fs_info->btree_inode)->location, 0,
               sizeof(struct btrfs_key));
-       BTRFS_I(fs_info->btree_inode)->dummy_inode = 1;
+       set_bit(BTRFS_INODE_DUMMY,
+               &BTRFS_I(fs_info->btree_inode)->runtime_flags);
        insert_inode_hash(fs_info->btree_inode);
 
        spin_lock_init(&fs_info->block_group_cache_lock);
@@ -2353,6 +2359,13 @@ retry_root_backup:
        fs_info->generation = generation;
        fs_info->last_trans_committed = generation;
 
+       ret = btrfs_init_dev_stats(fs_info);
+       if (ret) {
+               printk(KERN_ERR "btrfs: failed to init dev_stats: %d\n",
+                      ret);
+               goto fail_block_groups;
+       }
+
        ret = btrfs_init_space_info(fs_info);
        if (ret) {
                printk(KERN_ERR "Failed to initial space info: %d\n", ret);
@@ -2556,18 +2569,19 @@ recovery_tree_root:
 
 static void btrfs_end_buffer_write_sync(struct buffer_head *bh, int uptodate)
 {
-       char b[BDEVNAME_SIZE];
-
        if (uptodate) {
                set_buffer_uptodate(bh);
        } else {
+               struct btrfs_device *device = (struct btrfs_device *)
+                       bh->b_private;
+
                printk_ratelimited(KERN_WARNING "lost page write due to "
-                                       "I/O error on %s\n",
-                                      bdevname(bh->b_bdev, b));
+                                  "I/O error on %s\n", device->name);
                /* note, we dont' set_buffer_write_io_error because we have
                 * our own ways of dealing with the IO errors
                 */
                clear_buffer_uptodate(bh);
+               btrfs_dev_stat_inc_and_print(device, BTRFS_DEV_STAT_WRITE_ERRS);
        }
        unlock_buffer(bh);
        put_bh(bh);
@@ -2682,6 +2696,7 @@ static int write_dev_supers(struct btrfs_device *device,
                        set_buffer_uptodate(bh);
                        lock_buffer(bh);
                        bh->b_end_io = btrfs_end_buffer_write_sync;
+                       bh->b_private = device;
                }
 
                /*
@@ -2740,6 +2755,9 @@ static int write_dev_flush(struct btrfs_device *device, int wait)
                }
                if (!bio_flagged(bio, BIO_UPTODATE)) {
                        ret = -EIO;
+                       if (!bio_flagged(bio, BIO_EOPNOTSUPP))
+                               btrfs_dev_stat_inc_and_print(device,
+                                       BTRFS_DEV_STAT_FLUSH_ERRS);
                }
 
                /* drop the reference from the wait == 0 run */
@@ -2902,19 +2920,6 @@ int write_ctree_super(struct btrfs_trans_handle *trans,
        return ret;
 }
 
-/* Kill all outstanding I/O */
-void btrfs_abort_devices(struct btrfs_root *root)
-{
-       struct list_head *head;
-       struct btrfs_device *dev;
-       mutex_lock(&root->fs_info->fs_devices->device_list_mutex);
-       head = &root->fs_info->fs_devices->devices;
-       list_for_each_entry_rcu(dev, head, dev_list) {
-               blk_abort_queue(dev->bdev->bd_disk->queue);
-       }
-       mutex_unlock(&root->fs_info->fs_devices->device_list_mutex);
-}
-
 void btrfs_free_fs_root(struct btrfs_fs_info *fs_info, struct btrfs_root *root)
 {
        spin_lock(&fs_info->fs_roots_radix_lock);
@@ -3671,17 +3676,6 @@ int btrfs_cleanup_transaction(struct btrfs_root *root)
        return 0;
 }
 
-static int btree_writepage_io_failed_hook(struct bio *bio, struct page *page,
-                                         u64 start, u64 end,
-                                         struct extent_state *state)
-{
-       struct super_block *sb = page->mapping->host->i_sb;
-       struct btrfs_fs_info *fs_info = btrfs_sb(sb);
-       btrfs_error(fs_info, -EIO,
-                   "Error occured while writing out btree at %llu", start);
-       return -EIO;
-}
-
 static struct extent_io_ops btree_extent_io_ops = {
        .write_cache_pages_lock_hook = btree_lock_page_hook,
        .readpage_end_io_hook = btree_readpage_end_io_hook,
@@ -3689,5 +3683,4 @@ static struct extent_io_ops btree_extent_io_ops = {
        .submit_bio_hook = btree_submit_bio_hook,
        /* note we're sharing with inode.c for the merge bio hook */
        .merge_bio_hook = btrfs_merge_bio_hook,
-       .writepage_io_failed_hook = btree_writepage_io_failed_hook,
 };
index ab1830aaf0edbffba6a0cef86d13e9b3f2742cda..05b3fab39f7e814fc8c958e125f5a14c7e39d7f9 100644 (file)
@@ -89,7 +89,6 @@ int btrfs_add_log_tree(struct btrfs_trans_handle *trans,
 int btrfs_cleanup_transaction(struct btrfs_root *root);
 void btrfs_cleanup_one_transaction(struct btrfs_transaction *trans,
                                  struct btrfs_root *root);
-void btrfs_abort_devices(struct btrfs_root *root);
 
 #ifdef CONFIG_DEBUG_LOCK_ALLOC
 void btrfs_init_lockdep(void);
index e887ee62b6d4ba0a98f7e2437323eecfca88bf23..614f34a899c2db468792f1ef8406c5a366739258 100644 (file)
                                             parent_root_objectid) / 4)
 #define BTRFS_FID_SIZE_CONNECTABLE_ROOT (sizeof(struct btrfs_fid) / 4)
 
-static int btrfs_encode_fh(struct dentry *dentry, u32 *fh, int *max_len,
-                          int connectable)
+static int btrfs_encode_fh(struct inode *inode, u32 *fh, int *max_len,
+                          struct inode *parent)
 {
        struct btrfs_fid *fid = (struct btrfs_fid *)fh;
-       struct inode *inode = dentry->d_inode;
        int len = *max_len;
        int type;
 
-       if (connectable && (len < BTRFS_FID_SIZE_CONNECTABLE)) {
+       if (parent && (len < BTRFS_FID_SIZE_CONNECTABLE)) {
                *max_len = BTRFS_FID_SIZE_CONNECTABLE;
                return 255;
        } else if (len < BTRFS_FID_SIZE_NON_CONNECTABLE) {
@@ -36,19 +35,13 @@ static int btrfs_encode_fh(struct dentry *dentry, u32 *fh, int *max_len,
        fid->root_objectid = BTRFS_I(inode)->root->objectid;
        fid->gen = inode->i_generation;
 
-       if (connectable && !S_ISDIR(inode->i_mode)) {
-               struct inode *parent;
+       if (parent) {
                u64 parent_root_id;
 
-               spin_lock(&dentry->d_lock);
-
-               parent = dentry->d_parent->d_inode;
                fid->parent_objectid = BTRFS_I(parent)->location.objectid;
                fid->parent_gen = parent->i_generation;
                parent_root_id = BTRFS_I(parent)->root->objectid;
 
-               spin_unlock(&dentry->d_lock);
-
                if (parent_root_id != fid->root_objectid) {
                        fid->parent_root_objectid = parent_root_id;
                        len = BTRFS_FID_SIZE_CONNECTABLE_ROOT;
index 49fd7b66d57b272c7aeaea7db4b1bbd0985f8aa2..4b5a1e1bdefbe095c239b464e55c9699e865175b 100644 (file)
@@ -3578,7 +3578,7 @@ again:
        space_info->chunk_alloc = 0;
        spin_unlock(&space_info->lock);
 out:
-       mutex_unlock(&extent_root->fs_info->chunk_mutex);
+       mutex_unlock(&fs_info->chunk_mutex);
        return ret;
 }
 
@@ -4355,10 +4355,9 @@ static unsigned drop_outstanding_extent(struct inode *inode)
        BTRFS_I(inode)->outstanding_extents--;
 
        if (BTRFS_I(inode)->outstanding_extents == 0 &&
-           BTRFS_I(inode)->delalloc_meta_reserved) {
+           test_and_clear_bit(BTRFS_INODE_DELALLOC_META_RESERVED,
+                              &BTRFS_I(inode)->runtime_flags))
                drop_inode_space = 1;
-               BTRFS_I(inode)->delalloc_meta_reserved = 0;
-       }
 
        /*
         * If we have more or the same amount of outsanding extents than we have
@@ -4465,7 +4464,8 @@ int btrfs_delalloc_reserve_metadata(struct inode *inode, u64 num_bytes)
         * Add an item to reserve for updating the inode when we complete the
         * delalloc io.
         */
-       if (!BTRFS_I(inode)->delalloc_meta_reserved) {
+       if (!test_bit(BTRFS_INODE_DELALLOC_META_RESERVED,
+                     &BTRFS_I(inode)->runtime_flags)) {
                nr_extents++;
                extra_reserve = 1;
        }
@@ -4511,7 +4511,8 @@ int btrfs_delalloc_reserve_metadata(struct inode *inode, u64 num_bytes)
 
        spin_lock(&BTRFS_I(inode)->lock);
        if (extra_reserve) {
-               BTRFS_I(inode)->delalloc_meta_reserved = 1;
+               set_bit(BTRFS_INODE_DELALLOC_META_RESERVED,
+                       &BTRFS_I(inode)->runtime_flags);
                nr_extents--;
        }
        BTRFS_I(inode)->reserved_extents += nr_extents;
@@ -5217,7 +5218,7 @@ out:
 void btrfs_free_tree_block(struct btrfs_trans_handle *trans,
                           struct btrfs_root *root,
                           struct extent_buffer *buf,
-                          u64 parent, int last_ref, int for_cow)
+                          u64 parent, int last_ref)
 {
        struct btrfs_block_group_cache *cache = NULL;
        int ret;
@@ -5227,7 +5228,7 @@ void btrfs_free_tree_block(struct btrfs_trans_handle *trans,
                                        buf->start, buf->len,
                                        parent, root->root_key.objectid,
                                        btrfs_header_level(buf),
-                                       BTRFS_DROP_DELAYED_REF, NULL, for_cow);
+                                       BTRFS_DROP_DELAYED_REF, NULL, 0);
                BUG_ON(ret); /* -ENOMEM */
        }
 
@@ -6249,7 +6250,7 @@ struct extent_buffer *btrfs_alloc_free_block(struct btrfs_trans_handle *trans,
                                        struct btrfs_root *root, u32 blocksize,
                                        u64 parent, u64 root_objectid,
                                        struct btrfs_disk_key *key, int level,
-                                       u64 hint, u64 empty_size, int for_cow)
+                                       u64 hint, u64 empty_size)
 {
        struct btrfs_key ins;
        struct btrfs_block_rsv *block_rsv;
@@ -6297,7 +6298,7 @@ struct extent_buffer *btrfs_alloc_free_block(struct btrfs_trans_handle *trans,
                                        ins.objectid,
                                        ins.offset, parent, root_objectid,
                                        level, BTRFS_ADD_DELAYED_EXTENT,
-                                       extent_op, for_cow);
+                                       extent_op, 0);
                BUG_ON(ret); /* -ENOMEM */
        }
        return buf;
@@ -6715,7 +6716,7 @@ static noinline int walk_up_proc(struct btrfs_trans_handle *trans,
                               btrfs_header_owner(path->nodes[level + 1]));
        }
 
-       btrfs_free_tree_block(trans, root, eb, parent, wc->refs[level] == 1, 0);
+       btrfs_free_tree_block(trans, root, eb, parent, wc->refs[level] == 1);
 out:
        wc->refs[level] = 0;
        wc->flags[level] = 0;
index c9018a05036e943a52ad91d81019bb4b934b6b9a..2c8f7b2046173954f720125a6e53e96de3c7727e 100644 (file)
@@ -186,7 +186,6 @@ static struct rb_node *tree_insert(struct rb_root *root, u64 offset,
                        return parent;
        }
 
-       entry = rb_entry(node, struct tree_entry, rb_node);
        rb_link_node(node, parent, p);
        rb_insert_color(node, root);
        return NULL;
@@ -413,7 +412,7 @@ static struct extent_state *next_state(struct extent_state *state)
 
 /*
  * utility function to clear some bits in an extent state struct.
- * it will optionally wake up any one waiting on this state (wake == 1)
+ * it will optionally wake up any one waiting on this state (wake == 1).
  *
  * If no bits are set on the state struct after clearing things, the
  * struct is freed and removed from the tree
@@ -570,10 +569,8 @@ hit_next:
                if (err)
                        goto out;
                if (state->end <= end) {
-                       clear_state_bit(tree, state, &bits, wake);
-                       if (last_end == (u64)-1)
-                               goto out;
-                       start = last_end + 1;
+                       state = clear_state_bit(tree, state, &bits, wake);
+                       goto next;
                }
                goto search_again;
        }
@@ -781,7 +778,6 @@ hit_next:
         * Just lock what we found and keep going
         */
        if (state->start == start && state->end <= end) {
-               struct rb_node *next_node;
                if (state->state & exclusive_bits) {
                        *failed_start = state->start;
                        err = -EEXIST;
@@ -789,20 +785,15 @@ hit_next:
                }
 
                set_state_bits(tree, state, &bits);
-
                cache_state(state, cached_state);
                merge_state(tree, state);
                if (last_end == (u64)-1)
                        goto out;
-
                start = last_end + 1;
-               next_node = rb_next(&state->rb_node);
-               if (next_node && start < end && prealloc && !need_resched()) {
-                       state = rb_entry(next_node, struct extent_state,
-                                        rb_node);
-                       if (state->start == start)
-                               goto hit_next;
-               }
+               state = next_state(state);
+               if (start < end && state && state->start == start &&
+                   !need_resched())
+                       goto hit_next;
                goto search_again;
        }
 
@@ -845,6 +836,10 @@ hit_next:
                        if (last_end == (u64)-1)
                                goto out;
                        start = last_end + 1;
+                       state = next_state(state);
+                       if (start < end && state && state->start == start &&
+                           !need_resched())
+                               goto hit_next;
                }
                goto search_again;
        }
@@ -994,21 +989,14 @@ hit_next:
         * Just lock what we found and keep going
         */
        if (state->start == start && state->end <= end) {
-               struct rb_node *next_node;
-
                set_state_bits(tree, state, &bits);
-               clear_state_bit(tree, state, &clear_bits, 0);
+               state = clear_state_bit(tree, state, &clear_bits, 0);
                if (last_end == (u64)-1)
                        goto out;
-
                start = last_end + 1;
-               next_node = rb_next(&state->rb_node);
-               if (next_node && start < end && prealloc && !need_resched()) {
-                       state = rb_entry(next_node, struct extent_state,
-                                        rb_node);
-                       if (state->start == start)
-                               goto hit_next;
-               }
+               if (start < end && state && state->start == start &&
+                   !need_resched())
+                       goto hit_next;
                goto search_again;
        }
 
@@ -1042,10 +1030,13 @@ hit_next:
                        goto out;
                if (state->end <= end) {
                        set_state_bits(tree, state, &bits);
-                       clear_state_bit(tree, state, &clear_bits, 0);
+                       state = clear_state_bit(tree, state, &clear_bits, 0);
                        if (last_end == (u64)-1)
                                goto out;
                        start = last_end + 1;
+                       if (start < end && state && state->start == start &&
+                           !need_resched())
+                               goto hit_next;
                }
                goto search_again;
        }
@@ -1173,9 +1164,8 @@ int set_extent_uptodate(struct extent_io_tree *tree, u64 start, u64 end,
                              cached_state, mask);
 }
 
-static int clear_extent_uptodate(struct extent_io_tree *tree, u64 start,
-                                u64 end, struct extent_state **cached_state,
-                                gfp_t mask)
+int clear_extent_uptodate(struct extent_io_tree *tree, u64 start, u64 end,
+                         struct extent_state **cached_state, gfp_t mask)
 {
        return clear_extent_bit(tree, start, end, EXTENT_UPTODATE, 0, 0,
                                cached_state, mask);
@@ -1293,7 +1283,7 @@ out:
  * returned if we find something, and *start_ret and *end_ret are
  * set to reflect the state struct that was found.
  *
- * If nothing was found, 1 is returned, < 0 on error
+ * If nothing was found, 1 is returned. If found something, return 0.
  */
 int find_first_extent_bit(struct extent_io_tree *tree, u64 start,
                          u64 *start_ret, u64 *end_ret, int bits)
@@ -1923,6 +1913,7 @@ int repair_io_failure(struct btrfs_mapping_tree *map_tree, u64 start,
        if (!test_bit(BIO_UPTODATE, &bio->bi_flags)) {
                /* try to remap that extent elsewhere? */
                bio_put(bio);
+               btrfs_dev_stat_inc_and_print(dev, BTRFS_DEV_STAT_WRITE_ERRS);
                return -EIO;
        }
 
@@ -2222,17 +2213,7 @@ int end_extent_writepage(struct page *page, int err, u64 start, u64 end)
                        uptodate = 0;
        }
 
-       if (!uptodate && tree->ops &&
-           tree->ops->writepage_io_failed_hook) {
-               ret = tree->ops->writepage_io_failed_hook(NULL, page,
-                                                start, end, NULL);
-               /* Writeback already completed */
-               if (ret == 0)
-                       return 1;
-       }
-
        if (!uptodate) {
-               clear_extent_uptodate(tree, start, end, NULL, GFP_NOFS);
                ClearPageUptodate(page);
                SetPageError(page);
        }
@@ -2347,10 +2328,23 @@ static void end_bio_extent_readpage(struct bio *bio, int err)
                if (uptodate && tree->ops && tree->ops->readpage_end_io_hook) {
                        ret = tree->ops->readpage_end_io_hook(page, start, end,
                                                              state, mirror);
-                       if (ret)
+                       if (ret) {
+                               /* no IO indicated but software detected errors
+                                * in the block, either checksum errors or
+                                * issues with the contents */
+                               struct btrfs_root *root =
+                                       BTRFS_I(page->mapping->host)->root;
+                               struct btrfs_device *device;
+
                                uptodate = 0;
-                       else
+                               device = btrfs_find_device_for_logical(
+                                               root, start, mirror);
+                               if (device)
+                                       btrfs_dev_stat_inc_and_print(device,
+                                               BTRFS_DEV_STAT_CORRUPTION_ERRS);
+                       } else {
                                clean_io_failure(start, page);
+                       }
                }
 
                if (!uptodate && tree->ops && tree->ops->readpage_io_failed_hook) {
@@ -3164,7 +3158,7 @@ static int write_one_eb(struct extent_buffer *eb,
        u64 offset = eb->start;
        unsigned long i, num_pages;
        int rw = (epd->sync_io ? WRITE_SYNC : WRITE);
-       int ret;
+       int ret = 0;
 
        clear_bit(EXTENT_BUFFER_IOERR, &eb->bflags);
        num_pages = num_extent_pages(eb->start, eb->len);
@@ -3930,6 +3924,7 @@ static struct extent_buffer *__alloc_extent_buffer(struct extent_io_tree *tree,
        eb->start = start;
        eb->len = len;
        eb->tree = tree;
+       eb->bflags = 0;
        rwlock_init(&eb->lock);
        atomic_set(&eb->write_locks, 0);
        atomic_set(&eb->read_locks, 0);
@@ -3967,6 +3962,60 @@ static struct extent_buffer *__alloc_extent_buffer(struct extent_io_tree *tree,
        return eb;
 }
 
+struct extent_buffer *btrfs_clone_extent_buffer(struct extent_buffer *src)
+{
+       unsigned long i;
+       struct page *p;
+       struct extent_buffer *new;
+       unsigned long num_pages = num_extent_pages(src->start, src->len);
+
+       new = __alloc_extent_buffer(NULL, src->start, src->len, GFP_ATOMIC);
+       if (new == NULL)
+               return NULL;
+
+       for (i = 0; i < num_pages; i++) {
+               p = alloc_page(GFP_ATOMIC);
+               BUG_ON(!p);
+               attach_extent_buffer_page(new, p);
+               WARN_ON(PageDirty(p));
+               SetPageUptodate(p);
+               new->pages[i] = p;
+       }
+
+       copy_extent_buffer(new, src, 0, 0, src->len);
+       set_bit(EXTENT_BUFFER_UPTODATE, &new->bflags);
+       set_bit(EXTENT_BUFFER_DUMMY, &new->bflags);
+
+       return new;
+}
+
+struct extent_buffer *alloc_dummy_extent_buffer(u64 start, unsigned long len)
+{
+       struct extent_buffer *eb;
+       unsigned long num_pages = num_extent_pages(0, len);
+       unsigned long i;
+
+       eb = __alloc_extent_buffer(NULL, start, len, GFP_ATOMIC);
+       if (!eb)
+               return NULL;
+
+       for (i = 0; i < num_pages; i++) {
+               eb->pages[i] = alloc_page(GFP_ATOMIC);
+               if (!eb->pages[i])
+                       goto err;
+       }
+       set_extent_buffer_uptodate(eb);
+       btrfs_set_header_nritems(eb, 0);
+       set_bit(EXTENT_BUFFER_DUMMY, &eb->bflags);
+
+       return eb;
+err:
+       for (i--; i > 0; i--)
+               __free_page(eb->pages[i]);
+       __free_extent_buffer(eb);
+       return NULL;
+}
+
 static int extent_buffer_under_io(struct extent_buffer *eb)
 {
        return (atomic_read(&eb->io_pages) ||
@@ -3981,18 +4030,21 @@ static void btrfs_release_extent_buffer_page(struct extent_buffer *eb,
                                                unsigned long start_idx)
 {
        unsigned long index;
+       unsigned long num_pages;
        struct page *page;
+       int mapped = !test_bit(EXTENT_BUFFER_DUMMY, &eb->bflags);
 
        BUG_ON(extent_buffer_under_io(eb));
 
-       index = num_extent_pages(eb->start, eb->len);
+       num_pages = num_extent_pages(eb->start, eb->len);
+       index = start_idx + num_pages;
        if (start_idx >= index)
                return;
 
        do {
                index--;
                page = extent_buffer_page(eb, index);
-               if (page) {
+               if (page && mapped) {
                        spin_lock(&page->mapping->private_lock);
                        /*
                         * We do this since we'll remove the pages after we've
@@ -4017,6 +4069,8 @@ static void btrfs_release_extent_buffer_page(struct extent_buffer *eb,
                        }
                        spin_unlock(&page->mapping->private_lock);
 
+               }
+               if (page) {
                        /* One for when we alloced the page */
                        page_cache_release(page);
                }
@@ -4235,14 +4289,18 @@ static void release_extent_buffer(struct extent_buffer *eb, gfp_t mask)
 {
        WARN_ON(atomic_read(&eb->refs) == 0);
        if (atomic_dec_and_test(&eb->refs)) {
-               struct extent_io_tree *tree = eb->tree;
+               if (test_bit(EXTENT_BUFFER_DUMMY, &eb->bflags)) {
+                       spin_unlock(&eb->refs_lock);
+               } else {
+                       struct extent_io_tree *tree = eb->tree;
 
-               spin_unlock(&eb->refs_lock);
+                       spin_unlock(&eb->refs_lock);
 
-               spin_lock(&tree->buffer_lock);
-               radix_tree_delete(&tree->buffer,
-                                 eb->start >> PAGE_CACHE_SHIFT);
-               spin_unlock(&tree->buffer_lock);
+                       spin_lock(&tree->buffer_lock);
+                       radix_tree_delete(&tree->buffer,
+                                         eb->start >> PAGE_CACHE_SHIFT);
+                       spin_unlock(&tree->buffer_lock);
+               }
 
                /* Should be safe to release our pages at this point */
                btrfs_release_extent_buffer_page(eb, 0);
@@ -4259,6 +4317,10 @@ void free_extent_buffer(struct extent_buffer *eb)
                return;
 
        spin_lock(&eb->refs_lock);
+       if (atomic_read(&eb->refs) == 2 &&
+           test_bit(EXTENT_BUFFER_DUMMY, &eb->bflags))
+               atomic_dec(&eb->refs);
+
        if (atomic_read(&eb->refs) == 2 &&
            test_bit(EXTENT_BUFFER_STALE, &eb->bflags) &&
            !extent_buffer_under_io(eb) &&
index b516c3b8dec68d825e380a1930976f34c8a3e1a4..25900af5b15d43e6bdfe0cef7c865ac2aa81bd36 100644 (file)
@@ -39,6 +39,7 @@
 #define EXTENT_BUFFER_STALE 6
 #define EXTENT_BUFFER_WRITEBACK 7
 #define EXTENT_BUFFER_IOERR 8
+#define EXTENT_BUFFER_DUMMY 9
 
 /* these are flags for extent_clear_unlock_delalloc */
 #define EXTENT_CLEAR_UNLOCK_PAGE 0x1
@@ -75,9 +76,6 @@ struct extent_io_ops {
                              unsigned long bio_flags);
        int (*readpage_io_hook)(struct page *page, u64 start, u64 end);
        int (*readpage_io_failed_hook)(struct page *page, int failed_mirror);
-       int (*writepage_io_failed_hook)(struct bio *bio, struct page *page,
-                                       u64 start, u64 end,
-                                      struct extent_state *state);
        int (*readpage_end_io_hook)(struct page *page, u64 start, u64 end,
                                    struct extent_state *state, int mirror);
        int (*writepage_end_io_hook)(struct page *page, u64 start, u64 end,
@@ -225,6 +223,8 @@ int set_extent_bit(struct extent_io_tree *tree, u64 start, u64 end,
                   struct extent_state **cached_state, gfp_t mask);
 int set_extent_uptodate(struct extent_io_tree *tree, u64 start, u64 end,
                        struct extent_state **cached_state, gfp_t mask);
+int clear_extent_uptodate(struct extent_io_tree *tree, u64 start, u64 end,
+                         struct extent_state **cached_state, gfp_t mask);
 int set_extent_new(struct extent_io_tree *tree, u64 start, u64 end,
                   gfp_t mask);
 int set_extent_dirty(struct extent_io_tree *tree, u64 start, u64 end,
@@ -265,6 +265,8 @@ void set_page_extent_mapped(struct page *page);
 
 struct extent_buffer *alloc_extent_buffer(struct extent_io_tree *tree,
                                          u64 start, unsigned long len);
+struct extent_buffer *alloc_dummy_extent_buffer(u64 start, unsigned long len);
+struct extent_buffer *btrfs_clone_extent_buffer(struct extent_buffer *src);
 struct extent_buffer *find_extent_buffer(struct extent_io_tree *tree,
                                         u64 start, unsigned long len);
 void free_extent_buffer(struct extent_buffer *eb);
index 53bf2d764bbc4f5814db04710d3123d03c3779ba..70dc8ca73e257bc3a1e7a96ea48009bff093af9a 100644 (file)
@@ -65,6 +65,21 @@ struct inode_defrag {
        int cycled;
 };
 
+static int __compare_inode_defrag(struct inode_defrag *defrag1,
+                                 struct inode_defrag *defrag2)
+{
+       if (defrag1->root > defrag2->root)
+               return 1;
+       else if (defrag1->root < defrag2->root)
+               return -1;
+       else if (defrag1->ino > defrag2->ino)
+               return 1;
+       else if (defrag1->ino < defrag2->ino)
+               return -1;
+       else
+               return 0;
+}
+
 /* pop a record for an inode into the defrag tree.  The lock
  * must be held already
  *
@@ -81,15 +96,17 @@ static void __btrfs_add_inode_defrag(struct inode *inode,
        struct inode_defrag *entry;
        struct rb_node **p;
        struct rb_node *parent = NULL;
+       int ret;
 
        p = &root->fs_info->defrag_inodes.rb_node;
        while (*p) {
                parent = *p;
                entry = rb_entry(parent, struct inode_defrag, rb_node);
 
-               if (defrag->ino < entry->ino)
+               ret = __compare_inode_defrag(defrag, entry);
+               if (ret < 0)
                        p = &parent->rb_left;
-               else if (defrag->ino > entry->ino)
+               else if (ret > 0)
                        p = &parent->rb_right;
                else {
                        /* if we're reinserting an entry for
@@ -103,7 +120,7 @@ static void __btrfs_add_inode_defrag(struct inode *inode,
                        goto exists;
                }
        }
-       BTRFS_I(inode)->in_defrag = 1;
+       set_bit(BTRFS_INODE_IN_DEFRAG, &BTRFS_I(inode)->runtime_flags);
        rb_link_node(&defrag->rb_node, parent, p);
        rb_insert_color(&defrag->rb_node, &root->fs_info->defrag_inodes);
        return;
@@ -131,7 +148,7 @@ int btrfs_add_inode_defrag(struct btrfs_trans_handle *trans,
        if (btrfs_fs_closing(root->fs_info))
                return 0;
 
-       if (BTRFS_I(inode)->in_defrag)
+       if (test_bit(BTRFS_INODE_IN_DEFRAG, &BTRFS_I(inode)->runtime_flags))
                return 0;
 
        if (trans)
@@ -148,7 +165,7 @@ int btrfs_add_inode_defrag(struct btrfs_trans_handle *trans,
        defrag->root = root->root_key.objectid;
 
        spin_lock(&root->fs_info->defrag_inodes_lock);
-       if (!BTRFS_I(inode)->in_defrag)
+       if (!test_bit(BTRFS_INODE_IN_DEFRAG, &BTRFS_I(inode)->runtime_flags))
                __btrfs_add_inode_defrag(inode, defrag);
        else
                kfree(defrag);
@@ -159,28 +176,35 @@ int btrfs_add_inode_defrag(struct btrfs_trans_handle *trans,
 /*
  * must be called with the defrag_inodes lock held
  */
-struct inode_defrag *btrfs_find_defrag_inode(struct btrfs_fs_info *info, u64 ino,
+struct inode_defrag *btrfs_find_defrag_inode(struct btrfs_fs_info *info,
+                                            u64 root, u64 ino,
                                             struct rb_node **next)
 {
        struct inode_defrag *entry = NULL;
+       struct inode_defrag tmp;
        struct rb_node *p;
        struct rb_node *parent = NULL;
+       int ret;
+
+       tmp.ino = ino;
+       tmp.root = root;
 
        p = info->defrag_inodes.rb_node;
        while (p) {
                parent = p;
                entry = rb_entry(parent, struct inode_defrag, rb_node);
 
-               if (ino < entry->ino)
+               ret = __compare_inode_defrag(&tmp, entry);
+               if (ret < 0)
                        p = parent->rb_left;
-               else if (ino > entry->ino)
+               else if (ret > 0)
                        p = parent->rb_right;
                else
                        return entry;
        }
 
        if (next) {
-               while (parent && ino > entry->ino) {
+               while (parent && __compare_inode_defrag(&tmp, entry) > 0) {
                        parent = rb_next(parent);
                        entry = rb_entry(parent, struct inode_defrag, rb_node);
                }
@@ -202,6 +226,7 @@ int btrfs_run_defrag_inodes(struct btrfs_fs_info *fs_info)
        struct btrfs_key key;
        struct btrfs_ioctl_defrag_range_args range;
        u64 first_ino = 0;
+       u64 root_objectid = 0;
        int num_defrag;
        int defrag_batch = 1024;
 
@@ -214,11 +239,14 @@ int btrfs_run_defrag_inodes(struct btrfs_fs_info *fs_info)
                n = NULL;
 
                /* find an inode to defrag */
-               defrag = btrfs_find_defrag_inode(fs_info, first_ino, &n);
+               defrag = btrfs_find_defrag_inode(fs_info, root_objectid,
+                                                first_ino, &n);
                if (!defrag) {
-                       if (n)
-                               defrag = rb_entry(n, struct inode_defrag, rb_node);
-                       else if (first_ino) {
+                       if (n) {
+                               defrag = rb_entry(n, struct inode_defrag,
+                                                 rb_node);
+                       } else if (root_objectid || first_ino) {
+                               root_objectid = 0;
                                first_ino = 0;
                                continue;
                        } else {
@@ -228,6 +256,7 @@ int btrfs_run_defrag_inodes(struct btrfs_fs_info *fs_info)
 
                /* remove it from the rbtree */
                first_ino = defrag->ino + 1;
+               root_objectid = defrag->root;
                rb_erase(&defrag->rb_node, &fs_info->defrag_inodes);
 
                if (btrfs_fs_closing(fs_info))
@@ -252,7 +281,7 @@ int btrfs_run_defrag_inodes(struct btrfs_fs_info *fs_info)
                        goto next;
 
                /* do a chunk of defrag */
-               BTRFS_I(inode)->in_defrag = 0;
+               clear_bit(BTRFS_INODE_IN_DEFRAG, &BTRFS_I(inode)->runtime_flags);
                range.start = defrag->last_offset;
                num_defrag = btrfs_defrag_file(inode, NULL, &range, defrag->transid,
                                               defrag_batch);
@@ -1404,12 +1433,11 @@ static ssize_t btrfs_file_aio_write(struct kiocb *iocb,
                goto out;
        }
 
-       err = btrfs_update_time(file);
+       err = file_update_time(file);
        if (err) {
                mutex_unlock(&inode->i_mutex);
                goto out;
        }
-       BTRFS_I(inode)->sequence++;
 
        start_pos = round_down(pos, root->sectorsize);
        if (start_pos > i_size_read(inode)) {
@@ -1466,8 +1494,8 @@ int btrfs_release_file(struct inode *inode, struct file *filp)
         * flush down new bytes that may have been written if the
         * application were using truncate to replace a file in place.
         */
-       if (BTRFS_I(inode)->ordered_data_close) {
-               BTRFS_I(inode)->ordered_data_close = 0;
+       if (test_and_clear_bit(BTRFS_INODE_ORDERED_DATA_CLOSE,
+                              &BTRFS_I(inode)->runtime_flags)) {
                btrfs_add_ordered_operation(NULL, BTRFS_I(inode)->root, inode);
                if (inode->i_size > BTRFS_ORDERED_OPERATIONS_FLUSH_LIMIT)
                        filemap_flush(inode->i_mapping);
@@ -1498,14 +1526,15 @@ int btrfs_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
 
        trace_btrfs_sync_file(file, datasync);
 
-       ret = filemap_write_and_wait_range(inode->i_mapping, start, end);
-       if (ret)
-               return ret;
        mutex_lock(&inode->i_mutex);
 
-       /* we wait first, since the writeback may change the inode */
+       /*
+        * we wait first, since the writeback may change the inode, also wait
+        * ordered range does a filemape_write_and_wait_range which is why we
+        * don't do it above like other file systems.
+        */
        root->log_batch++;
-       btrfs_wait_ordered_range(inode, 0, (u64)-1);
+       btrfs_wait_ordered_range(inode, start, end);
        root->log_batch++;
 
        /*
@@ -1523,7 +1552,8 @@ int btrfs_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
         * syncing
         */
        smp_mb();
-       if (BTRFS_I(inode)->last_trans <=
+       if (btrfs_inode_in_log(inode, root->fs_info->generation) ||
+           BTRFS_I(inode)->last_trans <=
            root->fs_info->last_trans_committed) {
                BTRFS_I(inode)->last_trans = 0;
                mutex_unlock(&inode->i_mutex);
index 202008ec367d4c4c2cfcf73f7289692dd910b25c..81296c57405a5d53a27dba626a4d6201829bd578 100644 (file)
@@ -33,6 +33,8 @@
 
 static int link_free_space(struct btrfs_free_space_ctl *ctl,
                           struct btrfs_free_space *info);
+static void unlink_free_space(struct btrfs_free_space_ctl *ctl,
+                             struct btrfs_free_space *info);
 
 static struct inode *__lookup_free_space_inode(struct btrfs_root *root,
                                               struct btrfs_path *path,
@@ -75,7 +77,8 @@ static struct inode *__lookup_free_space_inode(struct btrfs_root *root,
                return ERR_PTR(-ENOENT);
        }
 
-       inode->i_mapping->flags &= ~__GFP_FS;
+       mapping_set_gfp_mask(inode->i_mapping,
+                       mapping_gfp_mask(inode->i_mapping) & ~__GFP_FS);
 
        return inode;
 }
@@ -365,7 +368,7 @@ static int io_ctl_prepare_pages(struct io_ctl *io_ctl, struct inode *inode,
 
 static void io_ctl_set_generation(struct io_ctl *io_ctl, u64 generation)
 {
-       u64 *val;
+       __le64 *val;
 
        io_ctl_map_page(io_ctl, 1);
 
@@ -388,7 +391,7 @@ static void io_ctl_set_generation(struct io_ctl *io_ctl, u64 generation)
 
 static int io_ctl_check_generation(struct io_ctl *io_ctl, u64 generation)
 {
-       u64 *gen;
+       __le64 *gen;
 
        /*
         * Skip the crc area.  If we don't check crcs then we just have a 64bit
@@ -584,6 +587,44 @@ static int io_ctl_read_bitmap(struct io_ctl *io_ctl,
        return 0;
 }
 
+/*
+ * Since we attach pinned extents after the fact we can have contiguous sections
+ * of free space that are split up in entries.  This poses a problem with the
+ * tree logging stuff since it could have allocated across what appears to be 2
+ * entries since we would have merged the entries when adding the pinned extents
+ * back to the free space cache.  So run through the space cache that we just
+ * loaded and merge contiguous entries.  This will make the log replay stuff not
+ * blow up and it will make for nicer allocator behavior.
+ */
+static void merge_space_tree(struct btrfs_free_space_ctl *ctl)
+{
+       struct btrfs_free_space *e, *prev = NULL;
+       struct rb_node *n;
+
+again:
+       spin_lock(&ctl->tree_lock);
+       for (n = rb_first(&ctl->free_space_offset); n; n = rb_next(n)) {
+               e = rb_entry(n, struct btrfs_free_space, offset_index);
+               if (!prev)
+                       goto next;
+               if (e->bitmap || prev->bitmap)
+                       goto next;
+               if (prev->offset + prev->bytes == e->offset) {
+                       unlink_free_space(ctl, prev);
+                       unlink_free_space(ctl, e);
+                       prev->bytes += e->bytes;
+                       kmem_cache_free(btrfs_free_space_cachep, e);
+                       link_free_space(ctl, prev);
+                       prev = NULL;
+                       spin_unlock(&ctl->tree_lock);
+                       goto again;
+               }
+next:
+               prev = e;
+       }
+       spin_unlock(&ctl->tree_lock);
+}
+
 int __load_free_space_cache(struct btrfs_root *root, struct inode *inode,
                            struct btrfs_free_space_ctl *ctl,
                            struct btrfs_path *path, u64 offset)
@@ -726,6 +767,7 @@ int __load_free_space_cache(struct btrfs_root *root, struct inode *inode,
        }
 
        io_ctl_drop_pages(&io_ctl);
+       merge_space_tree(ctl);
        ret = 1;
 out:
        io_ctl_free(&io_ctl);
@@ -972,9 +1014,7 @@ int __btrfs_write_out_cache(struct btrfs_root *root, struct inode *inode,
                goto out;
 
 
-       ret = filemap_write_and_wait(inode->i_mapping);
-       if (ret)
-               goto out;
+       btrfs_wait_ordered_range(inode, 0, (u64)-1);
 
        key.objectid = BTRFS_FREE_SPACE_OBJECTID;
        key.offset = offset;
index ceb7b9c9edcc1436693178fd6d2ff62f2334ada7..f6ab6f5e635a39b18ddb7f259bf5f0edd25d10a0 100644 (file)
@@ -89,7 +89,7 @@ static unsigned char btrfs_type_by_mode[S_IFMT >> S_SHIFT] = {
 
 static int btrfs_setsize(struct inode *inode, loff_t newsize);
 static int btrfs_truncate(struct inode *inode);
-static int btrfs_finish_ordered_io(struct inode *inode, u64 start, u64 end);
+static int btrfs_finish_ordered_io(struct btrfs_ordered_extent *ordered_extent);
 static noinline int cow_file_range(struct inode *inode,
                                   struct page *locked_page,
                                   u64 start, u64 end, int *page_started,
@@ -257,10 +257,13 @@ static noinline int cow_file_range_inline(struct btrfs_trans_handle *trans,
        ret = insert_inline_extent(trans, root, inode, start,
                                   inline_len, compressed_size,
                                   compress_type, compressed_pages);
-       if (ret) {
+       if (ret && ret != -ENOSPC) {
                btrfs_abort_transaction(trans, root, ret);
                return ret;
+       } else if (ret == -ENOSPC) {
+               return 1;
        }
+
        btrfs_delalloc_release_metadata(inode, end + 1 - start);
        btrfs_drop_extent_cache(inode, start, aligned_end - 1, 0);
        return 0;
@@ -1572,11 +1575,11 @@ static int btrfs_submit_bio_hook(struct inode *inode, int rw, struct bio *bio,
        if (btrfs_is_free_space_inode(root, inode))
                metadata = 2;
 
-       ret = btrfs_bio_wq_end_io(root->fs_info, bio, metadata);
-       if (ret)
-               return ret;
-
        if (!(rw & REQ_WRITE)) {
+               ret = btrfs_bio_wq_end_io(root->fs_info, bio, metadata);
+               if (ret)
+                       return ret;
+
                if (bio_flags & EXTENT_BIO_COMPRESSED) {
                        return btrfs_submit_compressed_read(inode, bio,
                                                    mirror_num, bio_flags);
@@ -1815,25 +1818,24 @@ out:
  * an ordered extent if the range of bytes in the file it covers are
  * fully written.
  */
-static int btrfs_finish_ordered_io(struct inode *inode, u64 start, u64 end)
+static int btrfs_finish_ordered_io(struct btrfs_ordered_extent *ordered_extent)
 {
+       struct inode *inode = ordered_extent->inode;
        struct btrfs_root *root = BTRFS_I(inode)->root;
        struct btrfs_trans_handle *trans = NULL;
-       struct btrfs_ordered_extent *ordered_extent = NULL;
        struct extent_io_tree *io_tree = &BTRFS_I(inode)->io_tree;
        struct extent_state *cached_state = NULL;
        int compress_type = 0;
        int ret;
        bool nolock;
 
-       ret = btrfs_dec_test_ordered_pending(inode, &ordered_extent, start,
-                                            end - start + 1);
-       if (!ret)
-               return 0;
-       BUG_ON(!ordered_extent); /* Logic error */
-
        nolock = btrfs_is_free_space_inode(root, inode);
 
+       if (test_bit(BTRFS_ORDERED_IOERR, &ordered_extent->flags)) {
+               ret = -EIO;
+               goto out;
+       }
+
        if (test_bit(BTRFS_ORDERED_NOCOW, &ordered_extent->flags)) {
                BUG_ON(!list_empty(&ordered_extent->list)); /* Logic error */
                ret = btrfs_ordered_update_i_size(inode, 0, ordered_extent);
@@ -1889,12 +1891,10 @@ static int btrfs_finish_ordered_io(struct inode *inode, u64 start, u64 end)
                                   ordered_extent->file_offset,
                                   ordered_extent->len);
        }
-       unlock_extent_cached(io_tree, ordered_extent->file_offset,
-                            ordered_extent->file_offset +
-                            ordered_extent->len - 1, &cached_state, GFP_NOFS);
+
        if (ret < 0) {
                btrfs_abort_transaction(trans, root, ret);
-               goto out;
+               goto out_unlock;
        }
 
        add_pending_csums(trans, inode, ordered_extent->file_offset,
@@ -1905,10 +1905,14 @@ static int btrfs_finish_ordered_io(struct inode *inode, u64 start, u64 end)
                ret = btrfs_update_inode_fallback(trans, root, inode);
                if (ret) { /* -ENOMEM or corruption */
                        btrfs_abort_transaction(trans, root, ret);
-                       goto out;
+                       goto out_unlock;
                }
        }
        ret = 0;
+out_unlock:
+       unlock_extent_cached(io_tree, ordered_extent->file_offset,
+                            ordered_extent->file_offset +
+                            ordered_extent->len - 1, &cached_state, GFP_NOFS);
 out:
        if (root != root->fs_info->tree_root)
                btrfs_delalloc_release_metadata(inode, ordered_extent->len);
@@ -1919,26 +1923,57 @@ out:
                        btrfs_end_transaction(trans, root);
        }
 
+       if (ret)
+               clear_extent_uptodate(io_tree, ordered_extent->file_offset,
+                                     ordered_extent->file_offset +
+                                     ordered_extent->len - 1, NULL, GFP_NOFS);
+
+       /*
+        * This needs to be dont to make sure anybody waiting knows we are done
+        * upating everything for this ordered extent.
+        */
+       btrfs_remove_ordered_extent(inode, ordered_extent);
+
        /* once for us */
        btrfs_put_ordered_extent(ordered_extent);
        /* once for the tree */
        btrfs_put_ordered_extent(ordered_extent);
 
-       return 0;
-out_unlock:
-       unlock_extent_cached(io_tree, ordered_extent->file_offset,
-                            ordered_extent->file_offset +
-                            ordered_extent->len - 1, &cached_state, GFP_NOFS);
-       goto out;
+       return ret;
+}
+
+static void finish_ordered_fn(struct btrfs_work *work)
+{
+       struct btrfs_ordered_extent *ordered_extent;
+       ordered_extent = container_of(work, struct btrfs_ordered_extent, work);
+       btrfs_finish_ordered_io(ordered_extent);
 }
 
 static int btrfs_writepage_end_io_hook(struct page *page, u64 start, u64 end,
                                struct extent_state *state, int uptodate)
 {
+       struct inode *inode = page->mapping->host;
+       struct btrfs_root *root = BTRFS_I(inode)->root;
+       struct btrfs_ordered_extent *ordered_extent = NULL;
+       struct btrfs_workers *workers;
+
        trace_btrfs_writepage_end_io_hook(page, start, end, uptodate);
 
        ClearPagePrivate2(page);
-       return btrfs_finish_ordered_io(page->mapping->host, start, end);
+       if (!btrfs_dec_test_ordered_pending(inode, &ordered_extent, start,
+                                           end - start + 1, uptodate))
+               return 0;
+
+       ordered_extent->work.func = finish_ordered_fn;
+       ordered_extent->work.flags = 0;
+
+       if (btrfs_is_free_space_inode(root, inode))
+               workers = &root->fs_info->endio_freespace_worker;
+       else
+               workers = &root->fs_info->endio_write_workers;
+       btrfs_queue_worker(workers, &ordered_extent->work);
+
+       return 0;
 }
 
 /*
@@ -2072,12 +2107,12 @@ void btrfs_orphan_commit_root(struct btrfs_trans_handle *trans,
        struct btrfs_block_rsv *block_rsv;
        int ret;
 
-       if (!list_empty(&root->orphan_list) ||
+       if (atomic_read(&root->orphan_inodes) ||
            root->orphan_cleanup_state != ORPHAN_CLEANUP_DONE)
                return;
 
        spin_lock(&root->orphan_lock);
-       if (!list_empty(&root->orphan_list)) {
+       if (atomic_read(&root->orphan_inodes)) {
                spin_unlock(&root->orphan_lock);
                return;
        }
@@ -2134,8 +2169,8 @@ int btrfs_orphan_add(struct btrfs_trans_handle *trans, struct inode *inode)
                block_rsv = NULL;
        }
 
-       if (list_empty(&BTRFS_I(inode)->i_orphan)) {
-               list_add(&BTRFS_I(inode)->i_orphan, &root->orphan_list);
+       if (!test_and_set_bit(BTRFS_INODE_HAS_ORPHAN_ITEM,
+                             &BTRFS_I(inode)->runtime_flags)) {
 #if 0
                /*
                 * For proper ENOSPC handling, we should do orphan
@@ -2148,12 +2183,12 @@ int btrfs_orphan_add(struct btrfs_trans_handle *trans, struct inode *inode)
                        insert = 1;
 #endif
                insert = 1;
+               atomic_dec(&root->orphan_inodes);
        }
 
-       if (!BTRFS_I(inode)->orphan_meta_reserved) {
-               BTRFS_I(inode)->orphan_meta_reserved = 1;
+       if (!test_and_set_bit(BTRFS_INODE_ORPHAN_META_RESERVED,
+                             &BTRFS_I(inode)->runtime_flags))
                reserve = 1;
-       }
        spin_unlock(&root->orphan_lock);
 
        /* grab metadata reservation from transaction handle */
@@ -2166,6 +2201,8 @@ int btrfs_orphan_add(struct btrfs_trans_handle *trans, struct inode *inode)
        if (insert >= 1) {
                ret = btrfs_insert_orphan_item(trans, root, btrfs_ino(inode));
                if (ret && ret != -EEXIST) {
+                       clear_bit(BTRFS_INODE_HAS_ORPHAN_ITEM,
+                                 &BTRFS_I(inode)->runtime_flags);
                        btrfs_abort_transaction(trans, root, ret);
                        return ret;
                }
@@ -2196,15 +2233,13 @@ int btrfs_orphan_del(struct btrfs_trans_handle *trans, struct inode *inode)
        int ret = 0;
 
        spin_lock(&root->orphan_lock);
-       if (!list_empty(&BTRFS_I(inode)->i_orphan)) {
-               list_del_init(&BTRFS_I(inode)->i_orphan);
+       if (test_and_clear_bit(BTRFS_INODE_HAS_ORPHAN_ITEM,
+                              &BTRFS_I(inode)->runtime_flags))
                delete_item = 1;
-       }
 
-       if (BTRFS_I(inode)->orphan_meta_reserved) {
-               BTRFS_I(inode)->orphan_meta_reserved = 0;
+       if (test_and_clear_bit(BTRFS_INODE_ORPHAN_META_RESERVED,
+                              &BTRFS_I(inode)->runtime_flags))
                release_rsv = 1;
-       }
        spin_unlock(&root->orphan_lock);
 
        if (trans && delete_item) {
@@ -2212,8 +2247,10 @@ int btrfs_orphan_del(struct btrfs_trans_handle *trans, struct inode *inode)
                BUG_ON(ret); /* -ENOMEM or corruption (JDM: Recheck) */
        }
 
-       if (release_rsv)
+       if (release_rsv) {
                btrfs_orphan_release_metadata(inode);
+               atomic_dec(&root->orphan_inodes);
+       }
 
        return 0;
 }
@@ -2341,6 +2378,8 @@ int btrfs_orphan_cleanup(struct btrfs_root *root)
                                ret = PTR_ERR(trans);
                                goto out;
                        }
+                       printk(KERN_ERR "auto deleting %Lu\n",
+                              found_key.objectid);
                        ret = btrfs_del_orphan_item(trans, root,
                                                    found_key.objectid);
                        BUG_ON(ret); /* -ENOMEM or corruption (JDM: Recheck) */
@@ -2352,9 +2391,8 @@ int btrfs_orphan_cleanup(struct btrfs_root *root)
                 * add this inode to the orphan list so btrfs_orphan_del does
                 * the proper thing when we hit it
                 */
-               spin_lock(&root->orphan_lock);
-               list_add(&BTRFS_I(inode)->i_orphan, &root->orphan_list);
-               spin_unlock(&root->orphan_lock);
+               set_bit(BTRFS_INODE_HAS_ORPHAN_ITEM,
+                       &BTRFS_I(inode)->runtime_flags);
 
                /* if we have links, this was a truncate, lets do that */
                if (inode->i_nlink) {
@@ -2510,7 +2548,7 @@ static void btrfs_read_locked_inode(struct inode *inode)
 
        inode_set_bytes(inode, btrfs_inode_nbytes(leaf, inode_item));
        BTRFS_I(inode)->generation = btrfs_inode_generation(leaf, inode_item);
-       BTRFS_I(inode)->sequence = btrfs_inode_sequence(leaf, inode_item);
+       inode->i_version = btrfs_inode_sequence(leaf, inode_item);
        inode->i_generation = BTRFS_I(inode)->generation;
        inode->i_rdev = 0;
        rdev = btrfs_inode_rdev(leaf, inode_item);
@@ -2594,7 +2632,7 @@ static void fill_inode_item(struct btrfs_trans_handle *trans,
 
        btrfs_set_inode_nbytes(leaf, item, inode_get_bytes(inode));
        btrfs_set_inode_generation(leaf, item, BTRFS_I(inode)->generation);
-       btrfs_set_inode_sequence(leaf, item, BTRFS_I(inode)->sequence);
+       btrfs_set_inode_sequence(leaf, item, inode->i_version);
        btrfs_set_inode_transid(leaf, item, trans->transid);
        btrfs_set_inode_rdev(leaf, item, inode->i_rdev);
        btrfs_set_inode_flags(leaf, item, BTRFS_I(inode)->flags);
@@ -2752,6 +2790,8 @@ err:
                goto out;
 
        btrfs_i_size_write(dir, dir->i_size - name_len * 2);
+       inode_inc_iversion(inode);
+       inode_inc_iversion(dir);
        inode->i_ctime = dir->i_mtime = dir->i_ctime = CURRENT_TIME;
        btrfs_update_inode(trans, root, dir);
 out:
@@ -3089,6 +3129,7 @@ int btrfs_unlink_subvol(struct btrfs_trans_handle *trans,
        }
 
        btrfs_i_size_write(dir, dir->i_size - name_len * 2);
+       inode_inc_iversion(dir);
        dir->i_mtime = dir->i_ctime = CURRENT_TIME;
        ret = btrfs_update_inode(trans, root, dir);
        if (ret)
@@ -3607,7 +3648,8 @@ static int btrfs_setsize(struct inode *inode, loff_t newsize)
                 * any new writes get down to disk quickly.
                 */
                if (newsize == 0)
-                       BTRFS_I(inode)->ordered_data_close = 1;
+                       set_bit(BTRFS_INODE_ORDERED_DATA_CLOSE,
+                               &BTRFS_I(inode)->runtime_flags);
 
                /* we don't support swapfiles, so vmtruncate shouldn't fail */
                truncate_setsize(inode, newsize);
@@ -3638,6 +3680,7 @@ static int btrfs_setattr(struct dentry *dentry, struct iattr *attr)
 
        if (attr->ia_valid) {
                setattr_copy(inode, attr);
+               inode_inc_iversion(inode);
                err = btrfs_dirty_inode(inode);
 
                if (!err && attr->ia_valid & ATTR_MODE)
@@ -3671,7 +3714,8 @@ void btrfs_evict_inode(struct inode *inode)
        btrfs_wait_ordered_range(inode, 0, (u64)-1);
 
        if (root->fs_info->log_root_recovering) {
-               BUG_ON(!list_empty(&BTRFS_I(inode)->i_orphan));
+               BUG_ON(!test_bit(BTRFS_INODE_HAS_ORPHAN_ITEM,
+                                &BTRFS_I(inode)->runtime_flags));
                goto no_delete;
        }
 
@@ -4066,7 +4110,7 @@ static struct inode *new_simple_dir(struct super_block *s,
 
        BTRFS_I(inode)->root = root;
        memcpy(&BTRFS_I(inode)->location, key, sizeof(*key));
-       BTRFS_I(inode)->dummy_inode = 1;
+       set_bit(BTRFS_INODE_DUMMY, &BTRFS_I(inode)->runtime_flags);
 
        inode->i_ino = BTRFS_EMPTY_SUBVOL_DIR_OBJECTID;
        inode->i_op = &btrfs_dir_ro_inode_operations;
@@ -4370,7 +4414,7 @@ int btrfs_write_inode(struct inode *inode, struct writeback_control *wbc)
        int ret = 0;
        bool nolock = false;
 
-       if (BTRFS_I(inode)->dummy_inode)
+       if (test_bit(BTRFS_INODE_DUMMY, &BTRFS_I(inode)->runtime_flags))
                return 0;
 
        if (btrfs_fs_closing(root->fs_info) && btrfs_is_free_space_inode(root, inode))
@@ -4403,7 +4447,7 @@ int btrfs_dirty_inode(struct inode *inode)
        struct btrfs_trans_handle *trans;
        int ret;
 
-       if (BTRFS_I(inode)->dummy_inode)
+       if (test_bit(BTRFS_INODE_DUMMY, &BTRFS_I(inode)->runtime_flags))
                return 0;
 
        trans = btrfs_join_transaction(root);
@@ -4431,46 +4475,18 @@ int btrfs_dirty_inode(struct inode *inode)
  * This is a copy of file_update_time.  We need this so we can return error on
  * ENOSPC for updating the inode in the case of file write and mmap writes.
  */
-int btrfs_update_time(struct file *file)
+static int btrfs_update_time(struct inode *inode, struct timespec *now,
+                            int flags)
 {
-       struct inode *inode = file->f_path.dentry->d_inode;
-       struct timespec now;
-       int ret;
-       enum { S_MTIME = 1, S_CTIME = 2, S_VERSION = 4 } sync_it = 0;
-
-       /* First try to exhaust all avenues to not sync */
-       if (IS_NOCMTIME(inode))
-               return 0;
-
-       now = current_fs_time(inode->i_sb);
-       if (!timespec_equal(&inode->i_mtime, &now))
-               sync_it = S_MTIME;
-
-       if (!timespec_equal(&inode->i_ctime, &now))
-               sync_it |= S_CTIME;
-
-       if (IS_I_VERSION(inode))
-               sync_it |= S_VERSION;
-
-       if (!sync_it)
-               return 0;
-
-       /* Finally allowed to write? Takes lock. */
-       if (mnt_want_write_file(file))
-               return 0;
-
-       /* Only change inode inside the lock region */
-       if (sync_it & S_VERSION)
+       if (flags & S_VERSION)
                inode_inc_iversion(inode);
-       if (sync_it & S_CTIME)
-               inode->i_ctime = now;
-       if (sync_it & S_MTIME)
-               inode->i_mtime = now;
-       ret = btrfs_dirty_inode(inode);
-       if (!ret)
-               mark_inode_dirty_sync(inode);
-       mnt_drop_write(file->f_path.mnt);
-       return ret;
+       if (flags & S_CTIME)
+               inode->i_ctime = *now;
+       if (flags & S_MTIME)
+               inode->i_mtime = *now;
+       if (flags & S_ATIME)
+               inode->i_atime = *now;
+       return btrfs_dirty_inode(inode);
 }
 
 /*
@@ -4730,6 +4746,7 @@ int btrfs_add_link(struct btrfs_trans_handle *trans,
 
        btrfs_i_size_write(parent_inode, parent_inode->i_size +
                           name_len * 2);
+       inode_inc_iversion(parent_inode);
        parent_inode->i_mtime = parent_inode->i_ctime = CURRENT_TIME;
        ret = btrfs_update_inode(trans, root, parent_inode);
        if (ret)
@@ -4937,6 +4954,7 @@ static int btrfs_link(struct dentry *old_dentry, struct inode *dir,
        }
 
        btrfs_inc_nlink(inode);
+       inode_inc_iversion(inode);
        inode->i_ctime = CURRENT_TIME;
        ihold(inode);
 
@@ -5903,9 +5921,7 @@ static void btrfs_endio_direct_write(struct bio *bio, int err)
        struct btrfs_dio_private *dip = bio->bi_private;
        struct inode *inode = dip->inode;
        struct btrfs_root *root = BTRFS_I(inode)->root;
-       struct btrfs_trans_handle *trans;
        struct btrfs_ordered_extent *ordered = NULL;
-       struct extent_state *cached_state = NULL;
        u64 ordered_offset = dip->logical_offset;
        u64 ordered_bytes = dip->bytes;
        int ret;
@@ -5915,73 +5931,14 @@ static void btrfs_endio_direct_write(struct bio *bio, int err)
 again:
        ret = btrfs_dec_test_first_ordered_pending(inode, &ordered,
                                                   &ordered_offset,
-                                                  ordered_bytes);
+                                                  ordered_bytes, !err);
        if (!ret)
                goto out_test;
 
-       BUG_ON(!ordered);
-
-       trans = btrfs_join_transaction(root);
-       if (IS_ERR(trans)) {
-               err = -ENOMEM;
-               goto out;
-       }
-       trans->block_rsv = &root->fs_info->delalloc_block_rsv;
-
-       if (test_bit(BTRFS_ORDERED_NOCOW, &ordered->flags)) {
-               ret = btrfs_ordered_update_i_size(inode, 0, ordered);
-               if (!ret)
-                       err = btrfs_update_inode_fallback(trans, root, inode);
-               goto out;
-       }
-
-       lock_extent_bits(&BTRFS_I(inode)->io_tree, ordered->file_offset,
-                        ordered->file_offset + ordered->len - 1, 0,
-                        &cached_state);
-
-       if (test_bit(BTRFS_ORDERED_PREALLOC, &ordered->flags)) {
-               ret = btrfs_mark_extent_written(trans, inode,
-                                               ordered->file_offset,
-                                               ordered->file_offset +
-                                               ordered->len);
-               if (ret) {
-                       err = ret;
-                       goto out_unlock;
-               }
-       } else {
-               ret = insert_reserved_file_extent(trans, inode,
-                                                 ordered->file_offset,
-                                                 ordered->start,
-                                                 ordered->disk_len,
-                                                 ordered->len,
-                                                 ordered->len,
-                                                 0, 0, 0,
-                                                 BTRFS_FILE_EXTENT_REG);
-               unpin_extent_cache(&BTRFS_I(inode)->extent_tree,
-                                  ordered->file_offset, ordered->len);
-               if (ret) {
-                       err = ret;
-                       WARN_ON(1);
-                       goto out_unlock;
-               }
-       }
-
-       add_pending_csums(trans, inode, ordered->file_offset, &ordered->list);
-       ret = btrfs_ordered_update_i_size(inode, 0, ordered);
-       if (!ret || !test_bit(BTRFS_ORDERED_PREALLOC, &ordered->flags))
-               btrfs_update_inode_fallback(trans, root, inode);
-       ret = 0;
-out_unlock:
-       unlock_extent_cached(&BTRFS_I(inode)->io_tree, ordered->file_offset,
-                            ordered->file_offset + ordered->len - 1,
-                            &cached_state, GFP_NOFS);
-out:
-       btrfs_delalloc_release_metadata(inode, ordered->len);
-       btrfs_end_transaction(trans, root);
-       ordered_offset = ordered->file_offset + ordered->len;
-       btrfs_put_ordered_extent(ordered);
-       btrfs_put_ordered_extent(ordered);
-
+       ordered->work.func = finish_ordered_fn;
+       ordered->work.flags = 0;
+       btrfs_queue_worker(&root->fs_info->endio_write_workers,
+                          &ordered->work);
 out_test:
        /*
         * our bio might span multiple ordered extents.  If we haven't
@@ -5990,12 +5947,12 @@ out_test:
        if (ordered_offset < dip->logical_offset + dip->bytes) {
                ordered_bytes = dip->logical_offset + dip->bytes -
                        ordered_offset;
+               ordered = NULL;
                goto again;
        }
 out_done:
        bio->bi_private = dip->private;
 
-       kfree(dip->csums);
        kfree(dip);
 
        /* If we had an error make sure to clear the uptodate flag */
@@ -6063,9 +6020,12 @@ static inline int __btrfs_submit_dio_bio(struct bio *bio, struct inode *inode,
        int ret;
 
        bio_get(bio);
-       ret = btrfs_bio_wq_end_io(root->fs_info, bio, 0);
-       if (ret)
-               goto err;
+
+       if (!write) {
+               ret = btrfs_bio_wq_end_io(root->fs_info, bio, 0);
+               if (ret)
+                       goto err;
+       }
 
        if (skip_sum)
                goto map;
@@ -6485,13 +6445,13 @@ static int btrfs_releasepage(struct page *page, gfp_t gfp_flags)
 
 static void btrfs_invalidatepage(struct page *page, unsigned long offset)
 {
+       struct inode *inode = page->mapping->host;
        struct extent_io_tree *tree;
        struct btrfs_ordered_extent *ordered;
        struct extent_state *cached_state = NULL;
        u64 page_start = page_offset(page);
        u64 page_end = page_start + PAGE_CACHE_SIZE - 1;
 
-
        /*
         * we have the page locked, so new writeback can't start,
         * and the dirty bit won't be cleared while we are here.
@@ -6501,13 +6461,13 @@ static void btrfs_invalidatepage(struct page *page, unsigned long offset)
         */
        wait_on_page_writeback(page);
 
-       tree = &BTRFS_I(page->mapping->host)->io_tree;
+       tree = &BTRFS_I(inode)->io_tree;
        if (offset) {
                btrfs_releasepage(page, GFP_NOFS);
                return;
        }
        lock_extent_bits(tree, page_start, page_end, 0, &cached_state);
-       ordered = btrfs_lookup_ordered_extent(page->mapping->host,
+       ordered = btrfs_lookup_ordered_extent(inode,
                                           page_offset(page));
        if (ordered) {
                /*
@@ -6522,9 +6482,10 @@ static void btrfs_invalidatepage(struct page *page, unsigned long offset)
                 * whoever cleared the private bit is responsible
                 * for the finish_ordered_io
                 */
-               if (TestClearPagePrivate2(page)) {
-                       btrfs_finish_ordered_io(page->mapping->host,
-                                               page_start, page_end);
+               if (TestClearPagePrivate2(page) &&
+                   btrfs_dec_test_ordered_pending(inode, &ordered, page_start,
+                                                  PAGE_CACHE_SIZE, 1)) {
+                       btrfs_finish_ordered_io(ordered);
                }
                btrfs_put_ordered_extent(ordered);
                cached_state = NULL;
@@ -6576,7 +6537,7 @@ int btrfs_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf)
 
        ret  = btrfs_delalloc_reserve_space(inode, PAGE_CACHE_SIZE);
        if (!ret) {
-               ret = btrfs_update_time(vma->vm_file);
+               ret = file_update_time(vma->vm_file);
                reserved = 1;
        }
        if (ret) {
@@ -6771,7 +6732,8 @@ static int btrfs_truncate(struct inode *inode)
         * using truncate to replace the contents of the file will
         * end up with a zero length file after a crash.
         */
-       if (inode->i_size == 0 && BTRFS_I(inode)->ordered_data_close)
+       if (inode->i_size == 0 && test_bit(BTRFS_INODE_ORDERED_DATA_CLOSE,
+                                          &BTRFS_I(inode)->runtime_flags))
                btrfs_add_ordered_operation(trans, root, inode);
 
        while (1) {
@@ -6894,7 +6856,6 @@ struct inode *btrfs_alloc_inode(struct super_block *sb)
        ei->root = NULL;
        ei->space_info = NULL;
        ei->generation = 0;
-       ei->sequence = 0;
        ei->last_trans = 0;
        ei->last_sub_trans = 0;
        ei->logged_trans = 0;
@@ -6909,11 +6870,7 @@ struct inode *btrfs_alloc_inode(struct super_block *sb)
        ei->outstanding_extents = 0;
        ei->reserved_extents = 0;
 
-       ei->ordered_data_close = 0;
-       ei->orphan_meta_reserved = 0;
-       ei->dummy_inode = 0;
-       ei->in_defrag = 0;
-       ei->delalloc_meta_reserved = 0;
+       ei->runtime_flags = 0;
        ei->force_compress = BTRFS_COMPRESS_NONE;
 
        ei->delayed_node = NULL;
@@ -6927,7 +6884,6 @@ struct inode *btrfs_alloc_inode(struct super_block *sb)
        mutex_init(&ei->log_mutex);
        mutex_init(&ei->delalloc_mutex);
        btrfs_ordered_inode_tree_init(&ei->ordered_tree);
-       INIT_LIST_HEAD(&ei->i_orphan);
        INIT_LIST_HEAD(&ei->delalloc_inodes);
        INIT_LIST_HEAD(&ei->ordered_operations);
        RB_CLEAR_NODE(&ei->rb_node);
@@ -6972,13 +6928,12 @@ void btrfs_destroy_inode(struct inode *inode)
                spin_unlock(&root->fs_info->ordered_extent_lock);
        }
 
-       spin_lock(&root->orphan_lock);
-       if (!list_empty(&BTRFS_I(inode)->i_orphan)) {
+       if (test_bit(BTRFS_INODE_HAS_ORPHAN_ITEM,
+                    &BTRFS_I(inode)->runtime_flags)) {
                printk(KERN_INFO "BTRFS: inode %llu still on the orphan list\n",
                       (unsigned long long)btrfs_ino(inode));
-               list_del_init(&BTRFS_I(inode)->i_orphan);
+               atomic_dec(&root->orphan_inodes);
        }
-       spin_unlock(&root->orphan_lock);
 
        while (1) {
                ordered = btrfs_lookup_first_ordered_extent(inode, (u64)-1);
@@ -7193,6 +7148,9 @@ static int btrfs_rename(struct inode *old_dir, struct dentry *old_dentry,
        if (new_inode && new_inode->i_size && S_ISREG(old_inode->i_mode))
                btrfs_add_ordered_operation(trans, root, old_inode);
 
+       inode_inc_iversion(old_dir);
+       inode_inc_iversion(new_dir);
+       inode_inc_iversion(old_inode);
        old_dir->i_ctime = old_dir->i_mtime = ctime;
        new_dir->i_ctime = new_dir->i_mtime = ctime;
        old_inode->i_ctime = ctime;
@@ -7219,6 +7177,7 @@ static int btrfs_rename(struct inode *old_dir, struct dentry *old_dentry,
        }
 
        if (new_inode) {
+               inode_inc_iversion(new_inode);
                new_inode->i_ctime = CURRENT_TIME;
                if (unlikely(btrfs_ino(new_inode) ==
                             BTRFS_EMPTY_SUBVOL_DIR_OBJECTID)) {
@@ -7490,6 +7449,7 @@ static int __btrfs_prealloc_file_range(struct inode *inode, int mode,
                cur_offset += ins.offset;
                *alloc_hint = ins.objectid + ins.offset;
 
+               inode_inc_iversion(inode);
                inode->i_ctime = CURRENT_TIME;
                BTRFS_I(inode)->flags |= BTRFS_INODE_PREALLOC;
                if (!(mode & FALLOC_FL_KEEP_SIZE) &&
@@ -7647,6 +7607,7 @@ static const struct inode_operations btrfs_file_inode_operations = {
        .permission     = btrfs_permission,
        .fiemap         = btrfs_fiemap,
        .get_acl        = btrfs_get_acl,
+       .update_time    = btrfs_update_time,
 };
 static const struct inode_operations btrfs_special_inode_operations = {
        .getattr        = btrfs_getattr,
@@ -7657,6 +7618,7 @@ static const struct inode_operations btrfs_special_inode_operations = {
        .listxattr      = btrfs_listxattr,
        .removexattr    = btrfs_removexattr,
        .get_acl        = btrfs_get_acl,
+       .update_time    = btrfs_update_time,
 };
 static const struct inode_operations btrfs_symlink_inode_operations = {
        .readlink       = generic_readlink,
@@ -7670,6 +7632,7 @@ static const struct inode_operations btrfs_symlink_inode_operations = {
        .listxattr      = btrfs_listxattr,
        .removexattr    = btrfs_removexattr,
        .get_acl        = btrfs_get_acl,
+       .update_time    = btrfs_update_time,
 };
 
 const struct dentry_operations btrfs_dentry_operations = {
index 14f8e1faa46ee0478ebb83d6f82d205d25c1dc51..24b776c08d99f7bbb621076f68500464b6829435 100644 (file)
@@ -261,6 +261,7 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
        }
 
        btrfs_update_iflags(inode);
+       inode_inc_iversion(inode);
        inode->i_ctime = CURRENT_TIME;
        ret = btrfs_update_inode(trans, root, inode);
 
@@ -367,7 +368,7 @@ static noinline int create_subvol(struct btrfs_root *root,
                return PTR_ERR(trans);
 
        leaf = btrfs_alloc_free_block(trans, root, root->leafsize,
-                                     0, objectid, NULL, 0, 0, 0, 0);
+                                     0, objectid, NULL, 0, 0, 0);
        if (IS_ERR(leaf)) {
                ret = PTR_ERR(leaf);
                goto fail;
@@ -2262,10 +2263,12 @@ static long btrfs_ioctl_dev_info(struct btrfs_root *root, void __user *arg)
        di_args->bytes_used = dev->bytes_used;
        di_args->total_bytes = dev->total_bytes;
        memcpy(di_args->uuid, dev->uuid, sizeof(di_args->uuid));
-       if (dev->name)
+       if (dev->name) {
                strncpy(di_args->path, dev->name, sizeof(di_args->path));
-       else
+               di_args->path[sizeof(di_args->path) - 1] = 0;
+       } else {
                di_args->path[0] = '\0';
+       }
 
 out:
        if (ret == 0 && copy_to_user(arg, di_args, sizeof(*di_args)))
@@ -2622,6 +2625,7 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
                        btrfs_mark_buffer_dirty(leaf);
                        btrfs_release_path(path);
 
+                       inode_inc_iversion(inode);
                        inode->i_mtime = inode->i_ctime = CURRENT_TIME;
 
                        /*
@@ -2914,7 +2918,7 @@ long btrfs_ioctl_space_info(struct btrfs_root *root, void __user *arg)
                up_read(&info->groups_sem);
        }
 
-       user_dest = (struct btrfs_ioctl_space_info *)
+       user_dest = (struct btrfs_ioctl_space_info __user *)
                (arg + sizeof(struct btrfs_ioctl_space_args));
 
        if (copy_to_user(user_dest, dest_orig, alloc_size))
@@ -3042,6 +3046,28 @@ static long btrfs_ioctl_scrub_progress(struct btrfs_root *root,
        return ret;
 }
 
+static long btrfs_ioctl_get_dev_stats(struct btrfs_root *root,
+                                     void __user *arg, int reset_after_read)
+{
+       struct btrfs_ioctl_get_dev_stats *sa;
+       int ret;
+
+       if (reset_after_read && !capable(CAP_SYS_ADMIN))
+               return -EPERM;
+
+       sa = memdup_user(arg, sizeof(*sa));
+       if (IS_ERR(sa))
+               return PTR_ERR(sa);
+
+       ret = btrfs_get_dev_stats(root, sa, reset_after_read);
+
+       if (copy_to_user(arg, sa, sizeof(*sa)))
+               ret = -EFAULT;
+
+       kfree(sa);
+       return ret;
+}
+
 static long btrfs_ioctl_ino_to_path(struct btrfs_root *root, void __user *arg)
 {
        int ret = 0;
@@ -3212,8 +3238,9 @@ void update_ioctl_balance_args(struct btrfs_fs_info *fs_info, int lock,
        }
 }
 
-static long btrfs_ioctl_balance(struct btrfs_root *root, void __user *arg)
+static long btrfs_ioctl_balance(struct file *file, void __user *arg)
 {
+       struct btrfs_root *root = BTRFS_I(fdentry(file)->d_inode)->root;
        struct btrfs_fs_info *fs_info = root->fs_info;
        struct btrfs_ioctl_balance_args *bargs;
        struct btrfs_balance_control *bctl;
@@ -3225,6 +3252,10 @@ static long btrfs_ioctl_balance(struct btrfs_root *root, void __user *arg)
        if (fs_info->sb->s_flags & MS_RDONLY)
                return -EROFS;
 
+       ret = mnt_want_write(file->f_path.mnt);
+       if (ret)
+               return ret;
+
        mutex_lock(&fs_info->volume_mutex);
        mutex_lock(&fs_info->balance_mutex);
 
@@ -3291,6 +3322,7 @@ out_bargs:
 out:
        mutex_unlock(&fs_info->balance_mutex);
        mutex_unlock(&fs_info->volume_mutex);
+       mnt_drop_write(file->f_path.mnt);
        return ret;
 }
 
@@ -3386,7 +3418,7 @@ long btrfs_ioctl(struct file *file, unsigned int
        case BTRFS_IOC_DEV_INFO:
                return btrfs_ioctl_dev_info(root, argp);
        case BTRFS_IOC_BALANCE:
-               return btrfs_ioctl_balance(root, NULL);
+               return btrfs_ioctl_balance(file, NULL);
        case BTRFS_IOC_CLONE:
                return btrfs_ioctl_clone(file, arg, 0, 0, 0);
        case BTRFS_IOC_CLONE_RANGE:
@@ -3419,11 +3451,15 @@ long btrfs_ioctl(struct file *file, unsigned int
        case BTRFS_IOC_SCRUB_PROGRESS:
                return btrfs_ioctl_scrub_progress(root, argp);
        case BTRFS_IOC_BALANCE_V2:
-               return btrfs_ioctl_balance(root, argp);
+               return btrfs_ioctl_balance(file, argp);
        case BTRFS_IOC_BALANCE_CTL:
                return btrfs_ioctl_balance_ctl(root, arg);
        case BTRFS_IOC_BALANCE_PROGRESS:
                return btrfs_ioctl_balance_progress(root, argp);
+       case BTRFS_IOC_GET_DEV_STATS:
+               return btrfs_ioctl_get_dev_stats(root, argp, 0);
+       case BTRFS_IOC_GET_AND_RESET_DEV_STATS:
+               return btrfs_ioctl_get_dev_stats(root, argp, 1);
        }
 
        return -ENOTTY;
index 086e6bdae1c4482b93b6dda4d16b1c5af288f2eb..497c530724cf6b7a50296d2c6660fef7f4066cb9 100644 (file)
@@ -266,6 +266,35 @@ struct btrfs_ioctl_logical_ino_args {
        __u64                           inodes;
 };
 
+enum btrfs_dev_stat_values {
+       /* disk I/O failure stats */
+       BTRFS_DEV_STAT_WRITE_ERRS, /* EIO or EREMOTEIO from lower layers */
+       BTRFS_DEV_STAT_READ_ERRS, /* EIO or EREMOTEIO from lower layers */
+       BTRFS_DEV_STAT_FLUSH_ERRS, /* EIO or EREMOTEIO from lower layers */
+
+       /* stats for indirect indications for I/O failures */
+       BTRFS_DEV_STAT_CORRUPTION_ERRS, /* checksum error, bytenr error or
+                                        * contents is illegal: this is an
+                                        * indication that the block was damaged
+                                        * during read or write, or written to
+                                        * wrong location or read from wrong
+                                        * location */
+       BTRFS_DEV_STAT_GENERATION_ERRS, /* an indication that blocks have not
+                                        * been written */
+
+       BTRFS_DEV_STAT_VALUES_MAX
+};
+
+struct btrfs_ioctl_get_dev_stats {
+       __u64 devid;                            /* in */
+       __u64 nr_items;                         /* in/out */
+
+       /* out values: */
+       __u64 values[BTRFS_DEV_STAT_VALUES_MAX];
+
+       __u64 unused[128 - 2 - BTRFS_DEV_STAT_VALUES_MAX]; /* pad to 1k */
+};
+
 #define BTRFS_IOC_SNAP_CREATE _IOW(BTRFS_IOCTL_MAGIC, 1, \
                                   struct btrfs_ioctl_vol_args)
 #define BTRFS_IOC_DEFRAG _IOW(BTRFS_IOCTL_MAGIC, 2, \
@@ -330,5 +359,9 @@ struct btrfs_ioctl_logical_ino_args {
                                        struct btrfs_ioctl_ino_path_args)
 #define BTRFS_IOC_LOGICAL_INO _IOWR(BTRFS_IOCTL_MAGIC, 36, \
                                        struct btrfs_ioctl_ino_path_args)
+#define BTRFS_IOC_GET_DEV_STATS _IOWR(BTRFS_IOCTL_MAGIC, 52, \
+                                     struct btrfs_ioctl_get_dev_stats)
+#define BTRFS_IOC_GET_AND_RESET_DEV_STATS _IOWR(BTRFS_IOCTL_MAGIC, 53, \
+                                       struct btrfs_ioctl_get_dev_stats)
 
 #endif
index bbf6d0d9aebe9b68f0ea8e5c121783d81733f7d7..9e138cdc36c5eb7d66bf80dfc37829878eeaa6e2 100644 (file)
@@ -196,7 +196,7 @@ static int __btrfs_add_ordered_extent(struct inode *inode, u64 file_offset,
        entry->len = len;
        entry->disk_len = disk_len;
        entry->bytes_left = len;
-       entry->inode = inode;
+       entry->inode = igrab(inode);
        entry->compress_type = compress_type;
        if (type != BTRFS_ORDERED_IO_DONE && type != BTRFS_ORDERED_COMPLETE)
                set_bit(type, &entry->flags);
@@ -212,12 +212,12 @@ static int __btrfs_add_ordered_extent(struct inode *inode, u64 file_offset,
 
        trace_btrfs_ordered_extent_add(inode, entry);
 
-       spin_lock(&tree->lock);
+       spin_lock_irq(&tree->lock);
        node = tree_insert(&tree->tree, file_offset,
                           &entry->rb_node);
        if (node)
                ordered_data_tree_panic(inode, -EEXIST, file_offset);
-       spin_unlock(&tree->lock);
+       spin_unlock_irq(&tree->lock);
 
        spin_lock(&BTRFS_I(inode)->root->fs_info->ordered_extent_lock);
        list_add_tail(&entry->root_extent_list,
@@ -264,9 +264,9 @@ void btrfs_add_ordered_sum(struct inode *inode,
        struct btrfs_ordered_inode_tree *tree;
 
        tree = &BTRFS_I(inode)->ordered_tree;
-       spin_lock(&tree->lock);
+       spin_lock_irq(&tree->lock);
        list_add_tail(&sum->list, &entry->list);
-       spin_unlock(&tree->lock);
+       spin_unlock_irq(&tree->lock);
 }
 
 /*
@@ -283,18 +283,19 @@ void btrfs_add_ordered_sum(struct inode *inode,
  */
 int btrfs_dec_test_first_ordered_pending(struct inode *inode,
                                   struct btrfs_ordered_extent **cached,
-                                  u64 *file_offset, u64 io_size)
+                                  u64 *file_offset, u64 io_size, int uptodate)
 {
        struct btrfs_ordered_inode_tree *tree;
        struct rb_node *node;
        struct btrfs_ordered_extent *entry = NULL;
        int ret;
+       unsigned long flags;
        u64 dec_end;
        u64 dec_start;
        u64 to_dec;
 
        tree = &BTRFS_I(inode)->ordered_tree;
-       spin_lock(&tree->lock);
+       spin_lock_irqsave(&tree->lock, flags);
        node = tree_search(tree, *file_offset);
        if (!node) {
                ret = 1;
@@ -323,6 +324,9 @@ int btrfs_dec_test_first_ordered_pending(struct inode *inode,
                       (unsigned long long)to_dec);
        }
        entry->bytes_left -= to_dec;
+       if (!uptodate)
+               set_bit(BTRFS_ORDERED_IOERR, &entry->flags);
+
        if (entry->bytes_left == 0)
                ret = test_and_set_bit(BTRFS_ORDERED_IO_DONE, &entry->flags);
        else
@@ -332,7 +336,7 @@ out:
                *cached = entry;
                atomic_inc(&entry->refs);
        }
-       spin_unlock(&tree->lock);
+       spin_unlock_irqrestore(&tree->lock, flags);
        return ret == 0;
 }
 
@@ -347,15 +351,21 @@ out:
  */
 int btrfs_dec_test_ordered_pending(struct inode *inode,
                                   struct btrfs_ordered_extent **cached,
-                                  u64 file_offset, u64 io_size)
+                                  u64 file_offset, u64 io_size, int uptodate)
 {
        struct btrfs_ordered_inode_tree *tree;
        struct rb_node *node;
        struct btrfs_ordered_extent *entry = NULL;
+       unsigned long flags;
        int ret;
 
        tree = &BTRFS_I(inode)->ordered_tree;
-       spin_lock(&tree->lock);
+       spin_lock_irqsave(&tree->lock, flags);
+       if (cached && *cached) {
+               entry = *cached;
+               goto have_entry;
+       }
+
        node = tree_search(tree, file_offset);
        if (!node) {
                ret = 1;
@@ -363,6 +373,7 @@ int btrfs_dec_test_ordered_pending(struct inode *inode,
        }
 
        entry = rb_entry(node, struct btrfs_ordered_extent, rb_node);
+have_entry:
        if (!offset_in_entry(entry, file_offset)) {
                ret = 1;
                goto out;
@@ -374,6 +385,9 @@ int btrfs_dec_test_ordered_pending(struct inode *inode,
                       (unsigned long long)io_size);
        }
        entry->bytes_left -= io_size;
+       if (!uptodate)
+               set_bit(BTRFS_ORDERED_IOERR, &entry->flags);
+
        if (entry->bytes_left == 0)
                ret = test_and_set_bit(BTRFS_ORDERED_IO_DONE, &entry->flags);
        else
@@ -383,7 +397,7 @@ out:
                *cached = entry;
                atomic_inc(&entry->refs);
        }
-       spin_unlock(&tree->lock);
+       spin_unlock_irqrestore(&tree->lock, flags);
        return ret == 0;
 }
 
@@ -399,6 +413,8 @@ void btrfs_put_ordered_extent(struct btrfs_ordered_extent *entry)
        trace_btrfs_ordered_extent_put(entry->inode, entry);
 
        if (atomic_dec_and_test(&entry->refs)) {
+               if (entry->inode)
+                       btrfs_add_delayed_iput(entry->inode);
                while (!list_empty(&entry->list)) {
                        cur = entry->list.next;
                        sum = list_entry(cur, struct btrfs_ordered_sum, list);
@@ -411,21 +427,22 @@ void btrfs_put_ordered_extent(struct btrfs_ordered_extent *entry)
 
 /*
  * remove an ordered extent from the tree.  No references are dropped
- * and you must wake_up entry->wait.  You must hold the tree lock
- * while you call this function.
+ * and waiters are woken up.
  */
-static void __btrfs_remove_ordered_extent(struct inode *inode,
-                                         struct btrfs_ordered_extent *entry)
+void btrfs_remove_ordered_extent(struct inode *inode,
+                                struct btrfs_ordered_extent *entry)
 {
        struct btrfs_ordered_inode_tree *tree;
        struct btrfs_root *root = BTRFS_I(inode)->root;
        struct rb_node *node;
 
        tree = &BTRFS_I(inode)->ordered_tree;
+       spin_lock_irq(&tree->lock);
        node = &entry->rb_node;
        rb_erase(node, &tree->tree);
        tree->last = NULL;
        set_bit(BTRFS_ORDERED_COMPLETE, &entry->flags);
+       spin_unlock_irq(&tree->lock);
 
        spin_lock(&root->fs_info->ordered_extent_lock);
        list_del_init(&entry->root_extent_list);
@@ -442,21 +459,6 @@ static void __btrfs_remove_ordered_extent(struct inode *inode,
                list_del_init(&BTRFS_I(inode)->ordered_operations);
        }
        spin_unlock(&root->fs_info->ordered_extent_lock);
-}
-
-/*
- * remove an ordered extent from the tree.  No references are dropped
- * but any waiters are woken.
- */
-void btrfs_remove_ordered_extent(struct inode *inode,
-                                struct btrfs_ordered_extent *entry)
-{
-       struct btrfs_ordered_inode_tree *tree;
-
-       tree = &BTRFS_I(inode)->ordered_tree;
-       spin_lock(&tree->lock);
-       __btrfs_remove_ordered_extent(inode, entry);
-       spin_unlock(&tree->lock);
        wake_up(&entry->wait);
 }
 
@@ -621,19 +623,11 @@ void btrfs_wait_ordered_range(struct inode *inode, u64 start, u64 len)
                if (orig_end > INT_LIMIT(loff_t))
                        orig_end = INT_LIMIT(loff_t);
        }
-again:
+
        /* start IO across the range first to instantiate any delalloc
         * extents
         */
-       filemap_fdatawrite_range(inode->i_mapping, start, orig_end);
-
-       /* The compression code will leave pages locked but return from
-        * writepage without setting the page writeback.  Starting again
-        * with WB_SYNC_ALL will end up waiting for the IO to actually start.
-        */
-       filemap_fdatawrite_range(inode->i_mapping, start, orig_end);
-
-       filemap_fdatawait_range(inode->i_mapping, start, orig_end);
+       filemap_write_and_wait_range(inode->i_mapping, start, orig_end);
 
        end = orig_end;
        found = 0;
@@ -657,11 +651,6 @@ again:
                        break;
                end--;
        }
-       if (found || test_range_bit(&BTRFS_I(inode)->io_tree, start, orig_end,
-                          EXTENT_DELALLOC, 0, NULL)) {
-               schedule_timeout(1);
-               goto again;
-       }
 }
 
 /*
@@ -676,7 +665,7 @@ struct btrfs_ordered_extent *btrfs_lookup_ordered_extent(struct inode *inode,
        struct btrfs_ordered_extent *entry = NULL;
 
        tree = &BTRFS_I(inode)->ordered_tree;
-       spin_lock(&tree->lock);
+       spin_lock_irq(&tree->lock);
        node = tree_search(tree, file_offset);
        if (!node)
                goto out;
@@ -687,7 +676,7 @@ struct btrfs_ordered_extent *btrfs_lookup_ordered_extent(struct inode *inode,
        if (entry)
                atomic_inc(&entry->refs);
 out:
-       spin_unlock(&tree->lock);
+       spin_unlock_irq(&tree->lock);
        return entry;
 }
 
@@ -703,7 +692,7 @@ struct btrfs_ordered_extent *btrfs_lookup_ordered_range(struct inode *inode,
        struct btrfs_ordered_extent *entry = NULL;
 
        tree = &BTRFS_I(inode)->ordered_tree;
-       spin_lock(&tree->lock);
+       spin_lock_irq(&tree->lock);
        node = tree_search(tree, file_offset);
        if (!node) {
                node = tree_search(tree, file_offset + len);
@@ -728,7 +717,7 @@ struct btrfs_ordered_extent *btrfs_lookup_ordered_range(struct inode *inode,
 out:
        if (entry)
                atomic_inc(&entry->refs);
-       spin_unlock(&tree->lock);
+       spin_unlock_irq(&tree->lock);
        return entry;
 }
 
@@ -744,7 +733,7 @@ btrfs_lookup_first_ordered_extent(struct inode *inode, u64 file_offset)
        struct btrfs_ordered_extent *entry = NULL;
 
        tree = &BTRFS_I(inode)->ordered_tree;
-       spin_lock(&tree->lock);
+       spin_lock_irq(&tree->lock);
        node = tree_search(tree, file_offset);
        if (!node)
                goto out;
@@ -752,7 +741,7 @@ btrfs_lookup_first_ordered_extent(struct inode *inode, u64 file_offset)
        entry = rb_entry(node, struct btrfs_ordered_extent, rb_node);
        atomic_inc(&entry->refs);
 out:
-       spin_unlock(&tree->lock);
+       spin_unlock_irq(&tree->lock);
        return entry;
 }
 
@@ -764,7 +753,6 @@ int btrfs_ordered_update_i_size(struct inode *inode, u64 offset,
                                struct btrfs_ordered_extent *ordered)
 {
        struct btrfs_ordered_inode_tree *tree = &BTRFS_I(inode)->ordered_tree;
-       struct extent_io_tree *io_tree = &BTRFS_I(inode)->io_tree;
        u64 disk_i_size;
        u64 new_i_size;
        u64 i_size_test;
@@ -779,7 +767,7 @@ int btrfs_ordered_update_i_size(struct inode *inode, u64 offset,
        else
                offset = ALIGN(offset, BTRFS_I(inode)->root->sectorsize);
 
-       spin_lock(&tree->lock);
+       spin_lock_irq(&tree->lock);
        disk_i_size = BTRFS_I(inode)->disk_i_size;
 
        /* truncate file */
@@ -797,14 +785,6 @@ int btrfs_ordered_update_i_size(struct inode *inode, u64 offset,
                goto out;
        }
 
-       /*
-        * we can't update the disk_isize if there are delalloc bytes
-        * between disk_i_size and  this ordered extent
-        */
-       if (test_range_bit(io_tree, disk_i_size, offset - 1,
-                          EXTENT_DELALLOC, 0, NULL)) {
-               goto out;
-       }
        /*
         * walk backward from this ordered extent to disk_i_size.
         * if we find an ordered extent then we can't update disk i_size
@@ -825,15 +805,18 @@ int btrfs_ordered_update_i_size(struct inode *inode, u64 offset,
                }
                node = prev;
        }
-       while (node) {
+       for (; node; node = rb_prev(node)) {
                test = rb_entry(node, struct btrfs_ordered_extent, rb_node);
+
+               /* We treat this entry as if it doesnt exist */
+               if (test_bit(BTRFS_ORDERED_UPDATED_ISIZE, &test->flags))
+                       continue;
                if (test->file_offset + test->len <= disk_i_size)
                        break;
                if (test->file_offset >= i_size)
                        break;
                if (test->file_offset >= disk_i_size)
                        goto out;
-               node = rb_prev(node);
        }
        new_i_size = min_t(u64, offset, i_size);
 
@@ -851,43 +834,49 @@ int btrfs_ordered_update_i_size(struct inode *inode, u64 offset,
                else
                        node = rb_first(&tree->tree);
        }
-       i_size_test = 0;
-       if (node) {
-               /*
-                * do we have an area where IO might have finished
-                * between our ordered extent and the next one.
-                */
+
+       /*
+        * We are looking for an area between our current extent and the next
+        * ordered extent to update the i_size to.  There are 3 cases here
+        *
+        * 1) We don't actually have anything and we can update to i_size.
+        * 2) We have stuff but they already did their i_size update so again we
+        * can just update to i_size.
+        * 3) We have an outstanding ordered extent so the most we can update
+        * our disk_i_size to is the start of the next offset.
+        */
+       i_size_test = i_size;
+       for (; node; node = rb_next(node)) {
                test = rb_entry(node, struct btrfs_ordered_extent, rb_node);
-               if (test->file_offset > offset)
+
+               if (test_bit(BTRFS_ORDERED_UPDATED_ISIZE, &test->flags))
+                       continue;
+               if (test->file_offset > offset) {
                        i_size_test = test->file_offset;
-       } else {
-               i_size_test = i_size;
+                       break;
+               }
        }
 
        /*
         * i_size_test is the end of a region after this ordered
-        * extent where there are no ordered extents.  As long as there
-        * are no delalloc bytes in this area, it is safe to update
-        * disk_i_size to the end of the region.
+        * extent where there are no ordered extents, we can safely set
+        * disk_i_size to this.
         */
-       if (i_size_test > offset &&
-           !test_range_bit(io_tree, offset, i_size_test - 1,
-                           EXTENT_DELALLOC, 0, NULL)) {
+       if (i_size_test > offset)
                new_i_size = min_t(u64, i_size_test, i_size);
-       }
        BTRFS_I(inode)->disk_i_size = new_i_size;
        ret = 0;
 out:
        /*
-        * we need to remove the ordered extent with the tree lock held
-        * so that other people calling this function don't find our fully
-        * processed ordered entry and skip updating the i_size
+        * We need to do this because we can't remove ordered extents until
+        * after the i_disk_size has been updated and then the inode has been
+        * updated to reflect the change, so we need to tell anybody who finds
+        * this ordered extent that we've already done all the real work, we
+        * just haven't completed all the other work.
         */
        if (ordered)
-               __btrfs_remove_ordered_extent(inode, ordered);
-       spin_unlock(&tree->lock);
-       if (ordered)
-               wake_up(&ordered->wait);
+               set_bit(BTRFS_ORDERED_UPDATED_ISIZE, &ordered->flags);
+       spin_unlock_irq(&tree->lock);
        return ret;
 }
 
@@ -912,7 +901,7 @@ int btrfs_find_ordered_sum(struct inode *inode, u64 offset, u64 disk_bytenr,
        if (!ordered)
                return 1;
 
-       spin_lock(&tree->lock);
+       spin_lock_irq(&tree->lock);
        list_for_each_entry_reverse(ordered_sum, &ordered->list, list) {
                if (disk_bytenr >= ordered_sum->bytenr) {
                        num_sectors = ordered_sum->len / sectorsize;
@@ -927,7 +916,7 @@ int btrfs_find_ordered_sum(struct inode *inode, u64 offset, u64 disk_bytenr,
                }
        }
 out:
-       spin_unlock(&tree->lock);
+       spin_unlock_irq(&tree->lock);
        btrfs_put_ordered_extent(ordered);
        return ret;
 }
index c355ad4dc1a66962d30557e9bbdc08ca9fc25da8..e03c560d299732cfe2114fe41d049b691a949e61 100644 (file)
@@ -74,6 +74,12 @@ struct btrfs_ordered_sum {
 
 #define BTRFS_ORDERED_DIRECT 5 /* set when we're doing DIO with this extent */
 
+#define BTRFS_ORDERED_IOERR 6 /* We had an io error when writing this out */
+
+#define BTRFS_ORDERED_UPDATED_ISIZE 7 /* indicates wether this ordered extent
+                                      * has done its due diligence in updating
+                                      * the isize. */
+
 struct btrfs_ordered_extent {
        /* logical offset in the file */
        u64 file_offset;
@@ -113,6 +119,8 @@ struct btrfs_ordered_extent {
 
        /* a per root list of all the pending ordered extents */
        struct list_head root_extent_list;
+
+       struct btrfs_work work;
 };
 
 
@@ -143,10 +151,11 @@ void btrfs_remove_ordered_extent(struct inode *inode,
                                struct btrfs_ordered_extent *entry);
 int btrfs_dec_test_ordered_pending(struct inode *inode,
                                   struct btrfs_ordered_extent **cached,
-                                  u64 file_offset, u64 io_size);
+                                  u64 file_offset, u64 io_size, int uptodate);
 int btrfs_dec_test_first_ordered_pending(struct inode *inode,
                                   struct btrfs_ordered_extent **cached,
-                                  u64 *file_offset, u64 io_size);
+                                  u64 *file_offset, u64 io_size,
+                                  int uptodate);
 int btrfs_add_ordered_extent(struct inode *inode, u64 file_offset,
                             u64 start, u64 len, u64 disk_len, int type);
 int btrfs_add_ordered_extent_dio(struct inode *inode, u64 file_offset,
index f38e452486b8d12ba36589248579dc158981c3be..5e23684887eb8eb401594af69b1be7372f7188aa 100644 (file)
@@ -294,6 +294,9 @@ void btrfs_print_leaf(struct btrfs_root *root, struct extent_buffer *l)
                               btrfs_dev_extent_chunk_offset(l, dev_extent),
                               (unsigned long long)
                               btrfs_dev_extent_length(l, dev_extent));
+               case BTRFS_DEV_STATS_KEY:
+                       printk(KERN_INFO "\t\tdevice stats\n");
+                       break;
                };
        }
 }
index ac5d010858848d007e380d529476ad9eb4f6fb31..48a4882d8ad5955eaa0be2b940e35f0b3b2a7f6f 100644 (file)
@@ -718,13 +718,18 @@ static void reada_start_machine_worker(struct btrfs_work *work)
 {
        struct reada_machine_work *rmw;
        struct btrfs_fs_info *fs_info;
+       int old_ioprio;
 
        rmw = container_of(work, struct reada_machine_work, work);
        fs_info = rmw->fs_info;
 
        kfree(rmw);
 
+       old_ioprio = IOPRIO_PRIO_VALUE(task_nice_ioclass(current),
+                                      task_nice_ioprio(current));
+       set_task_ioprio(current, BTRFS_IOPRIO_READA);
        __reada_start_machine(fs_info);
+       set_task_ioprio(current, old_ioprio);
 }
 
 static void __reada_start_machine(struct btrfs_fs_info *fs_info)
index 2f3d6f917fb3373c02335b6912fcba1006f5fabe..a38cfa4f251ec1065410f561188c4adf5868cea3 100644 (file)
@@ -50,7 +50,7 @@ struct scrub_dev;
 struct scrub_page {
        struct scrub_block      *sblock;
        struct page             *page;
-       struct block_device     *bdev;
+       struct btrfs_device     *dev;
        u64                     flags;  /* extent flags */
        u64                     generation;
        u64                     logical;
@@ -86,6 +86,7 @@ struct scrub_block {
                unsigned int    header_error:1;
                unsigned int    checksum_error:1;
                unsigned int    no_io_error_seen:1;
+               unsigned int    generation_error:1; /* also sets header_error */
        };
 };
 
@@ -675,6 +676,8 @@ static int scrub_handle_errored_block(struct scrub_block *sblock_to_check)
                sdev->stat.read_errors++;
                sdev->stat.uncorrectable_errors++;
                spin_unlock(&sdev->stat_lock);
+               btrfs_dev_stat_inc_and_print(sdev->dev,
+                                            BTRFS_DEV_STAT_READ_ERRS);
                goto out;
        }
 
@@ -686,6 +689,8 @@ static int scrub_handle_errored_block(struct scrub_block *sblock_to_check)
                sdev->stat.read_errors++;
                sdev->stat.uncorrectable_errors++;
                spin_unlock(&sdev->stat_lock);
+               btrfs_dev_stat_inc_and_print(sdev->dev,
+                                            BTRFS_DEV_STAT_READ_ERRS);
                goto out;
        }
        BUG_ON(failed_mirror_index >= BTRFS_MAX_MIRRORS);
@@ -699,6 +704,8 @@ static int scrub_handle_errored_block(struct scrub_block *sblock_to_check)
                sdev->stat.read_errors++;
                sdev->stat.uncorrectable_errors++;
                spin_unlock(&sdev->stat_lock);
+               btrfs_dev_stat_inc_and_print(sdev->dev,
+                                            BTRFS_DEV_STAT_READ_ERRS);
                goto out;
        }
 
@@ -725,12 +732,16 @@ static int scrub_handle_errored_block(struct scrub_block *sblock_to_check)
                spin_unlock(&sdev->stat_lock);
                if (__ratelimit(&_rs))
                        scrub_print_warning("i/o error", sblock_to_check);
+               btrfs_dev_stat_inc_and_print(sdev->dev,
+                                            BTRFS_DEV_STAT_READ_ERRS);
        } else if (sblock_bad->checksum_error) {
                spin_lock(&sdev->stat_lock);
                sdev->stat.csum_errors++;
                spin_unlock(&sdev->stat_lock);
                if (__ratelimit(&_rs))
                        scrub_print_warning("checksum error", sblock_to_check);
+               btrfs_dev_stat_inc_and_print(sdev->dev,
+                                            BTRFS_DEV_STAT_CORRUPTION_ERRS);
        } else if (sblock_bad->header_error) {
                spin_lock(&sdev->stat_lock);
                sdev->stat.verify_errors++;
@@ -738,6 +749,12 @@ static int scrub_handle_errored_block(struct scrub_block *sblock_to_check)
                if (__ratelimit(&_rs))
                        scrub_print_warning("checksum/header error",
                                            sblock_to_check);
+               if (sblock_bad->generation_error)
+                       btrfs_dev_stat_inc_and_print(sdev->dev,
+                               BTRFS_DEV_STAT_GENERATION_ERRS);
+               else
+                       btrfs_dev_stat_inc_and_print(sdev->dev,
+                               BTRFS_DEV_STAT_CORRUPTION_ERRS);
        }
 
        if (sdev->readonly)
@@ -998,8 +1015,8 @@ static int scrub_setup_recheck_block(struct scrub_dev *sdev,
                        page = sblock->pagev + page_index;
                        page->logical = logical;
                        page->physical = bbio->stripes[mirror_index].physical;
-                       /* for missing devices, bdev is NULL */
-                       page->bdev = bbio->stripes[mirror_index].dev->bdev;
+                       /* for missing devices, dev->bdev is NULL */
+                       page->dev = bbio->stripes[mirror_index].dev;
                        page->mirror_num = mirror_index + 1;
                        page->page = alloc_page(GFP_NOFS);
                        if (!page->page) {
@@ -1043,7 +1060,7 @@ static int scrub_recheck_block(struct btrfs_fs_info *fs_info,
                struct scrub_page *page = sblock->pagev + page_num;
                DECLARE_COMPLETION_ONSTACK(complete);
 
-               if (page->bdev == NULL) {
+               if (page->dev->bdev == NULL) {
                        page->io_error = 1;
                        sblock->no_io_error_seen = 0;
                        continue;
@@ -1053,7 +1070,7 @@ static int scrub_recheck_block(struct btrfs_fs_info *fs_info,
                bio = bio_alloc(GFP_NOFS, 1);
                if (!bio)
                        return -EIO;
-               bio->bi_bdev = page->bdev;
+               bio->bi_bdev = page->dev->bdev;
                bio->bi_sector = page->physical >> 9;
                bio->bi_end_io = scrub_complete_bio_end_io;
                bio->bi_private = &complete;
@@ -1102,11 +1119,14 @@ static void scrub_recheck_block_checksum(struct btrfs_fs_info *fs_info,
                h = (struct btrfs_header *)mapped_buffer;
 
                if (sblock->pagev[0].logical != le64_to_cpu(h->bytenr) ||
-                   generation != le64_to_cpu(h->generation) ||
                    memcmp(h->fsid, fs_info->fsid, BTRFS_UUID_SIZE) ||
                    memcmp(h->chunk_tree_uuid, fs_info->chunk_tree_uuid,
-                          BTRFS_UUID_SIZE))
+                          BTRFS_UUID_SIZE)) {
                        sblock->header_error = 1;
+               } else if (generation != le64_to_cpu(h->generation)) {
+                       sblock->header_error = 1;
+                       sblock->generation_error = 1;
+               }
                csum = h->csum;
        } else {
                if (!have_csum)
@@ -1182,7 +1202,7 @@ static int scrub_repair_page_from_good_copy(struct scrub_block *sblock_bad,
                bio = bio_alloc(GFP_NOFS, 1);
                if (!bio)
                        return -EIO;
-               bio->bi_bdev = page_bad->bdev;
+               bio->bi_bdev = page_bad->dev->bdev;
                bio->bi_sector = page_bad->physical >> 9;
                bio->bi_end_io = scrub_complete_bio_end_io;
                bio->bi_private = &complete;
@@ -1196,6 +1216,12 @@ static int scrub_repair_page_from_good_copy(struct scrub_block *sblock_bad,
 
                /* this will also unplug the queue */
                wait_for_completion(&complete);
+               if (!bio_flagged(bio, BIO_UPTODATE)) {
+                       btrfs_dev_stat_inc_and_print(page_bad->dev,
+                               BTRFS_DEV_STAT_WRITE_ERRS);
+                       bio_put(bio);
+                       return -EIO;
+               }
                bio_put(bio);
        }
 
@@ -1352,7 +1378,8 @@ static int scrub_checksum_super(struct scrub_block *sblock)
        u64 mapped_size;
        void *p;
        u32 crc = ~(u32)0;
-       int fail = 0;
+       int fail_gen = 0;
+       int fail_cor = 0;
        u64 len;
        int index;
 
@@ -1363,13 +1390,13 @@ static int scrub_checksum_super(struct scrub_block *sblock)
        memcpy(on_disk_csum, s->csum, sdev->csum_size);
 
        if (sblock->pagev[0].logical != le64_to_cpu(s->bytenr))
-               ++fail;
+               ++fail_cor;
 
        if (sblock->pagev[0].generation != le64_to_cpu(s->generation))
-               ++fail;
+               ++fail_gen;
 
        if (memcmp(s->fsid, fs_info->fsid, BTRFS_UUID_SIZE))
-               ++fail;
+               ++fail_cor;
 
        len = BTRFS_SUPER_INFO_SIZE - BTRFS_CSUM_SIZE;
        mapped_size = PAGE_SIZE - BTRFS_CSUM_SIZE;
@@ -1394,9 +1421,9 @@ static int scrub_checksum_super(struct scrub_block *sblock)
 
        btrfs_csum_final(crc, calculated_csum);
        if (memcmp(calculated_csum, on_disk_csum, sdev->csum_size))
-               ++fail;
+               ++fail_cor;
 
-       if (fail) {
+       if (fail_cor + fail_gen) {
                /*
                 * if we find an error in a super block, we just report it.
                 * They will get written with the next transaction commit
@@ -1405,9 +1432,15 @@ static int scrub_checksum_super(struct scrub_block *sblock)
                spin_lock(&sdev->stat_lock);
                ++sdev->stat.super_errors;
                spin_unlock(&sdev->stat_lock);
+               if (fail_cor)
+                       btrfs_dev_stat_inc_and_print(sdev->dev,
+                               BTRFS_DEV_STAT_CORRUPTION_ERRS);
+               else
+                       btrfs_dev_stat_inc_and_print(sdev->dev,
+                               BTRFS_DEV_STAT_GENERATION_ERRS);
        }
 
-       return fail;
+       return fail_cor + fail_gen;
 }
 
 static void scrub_block_get(struct scrub_block *sblock)
@@ -1551,7 +1584,7 @@ static int scrub_pages(struct scrub_dev *sdev, u64 logical, u64 len,
                        return -ENOMEM;
                }
                spage->sblock = sblock;
-               spage->bdev = sdev->dev->bdev;
+               spage->dev = sdev->dev;
                spage->flags = flags;
                spage->generation = gen;
                spage->logical = logical;
index c5f8fca4195fca9eb3806ebfbccf52d03049691e..96eb9fef7bd279584cf4dd8b6ed42cc09e425c1d 100644 (file)
@@ -188,7 +188,8 @@ void btrfs_printk(struct btrfs_fs_info *fs_info, const char *fmt, ...)
        va_start(args, fmt);
 
        if (fmt[0] == '<' && isdigit(fmt[1]) && fmt[2] == '>') {
-               strncpy(lvl, fmt, 3);
+               memcpy(lvl, fmt, 3);
+               lvl[3] = '\0';
                fmt += 3;
                type = logtypes[fmt[1] - '0'];
        } else
@@ -435,11 +436,8 @@ int btrfs_parse_options(struct btrfs_root *root, char *options)
                case Opt_thread_pool:
                        intarg = 0;
                        match_int(&args[0], &intarg);
-                       if (intarg) {
+                       if (intarg)
                                info->thread_pool_size = intarg;
-                               printk(KERN_INFO "btrfs: thread pool %d\n",
-                                      info->thread_pool_size);
-                       }
                        break;
                case Opt_max_inline:
                        num = match_strdup(&args[0]);
@@ -769,7 +767,7 @@ static int btrfs_fill_super(struct super_block *sb,
 #ifdef CONFIG_BTRFS_FS_POSIX_ACL
        sb->s_flags |= MS_POSIXACL;
 #endif
-
+       sb->s_flags |= MS_I_VERSION;
        err = open_ctree(sb, fs_devices, (char *)data);
        if (err) {
                printk("btrfs: open_ctree failed\n");
@@ -925,63 +923,48 @@ static inline int is_subvolume_inode(struct inode *inode)
  */
 static char *setup_root_args(char *args)
 {
-       unsigned copied = 0;
-       unsigned len = strlen(args) + 2;
-       char *pos;
-       char *ret;
+       unsigned len = strlen(args) + 2 + 1;
+       char *src, *dst, *buf;
 
        /*
-        * We need the same args as before, but minus
-        *
-        * subvol=a
-        *
-        * and add
-        *
-        * subvolid=0
+        * We need the same args as before, but with this substitution:
+        * s!subvol=[^,]+!subvolid=0!
         *
-        * which is a difference of 2 characters, so we allocate strlen(args) +
-        * 2 characters.
+        * Since the replacement string is up to 2 bytes longer than the
+        * original, allocate strlen(args) + 2 + 1 bytes.
         */
-       ret = kzalloc(len * sizeof(char), GFP_NOFS);
-       if (!ret)
-               return NULL;
-       pos = strstr(args, "subvol=");
 
+       src = strstr(args, "subvol=");
        /* This shouldn't happen, but just in case.. */
-       if (!pos) {
-               kfree(ret);
+       if (!src)
+               return NULL;
+
+       buf = dst = kmalloc(len, GFP_NOFS);
+       if (!buf)
                return NULL;
-       }
 
        /*
-        * The subvol=<> arg is not at the front of the string, copy everybody
-        * up to that into ret.
+        * If the subvol= arg is not at the start of the string,
+        * copy whatever precedes it into buf.
         */
-       if (pos != args) {
-               *pos = '\0';
-               strcpy(ret, args);
-               copied += strlen(args);
-               pos++;
+       if (src != args) {
+               *src++ = '\0';
+               strcpy(buf, args);
+               dst += strlen(args);
        }
 
-       strncpy(ret + copied, "subvolid=0", len - copied);
-
-       /* Length of subvolid=0 */
-       copied += 10;
+       strcpy(dst, "subvolid=0");
+       dst += strlen("subvolid=0");
 
        /*
-        * If there is no , after the subvol= option then we know there's no
-        * other options and we can just return.
+        * If there is a "," after the original subvol=... string,
+        * copy that suffix into our buffer.  Otherwise, we're done.
         */
-       pos = strchr(pos, ',');
-       if (!pos)
-               return ret;
+       src = strchr(src, ',');
+       if (src)
+               strcpy(dst, src);
 
-       /* Copy the rest of the arguments into our buffer */
-       strncpy(ret + copied, pos, len - copied);
-       copied += strlen(pos);
-
-       return ret;
+       return buf;
 }
 
 static struct dentry *mount_subvol(const char *subvol_name, int flags,
@@ -1118,6 +1101,40 @@ error_fs_info:
        return ERR_PTR(error);
 }
 
+static void btrfs_set_max_workers(struct btrfs_workers *workers, int new_limit)
+{
+       spin_lock_irq(&workers->lock);
+       workers->max_workers = new_limit;
+       spin_unlock_irq(&workers->lock);
+}
+
+static void btrfs_resize_thread_pool(struct btrfs_fs_info *fs_info,
+                                    int new_pool_size, int old_pool_size)
+{
+       if (new_pool_size == old_pool_size)
+               return;
+
+       fs_info->thread_pool_size = new_pool_size;
+
+       printk(KERN_INFO "btrfs: resize thread pool %d -> %d\n",
+              old_pool_size, new_pool_size);
+
+       btrfs_set_max_workers(&fs_info->generic_worker, new_pool_size);
+       btrfs_set_max_workers(&fs_info->workers, new_pool_size);
+       btrfs_set_max_workers(&fs_info->delalloc_workers, new_pool_size);
+       btrfs_set_max_workers(&fs_info->submit_workers, new_pool_size);
+       btrfs_set_max_workers(&fs_info->caching_workers, new_pool_size);
+       btrfs_set_max_workers(&fs_info->fixup_workers, new_pool_size);
+       btrfs_set_max_workers(&fs_info->endio_workers, new_pool_size);
+       btrfs_set_max_workers(&fs_info->endio_meta_workers, new_pool_size);
+       btrfs_set_max_workers(&fs_info->endio_meta_write_workers, new_pool_size);
+       btrfs_set_max_workers(&fs_info->endio_write_workers, new_pool_size);
+       btrfs_set_max_workers(&fs_info->endio_freespace_worker, new_pool_size);
+       btrfs_set_max_workers(&fs_info->delayed_workers, new_pool_size);
+       btrfs_set_max_workers(&fs_info->readahead_workers, new_pool_size);
+       btrfs_set_max_workers(&fs_info->scrub_workers, new_pool_size);
+}
+
 static int btrfs_remount(struct super_block *sb, int *flags, char *data)
 {
        struct btrfs_fs_info *fs_info = btrfs_sb(sb);
@@ -1137,6 +1154,9 @@ static int btrfs_remount(struct super_block *sb, int *flags, char *data)
                goto restore;
        }
 
+       btrfs_resize_thread_pool(fs_info,
+               fs_info->thread_pool_size, old_thread_pool_size);
+
        if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY))
                return 0;
 
@@ -1180,7 +1200,8 @@ restore:
        fs_info->compress_type = old_compress_type;
        fs_info->max_inline = old_max_inline;
        fs_info->alloc_start = old_alloc_start;
-       fs_info->thread_pool_size = old_thread_pool_size;
+       btrfs_resize_thread_pool(fs_info,
+               old_thread_pool_size, fs_info->thread_pool_size);
        fs_info->metadata_ratio = old_metadata_ratio;
        return ret;
 }
index 36422254ef6765c14290a2373fa6d83cf2d364d5..1791c6e3d83487d82c9ffe80ab0239976cfd1c96 100644 (file)
@@ -28,6 +28,7 @@
 #include "locking.h"
 #include "tree-log.h"
 #include "inode-map.h"
+#include "volumes.h"
 
 #define BTRFS_ROOT_TRANS_TAG 0
 
@@ -55,48 +56,49 @@ static noinline void switch_commit_root(struct btrfs_root *root)
 static noinline int join_transaction(struct btrfs_root *root, int nofail)
 {
        struct btrfs_transaction *cur_trans;
+       struct btrfs_fs_info *fs_info = root->fs_info;
 
-       spin_lock(&root->fs_info->trans_lock);
+       spin_lock(&fs_info->trans_lock);
 loop:
        /* The file system has been taken offline. No new transactions. */
-       if (root->fs_info->fs_state & BTRFS_SUPER_FLAG_ERROR) {
-               spin_unlock(&root->fs_info->trans_lock);
+       if (fs_info->fs_state & BTRFS_SUPER_FLAG_ERROR) {
+               spin_unlock(&fs_info->trans_lock);
                return -EROFS;
        }
 
-       if (root->fs_info->trans_no_join) {
+       if (fs_info->trans_no_join) {
                if (!nofail) {
-                       spin_unlock(&root->fs_info->trans_lock);
+                       spin_unlock(&fs_info->trans_lock);
                        return -EBUSY;
                }
        }
 
-       cur_trans = root->fs_info->running_transaction;
+       cur_trans = fs_info->running_transaction;
        if (cur_trans) {
                if (cur_trans->aborted) {
-                       spin_unlock(&root->fs_info->trans_lock);
+                       spin_unlock(&fs_info->trans_lock);
                        return cur_trans->aborted;
                }
                atomic_inc(&cur_trans->use_count);
                atomic_inc(&cur_trans->num_writers);
                cur_trans->num_joined++;
-               spin_unlock(&root->fs_info->trans_lock);
+               spin_unlock(&fs_info->trans_lock);
                return 0;
        }
-       spin_unlock(&root->fs_info->trans_lock);
+       spin_unlock(&fs_info->trans_lock);
 
        cur_trans = kmem_cache_alloc(btrfs_transaction_cachep, GFP_NOFS);
        if (!cur_trans)
                return -ENOMEM;
 
-       spin_lock(&root->fs_info->trans_lock);
-       if (root->fs_info->running_transaction) {
+       spin_lock(&fs_info->trans_lock);
+       if (fs_info->running_transaction) {
                /*
                 * someone started a transaction after we unlocked.  Make sure
                 * to redo the trans_no_join checks above
                 */
                kmem_cache_free(btrfs_transaction_cachep, cur_trans);
-               cur_trans = root->fs_info->running_transaction;
+               cur_trans = fs_info->running_transaction;
                goto loop;
        }
 
@@ -121,20 +123,38 @@ loop:
        cur_trans->delayed_refs.flushing = 0;
        cur_trans->delayed_refs.run_delayed_start = 0;
        cur_trans->delayed_refs.seq = 1;
+
+       /*
+        * although the tree mod log is per file system and not per transaction,
+        * the log must never go across transaction boundaries.
+        */
+       smp_mb();
+       if (!list_empty(&fs_info->tree_mod_seq_list)) {
+               printk(KERN_ERR "btrfs: tree_mod_seq_list not empty when "
+                       "creating a fresh transaction\n");
+               WARN_ON(1);
+       }
+       if (!RB_EMPTY_ROOT(&fs_info->tree_mod_log)) {
+               printk(KERN_ERR "btrfs: tree_mod_log rb tree not empty when "
+                       "creating a fresh transaction\n");
+               WARN_ON(1);
+       }
+       atomic_set(&fs_info->tree_mod_seq, 0);
+
        init_waitqueue_head(&cur_trans->delayed_refs.seq_wait);
        spin_lock_init(&cur_trans->commit_lock);
        spin_lock_init(&cur_trans->delayed_refs.lock);
        INIT_LIST_HEAD(&cur_trans->delayed_refs.seq_head);
 
        INIT_LIST_HEAD(&cur_trans->pending_snapshots);
-       list_add_tail(&cur_trans->list, &root->fs_info->trans_list);
+       list_add_tail(&cur_trans->list, &fs_info->trans_list);
        extent_io_tree_init(&cur_trans->dirty_pages,
-                            root->fs_info->btree_inode->i_mapping);
-       root->fs_info->generation++;
-       cur_trans->transid = root->fs_info->generation;
-       root->fs_info->running_transaction = cur_trans;
+                            fs_info->btree_inode->i_mapping);
+       fs_info->generation++;
+       cur_trans->transid = fs_info->generation;
+       fs_info->running_transaction = cur_trans;
        cur_trans->aborted = 0;
-       spin_unlock(&root->fs_info->trans_lock);
+       spin_unlock(&fs_info->trans_lock);
 
        return 0;
 }
@@ -758,6 +778,9 @@ static noinline int commit_cowonly_roots(struct btrfs_trans_handle *trans,
        if (ret)
                return ret;
 
+       ret = btrfs_run_dev_stats(trans, root->fs_info);
+       BUG_ON(ret);
+
        while (!list_empty(&fs_info->dirty_cowonly_roots)) {
                next = fs_info->dirty_cowonly_roots.next;
                list_del_init(next);
index eb1ae908582cc51162a61798c80f3ed38e7ab6e8..2017d0ff511ca3304dad46e85045ad2ab28d4e75 100644 (file)
@@ -1628,7 +1628,9 @@ static int replay_one_buffer(struct btrfs_root *log, struct extent_buffer *eb,
        int i;
        int ret;
 
-       btrfs_read_buffer(eb, gen);
+       ret = btrfs_read_buffer(eb, gen);
+       if (ret)
+               return ret;
 
        level = btrfs_header_level(eb);
 
@@ -1749,7 +1751,11 @@ static noinline int walk_down_log_tree(struct btrfs_trans_handle *trans,
 
                        path->slots[*level]++;
                        if (wc->free) {
-                               btrfs_read_buffer(next, ptr_gen);
+                               ret = btrfs_read_buffer(next, ptr_gen);
+                               if (ret) {
+                                       free_extent_buffer(next);
+                                       return ret;
+                               }
 
                                btrfs_tree_lock(next);
                                btrfs_set_lock_blocking(next);
@@ -1766,7 +1772,11 @@ static noinline int walk_down_log_tree(struct btrfs_trans_handle *trans,
                        free_extent_buffer(next);
                        continue;
                }
-               btrfs_read_buffer(next, ptr_gen);
+               ret = btrfs_read_buffer(next, ptr_gen);
+               if (ret) {
+                       free_extent_buffer(next);
+                       return ret;
+               }
 
                WARN_ON(*level <= 0);
                if (path->nodes[*level-1])
@@ -2657,6 +2667,8 @@ static int drop_objectid_items(struct btrfs_trans_handle *trans,
                btrfs_release_path(path);
        }
        btrfs_release_path(path);
+       if (ret > 0)
+               ret = 0;
        return ret;
 }
 
@@ -3028,21 +3040,6 @@ out:
        return ret;
 }
 
-static int inode_in_log(struct btrfs_trans_handle *trans,
-                struct inode *inode)
-{
-       struct btrfs_root *root = BTRFS_I(inode)->root;
-       int ret = 0;
-
-       mutex_lock(&root->log_mutex);
-       if (BTRFS_I(inode)->logged_trans == trans->transid &&
-           BTRFS_I(inode)->last_sub_trans <= root->last_log_commit)
-               ret = 1;
-       mutex_unlock(&root->log_mutex);
-       return ret;
-}
-
-
 /*
  * helper function around btrfs_log_inode to make sure newly created
  * parent directories also end up in the log.  A minimal inode and backref
@@ -3083,7 +3080,7 @@ int btrfs_log_inode_parent(struct btrfs_trans_handle *trans,
        if (ret)
                goto end_no_trans;
 
-       if (inode_in_log(trans, inode)) {
+       if (btrfs_inode_in_log(inode, trans->transid)) {
                ret = BTRFS_NO_LOG_SYNC;
                goto end_no_trans;
        }
index 12f5147bd2b1ae2a6016e7283c72ceccb44283b7..ab942f46b3dd81e06348c4950901f3e4eef87016 100644 (file)
@@ -23,9 +23,9 @@
  *
  * ulist = ulist_alloc();
  * ulist_add(ulist, root);
- * elem = NULL;
+ * ULIST_ITER_INIT(&uiter);
  *
- * while ((elem = ulist_next(ulist, elem)) {
+ * while ((elem = ulist_next(ulist, &uiter)) {
  *     for (all child nodes n in elem)
  *             ulist_add(ulist, n);
  *     do something useful with the node;
@@ -95,7 +95,7 @@ EXPORT_SYMBOL(ulist_reinit);
  *
  * The allocated ulist will be returned in an initialized state.
  */
-struct ulist *ulist_alloc(unsigned long gfp_mask)
+struct ulist *ulist_alloc(gfp_t gfp_mask)
 {
        struct ulist *ulist = kmalloc(sizeof(*ulist), gfp_mask);
 
@@ -144,13 +144,22 @@ EXPORT_SYMBOL(ulist_free);
  * unaltered.
  */
 int ulist_add(struct ulist *ulist, u64 val, unsigned long aux,
-             unsigned long gfp_mask)
+             gfp_t gfp_mask)
+{
+       return ulist_add_merge(ulist, val, aux, NULL, gfp_mask);
+}
+
+int ulist_add_merge(struct ulist *ulist, u64 val, unsigned long aux,
+                   unsigned long *old_aux, gfp_t gfp_mask)
 {
        int i;
 
        for (i = 0; i < ulist->nnodes; ++i) {
-               if (ulist->nodes[i].val == val)
+               if (ulist->nodes[i].val == val) {
+                       if (old_aux)
+                               *old_aux = ulist->nodes[i].aux;
                        return 0;
+               }
        }
 
        if (ulist->nnodes >= ulist->nodes_alloced) {
@@ -188,33 +197,26 @@ EXPORT_SYMBOL(ulist_add);
 /**
  * ulist_next - iterate ulist
  * @ulist:     ulist to iterate
- * @prev:      previously returned element or %NULL to start iteration
+ * @uiter:     iterator variable, initialized with ULIST_ITER_INIT(&iterator)
  *
  * Note: locking must be provided by the caller. In case of rwlocks only read
  *       locking is needed
  *
- * This function is used to iterate an ulist. The iteration is started with
- * @prev = %NULL. It returns the next element from the ulist or %NULL when the
+ * This function is used to iterate an ulist.
+ * It returns the next element from the ulist or %NULL when the
  * end is reached. No guarantee is made with respect to the order in which
  * the elements are returned. They might neither be returned in order of
  * addition nor in ascending order.
  * It is allowed to call ulist_add during an enumeration. Newly added items
  * are guaranteed to show up in the running enumeration.
  */
-struct ulist_node *ulist_next(struct ulist *ulist, struct ulist_node *prev)
+struct ulist_node *ulist_next(struct ulist *ulist, struct ulist_iterator *uiter)
 {
-       int next;
-
        if (ulist->nnodes == 0)
                return NULL;
-
-       if (!prev)
-               return &ulist->nodes[0];
-
-       next = (prev - ulist->nodes) + 1;
-       if (next < 0 || next >= ulist->nnodes)
+       if (uiter->i < 0 || uiter->i >= ulist->nnodes)
                return NULL;
 
-       return &ulist->nodes[next];
+       return &ulist->nodes[uiter->i++];
 }
 EXPORT_SYMBOL(ulist_next);
index 2e25dec58ec0e56251fbca880d27cc927aac95dc..21bdc8ec813046ac56e3c7db0739bcdba7ac188a 100644 (file)
  */
 #define ULIST_SIZE 16
 
+struct ulist_iterator {
+       int i;
+};
+
 /*
  * element of the list
  */
@@ -59,10 +63,15 @@ struct ulist {
 void ulist_init(struct ulist *ulist);
 void ulist_fini(struct ulist *ulist);
 void ulist_reinit(struct ulist *ulist);
-struct ulist *ulist_alloc(unsigned long gfp_mask);
+struct ulist *ulist_alloc(gfp_t gfp_mask);
 void ulist_free(struct ulist *ulist);
 int ulist_add(struct ulist *ulist, u64 val, unsigned long aux,
-             unsigned long gfp_mask);
-struct ulist_node *ulist_next(struct ulist *ulist, struct ulist_node *prev);
+             gfp_t gfp_mask);
+int ulist_add_merge(struct ulist *ulist, u64 val, unsigned long aux,
+                   unsigned long *old_aux, gfp_t gfp_mask);
+struct ulist_node *ulist_next(struct ulist *ulist,
+                             struct ulist_iterator *uiter);
+
+#define ULIST_ITER_INIT(uiter) ((uiter)->i = 0)
 
 #endif
index 1411b99555a4c1f138a6a3bf699842849d2b3e08..7782020996feccd4b7103528a4c2989230f79b71 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/random.h>
 #include <linux/iocontext.h>
 #include <linux/capability.h>
+#include <linux/ratelimit.h>
 #include <linux/kthread.h>
 #include <asm/div64.h>
 #include "compat.h"
@@ -39,6 +40,8 @@ static int init_first_rw_device(struct btrfs_trans_handle *trans,
                                struct btrfs_root *root,
                                struct btrfs_device *device);
 static int btrfs_relocate_sys_chunks(struct btrfs_root *root);
+static void __btrfs_reset_dev_stats(struct btrfs_device *dev);
+static void btrfs_dev_stat_print_on_load(struct btrfs_device *device);
 
 static DEFINE_MUTEX(uuid_mutex);
 static LIST_HEAD(fs_uuids);
@@ -361,6 +364,7 @@ static noinline int device_list_add(const char *path,
                        return -ENOMEM;
                }
                device->devid = devid;
+               device->dev_stats_valid = 0;
                device->work.func = pending_bios_fn;
                memcpy(device->uuid, disk_super->dev_item.uuid,
                       BTRFS_UUID_SIZE);
@@ -1633,7 +1637,7 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path)
        int ret = 0;
 
        if ((sb->s_flags & MS_RDONLY) && !root->fs_info->fs_devices->seeding)
-               return -EINVAL;
+               return -EROFS;
 
        bdev = blkdev_get_by_path(device_path, FMODE_WRITE | FMODE_EXCL,
                                  root->fs_info->bdev_holder);
@@ -4001,13 +4005,58 @@ int btrfs_rmap_block(struct btrfs_mapping_tree *map_tree,
        return 0;
 }
 
+static void *merge_stripe_index_into_bio_private(void *bi_private,
+                                                unsigned int stripe_index)
+{
+       /*
+        * with single, dup, RAID0, RAID1 and RAID10, stripe_index is
+        * at most 1.
+        * The alternative solution (instead of stealing bits from the
+        * pointer) would be to allocate an intermediate structure
+        * that contains the old private pointer plus the stripe_index.
+        */
+       BUG_ON((((uintptr_t)bi_private) & 3) != 0);
+       BUG_ON(stripe_index > 3);
+       return (void *)(((uintptr_t)bi_private) | stripe_index);
+}
+
+static struct btrfs_bio *extract_bbio_from_bio_private(void *bi_private)
+{
+       return (struct btrfs_bio *)(((uintptr_t)bi_private) & ~((uintptr_t)3));
+}
+
+static unsigned int extract_stripe_index_from_bio_private(void *bi_private)
+{
+       return (unsigned int)((uintptr_t)bi_private) & 3;
+}
+
 static void btrfs_end_bio(struct bio *bio, int err)
 {
-       struct btrfs_bio *bbio = bio->bi_private;
+       struct btrfs_bio *bbio = extract_bbio_from_bio_private(bio->bi_private);
        int is_orig_bio = 0;
 
-       if (err)
+       if (err) {
                atomic_inc(&bbio->error);
+               if (err == -EIO || err == -EREMOTEIO) {
+                       unsigned int stripe_index =
+                               extract_stripe_index_from_bio_private(
+                                       bio->bi_private);
+                       struct btrfs_device *dev;
+
+                       BUG_ON(stripe_index >= bbio->num_stripes);
+                       dev = bbio->stripes[stripe_index].dev;
+                       if (bio->bi_rw & WRITE)
+                               btrfs_dev_stat_inc(dev,
+                                                  BTRFS_DEV_STAT_WRITE_ERRS);
+                       else
+                               btrfs_dev_stat_inc(dev,
+                                                  BTRFS_DEV_STAT_READ_ERRS);
+                       if ((bio->bi_rw & WRITE_FLUSH) == WRITE_FLUSH)
+                               btrfs_dev_stat_inc(dev,
+                                                  BTRFS_DEV_STAT_FLUSH_ERRS);
+                       btrfs_dev_stat_print_on_error(dev);
+               }
+       }
 
        if (bio == bbio->orig_bio)
                is_orig_bio = 1;
@@ -4149,6 +4198,8 @@ int btrfs_map_bio(struct btrfs_root *root, int rw, struct bio *bio,
                        bio = first_bio;
                }
                bio->bi_private = bbio;
+               bio->bi_private = merge_stripe_index_into_bio_private(
+                               bio->bi_private, (unsigned int)dev_nr);
                bio->bi_end_io = btrfs_end_bio;
                bio->bi_sector = bbio->stripes[dev_nr].physical >> 9;
                dev = bbio->stripes[dev_nr].dev;
@@ -4509,6 +4560,28 @@ int btrfs_read_sys_array(struct btrfs_root *root)
        return ret;
 }
 
+struct btrfs_device *btrfs_find_device_for_logical(struct btrfs_root *root,
+                                                  u64 logical, int mirror_num)
+{
+       struct btrfs_mapping_tree *map_tree = &root->fs_info->mapping_tree;
+       int ret;
+       u64 map_length = 0;
+       struct btrfs_bio *bbio = NULL;
+       struct btrfs_device *device;
+
+       BUG_ON(mirror_num == 0);
+       ret = btrfs_map_block(map_tree, WRITE, logical, &map_length, &bbio,
+                             mirror_num);
+       if (ret) {
+               BUG_ON(bbio != NULL);
+               return NULL;
+       }
+       BUG_ON(mirror_num != bbio->mirror_num);
+       device = bbio->stripes[mirror_num - 1].dev;
+       kfree(bbio);
+       return device;
+}
+
 int btrfs_read_chunk_tree(struct btrfs_root *root)
 {
        struct btrfs_path *path;
@@ -4583,3 +4656,230 @@ error:
        btrfs_free_path(path);
        return ret;
 }
+
+static void __btrfs_reset_dev_stats(struct btrfs_device *dev)
+{
+       int i;
+
+       for (i = 0; i < BTRFS_DEV_STAT_VALUES_MAX; i++)
+               btrfs_dev_stat_reset(dev, i);
+}
+
+int btrfs_init_dev_stats(struct btrfs_fs_info *fs_info)
+{
+       struct btrfs_key key;
+       struct btrfs_key found_key;
+       struct btrfs_root *dev_root = fs_info->dev_root;
+       struct btrfs_fs_devices *fs_devices = fs_info->fs_devices;
+       struct extent_buffer *eb;
+       int slot;
+       int ret = 0;
+       struct btrfs_device *device;
+       struct btrfs_path *path = NULL;
+       int i;
+
+       path = btrfs_alloc_path();
+       if (!path) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       mutex_lock(&fs_devices->device_list_mutex);
+       list_for_each_entry(device, &fs_devices->devices, dev_list) {
+               int item_size;
+               struct btrfs_dev_stats_item *ptr;
+
+               key.objectid = 0;
+               key.type = BTRFS_DEV_STATS_KEY;
+               key.offset = device->devid;
+               ret = btrfs_search_slot(NULL, dev_root, &key, path, 0, 0);
+               if (ret) {
+                       printk(KERN_WARNING "btrfs: no dev_stats entry found for device %s (devid %llu) (OK on first mount after mkfs)\n",
+                              device->name, (unsigned long long)device->devid);
+                       __btrfs_reset_dev_stats(device);
+                       device->dev_stats_valid = 1;
+                       btrfs_release_path(path);
+                       continue;
+               }
+               slot = path->slots[0];
+               eb = path->nodes[0];
+               btrfs_item_key_to_cpu(eb, &found_key, slot);
+               item_size = btrfs_item_size_nr(eb, slot);
+
+               ptr = btrfs_item_ptr(eb, slot,
+                                    struct btrfs_dev_stats_item);
+
+               for (i = 0; i < BTRFS_DEV_STAT_VALUES_MAX; i++) {
+                       if (item_size >= (1 + i) * sizeof(__le64))
+                               btrfs_dev_stat_set(device, i,
+                                       btrfs_dev_stats_value(eb, ptr, i));
+                       else
+                               btrfs_dev_stat_reset(device, i);
+               }
+
+               device->dev_stats_valid = 1;
+               btrfs_dev_stat_print_on_load(device);
+               btrfs_release_path(path);
+       }
+       mutex_unlock(&fs_devices->device_list_mutex);
+
+out:
+       btrfs_free_path(path);
+       return ret < 0 ? ret : 0;
+}
+
+static int update_dev_stat_item(struct btrfs_trans_handle *trans,
+                               struct btrfs_root *dev_root,
+                               struct btrfs_device *device)
+{
+       struct btrfs_path *path;
+       struct btrfs_key key;
+       struct extent_buffer *eb;
+       struct btrfs_dev_stats_item *ptr;
+       int ret;
+       int i;
+
+       key.objectid = 0;
+       key.type = BTRFS_DEV_STATS_KEY;
+       key.offset = device->devid;
+
+       path = btrfs_alloc_path();
+       BUG_ON(!path);
+       ret = btrfs_search_slot(trans, dev_root, &key, path, -1, 1);
+       if (ret < 0) {
+               printk(KERN_WARNING "btrfs: error %d while searching for dev_stats item for device %s!\n",
+                      ret, device->name);
+               goto out;
+       }
+
+       if (ret == 0 &&
+           btrfs_item_size_nr(path->nodes[0], path->slots[0]) < sizeof(*ptr)) {
+               /* need to delete old one and insert a new one */
+               ret = btrfs_del_item(trans, dev_root, path);
+               if (ret != 0) {
+                       printk(KERN_WARNING "btrfs: delete too small dev_stats item for device %s failed %d!\n",
+                              device->name, ret);
+                       goto out;
+               }
+               ret = 1;
+       }
+
+       if (ret == 1) {
+               /* need to insert a new item */
+               btrfs_release_path(path);
+               ret = btrfs_insert_empty_item(trans, dev_root, path,
+                                             &key, sizeof(*ptr));
+               if (ret < 0) {
+                       printk(KERN_WARNING "btrfs: insert dev_stats item for device %s failed %d!\n",
+                              device->name, ret);
+                       goto out;
+               }
+       }
+
+       eb = path->nodes[0];
+       ptr = btrfs_item_ptr(eb, path->slots[0], struct btrfs_dev_stats_item);
+       for (i = 0; i < BTRFS_DEV_STAT_VALUES_MAX; i++)
+               btrfs_set_dev_stats_value(eb, ptr, i,
+                                         btrfs_dev_stat_read(device, i));
+       btrfs_mark_buffer_dirty(eb);
+
+out:
+       btrfs_free_path(path);
+       return ret;
+}
+
+/*
+ * called from commit_transaction. Writes all changed device stats to disk.
+ */
+int btrfs_run_dev_stats(struct btrfs_trans_handle *trans,
+                       struct btrfs_fs_info *fs_info)
+{
+       struct btrfs_root *dev_root = fs_info->dev_root;
+       struct btrfs_fs_devices *fs_devices = fs_info->fs_devices;
+       struct btrfs_device *device;
+       int ret = 0;
+
+       mutex_lock(&fs_devices->device_list_mutex);
+       list_for_each_entry(device, &fs_devices->devices, dev_list) {
+               if (!device->dev_stats_valid || !device->dev_stats_dirty)
+                       continue;
+
+               ret = update_dev_stat_item(trans, dev_root, device);
+               if (!ret)
+                       device->dev_stats_dirty = 0;
+       }
+       mutex_unlock(&fs_devices->device_list_mutex);
+
+       return ret;
+}
+
+void btrfs_dev_stat_inc_and_print(struct btrfs_device *dev, int index)
+{
+       btrfs_dev_stat_inc(dev, index);
+       btrfs_dev_stat_print_on_error(dev);
+}
+
+void btrfs_dev_stat_print_on_error(struct btrfs_device *dev)
+{
+       if (!dev->dev_stats_valid)
+               return;
+       printk_ratelimited(KERN_ERR
+                          "btrfs: bdev %s errs: wr %u, rd %u, flush %u, corrupt %u, gen %u\n",
+                          dev->name,
+                          btrfs_dev_stat_read(dev, BTRFS_DEV_STAT_WRITE_ERRS),
+                          btrfs_dev_stat_read(dev, BTRFS_DEV_STAT_READ_ERRS),
+                          btrfs_dev_stat_read(dev, BTRFS_DEV_STAT_FLUSH_ERRS),
+                          btrfs_dev_stat_read(dev,
+                                              BTRFS_DEV_STAT_CORRUPTION_ERRS),
+                          btrfs_dev_stat_read(dev,
+                                              BTRFS_DEV_STAT_GENERATION_ERRS));
+}
+
+static void btrfs_dev_stat_print_on_load(struct btrfs_device *dev)
+{
+       printk(KERN_INFO "btrfs: bdev %s errs: wr %u, rd %u, flush %u, corrupt %u, gen %u\n",
+              dev->name,
+              btrfs_dev_stat_read(dev, BTRFS_DEV_STAT_WRITE_ERRS),
+              btrfs_dev_stat_read(dev, BTRFS_DEV_STAT_READ_ERRS),
+              btrfs_dev_stat_read(dev, BTRFS_DEV_STAT_FLUSH_ERRS),
+              btrfs_dev_stat_read(dev, BTRFS_DEV_STAT_CORRUPTION_ERRS),
+              btrfs_dev_stat_read(dev, BTRFS_DEV_STAT_GENERATION_ERRS));
+}
+
+int btrfs_get_dev_stats(struct btrfs_root *root,
+                       struct btrfs_ioctl_get_dev_stats *stats,
+                       int reset_after_read)
+{
+       struct btrfs_device *dev;
+       struct btrfs_fs_devices *fs_devices = root->fs_info->fs_devices;
+       int i;
+
+       mutex_lock(&fs_devices->device_list_mutex);
+       dev = btrfs_find_device(root, stats->devid, NULL, NULL);
+       mutex_unlock(&fs_devices->device_list_mutex);
+
+       if (!dev) {
+               printk(KERN_WARNING
+                      "btrfs: get dev_stats failed, device not found\n");
+               return -ENODEV;
+       } else if (!dev->dev_stats_valid) {
+               printk(KERN_WARNING
+                      "btrfs: get dev_stats failed, not yet valid\n");
+               return -ENODEV;
+       } else if (reset_after_read) {
+               for (i = 0; i < BTRFS_DEV_STAT_VALUES_MAX; i++) {
+                       if (stats->nr_items > i)
+                               stats->values[i] =
+                                       btrfs_dev_stat_read_and_reset(dev, i);
+                       else
+                               btrfs_dev_stat_reset(dev, i);
+               }
+       } else {
+               for (i = 0; i < BTRFS_DEV_STAT_VALUES_MAX; i++)
+                       if (stats->nr_items > i)
+                               stats->values[i] = btrfs_dev_stat_read(dev, i);
+       }
+       if (stats->nr_items > BTRFS_DEV_STAT_VALUES_MAX)
+               stats->nr_items = BTRFS_DEV_STAT_VALUES_MAX;
+       return 0;
+}
index bb6b03f97aaa089793d667fae93335373773a7eb..3406a88ca83e023429b8af19f2d6aa64d4cac6f8 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/bio.h>
 #include <linux/sort.h>
 #include "async-thread.h"
+#include "ioctl.h"
 
 #define BTRFS_STRIPE_LEN       (64 * 1024)
 
@@ -106,6 +107,11 @@ struct btrfs_device {
        struct completion flush_wait;
        int nobarriers;
 
+       /* disk I/O failure stats. For detailed description refer to
+        * enum btrfs_dev_stat_values in ioctl.h */
+       int dev_stats_valid;
+       int dev_stats_dirty; /* counters need to be written to disk */
+       atomic_t dev_stat_values[BTRFS_DEV_STAT_VALUES_MAX];
 };
 
 struct btrfs_fs_devices {
@@ -281,4 +287,50 @@ int btrfs_cancel_balance(struct btrfs_fs_info *fs_info);
 int btrfs_chunk_readonly(struct btrfs_root *root, u64 chunk_offset);
 int find_free_dev_extent(struct btrfs_device *device, u64 num_bytes,
                         u64 *start, u64 *max_avail);
+struct btrfs_device *btrfs_find_device_for_logical(struct btrfs_root *root,
+                                                  u64 logical, int mirror_num);
+void btrfs_dev_stat_print_on_error(struct btrfs_device *device);
+void btrfs_dev_stat_inc_and_print(struct btrfs_device *dev, int index);
+int btrfs_get_dev_stats(struct btrfs_root *root,
+                       struct btrfs_ioctl_get_dev_stats *stats,
+                       int reset_after_read);
+int btrfs_init_dev_stats(struct btrfs_fs_info *fs_info);
+int btrfs_run_dev_stats(struct btrfs_trans_handle *trans,
+                       struct btrfs_fs_info *fs_info);
+
+static inline void btrfs_dev_stat_inc(struct btrfs_device *dev,
+                                     int index)
+{
+       atomic_inc(dev->dev_stat_values + index);
+       dev->dev_stats_dirty = 1;
+}
+
+static inline int btrfs_dev_stat_read(struct btrfs_device *dev,
+                                     int index)
+{
+       return atomic_read(dev->dev_stat_values + index);
+}
+
+static inline int btrfs_dev_stat_read_and_reset(struct btrfs_device *dev,
+                                               int index)
+{
+       int ret;
+
+       ret = atomic_xchg(dev->dev_stat_values + index, 0);
+       dev->dev_stats_dirty = 1;
+       return ret;
+}
+
+static inline void btrfs_dev_stat_set(struct btrfs_device *dev,
+                                     int index, unsigned long val)
+{
+       atomic_set(dev->dev_stat_values + index, val);
+       dev->dev_stats_dirty = 1;
+}
+
+static inline void btrfs_dev_stat_reset(struct btrfs_device *dev,
+                                       int index)
+{
+       btrfs_dev_stat_set(dev, index, 0);
+}
 #endif
index e7a5659087e66f93769bc750562d21294c9bd2b6..3f4e2d69e83a13cb66f3f3a56024f53f5299f5c4 100644 (file)
@@ -196,6 +196,7 @@ int __btrfs_setxattr(struct btrfs_trans_handle *trans,
        if (ret)
                goto out;
 
+       inode_inc_iversion(inode);
        inode->i_ctime = CURRENT_TIME;
        ret = btrfs_update_inode(trans, root, inode);
        BUG_ON(ret);
index ad5938ca357c270ace08388401176f22a6343571..838a9cf246bd0fa561ab66295f9bb3df77e0c6a2 100644 (file)
@@ -3152,7 +3152,7 @@ SYSCALL_DEFINE2(bdflush, int, func, long, data)
 /*
  * Buffer-head allocation
  */
-static struct kmem_cache *bh_cachep;
+static struct kmem_cache *bh_cachep __read_mostly;
 
 /*
  * Once the number of bh's in the machine exceeds this level, we start
index fbb2a643ef10a1f75c4918f165c9e3a22a603a86..8e1b60e557b65bea0df86a881376456658a9cffd 100644 (file)
@@ -40,38 +40,49 @@ struct ceph_nfs_confh {
        u32 parent_name_hash;
 } __attribute__ ((packed));
 
-static int ceph_encode_fh(struct dentry *dentry, u32 *rawfh, int *max_len,
-                         int connectable)
+/*
+ * The presence of @parent_inode here tells us whether NFS wants a
+ * connectable file handle.  However, we want to make a connectionable
+ * file handle unconditionally so that the MDS gets as much of a hint
+ * as possible.  That means we only use @parent_dentry to indicate
+ * whether nfsd wants a connectable fh, and whether we should indicate
+ * failure from a too-small @max_len.
+ */
+static int ceph_encode_fh(struct inode *inode, u32 *rawfh, int *max_len,
+                         struct inode *parent_inode)
 {
        int type;
        struct ceph_nfs_fh *fh = (void *)rawfh;
        struct ceph_nfs_confh *cfh = (void *)rawfh;
-       struct dentry *parent;
-       struct inode *inode = dentry->d_inode;
        int connected_handle_length = sizeof(*cfh)/4;
        int handle_length = sizeof(*fh)/4;
+       struct dentry *dentry = d_find_alias(inode);
+       struct dentry *parent;
 
        /* don't re-export snaps */
        if (ceph_snap(inode) != CEPH_NOSNAP)
                return -EINVAL;
 
-       spin_lock(&dentry->d_lock);
-       parent = dentry->d_parent;
-       if (*max_len >= connected_handle_length) {
+       /* if we found an alias, generate a connectable fh */
+       if (*max_len >= connected_handle_length && dentry) {
                dout("encode_fh %p connectable\n", dentry);
-               cfh->ino = ceph_ino(dentry->d_inode);
+               spin_lock(&dentry->d_lock);
+               parent = dentry->d_parent;
+               cfh->ino = ceph_ino(inode);
                cfh->parent_ino = ceph_ino(parent->d_inode);
                cfh->parent_name_hash = ceph_dentry_hash(parent->d_inode,
                                                         dentry);
                *max_len = connected_handle_length;
                type = 2;
+               spin_unlock(&dentry->d_lock);
        } else if (*max_len >= handle_length) {
-               if (connectable) {
+               if (parent_inode) {
+                       /* nfsd wants connectable */
                        *max_len = connected_handle_length;
                        type = 255;
                } else {
                        dout("encode_fh %p\n", dentry);
-                       fh->ino = ceph_ino(dentry->d_inode);
+                       fh->ino = ceph_ino(inode);
                        *max_len = handle_length;
                        type = 1;
                }
@@ -79,7 +90,6 @@ static int ceph_encode_fh(struct dentry *dentry, u32 *rawfh, int *max_len,
                *max_len = handle_length;
                type = 255;
        }
-       spin_unlock(&dentry->d_lock);
        return type;
 }
 
index ed72428d9c75c80a6744ccd6a996b83c1a20d333..988d4f302e4880281a2b5e04c9f44dd7870202d2 100644 (file)
@@ -54,7 +54,6 @@ prepare_open_request(struct super_block *sb, int flags, int create_mode)
        req->r_fmode = ceph_flags_to_mode(flags);
        req->r_args.open.flags = cpu_to_le32(flags);
        req->r_args.open.mode = cpu_to_le32(create_mode);
-       req->r_args.open.preferred = cpu_to_le32(-1);
 out:
        return req;
 }
index 790914a598dd5d68b8f40b851c2faff2e790e4af..8e3fb69fbe62e60cd3698c07d9fa8e53d6d2b184 100644 (file)
@@ -26,8 +26,7 @@ static long ceph_ioctl_get_layout(struct file *file, void __user *arg)
                l.stripe_count = ceph_file_layout_stripe_count(ci->i_layout);
                l.object_size = ceph_file_layout_object_size(ci->i_layout);
                l.data_pool = le32_to_cpu(ci->i_layout.fl_pg_pool);
-               l.preferred_osd =
-                       (s32)le32_to_cpu(ci->i_layout.fl_pg_preferred);
+               l.preferred_osd = (s32)-1;
                if (copy_to_user(arg, &l, sizeof(l)))
                        return -EFAULT;
        }
@@ -35,6 +34,32 @@ static long ceph_ioctl_get_layout(struct file *file, void __user *arg)
        return err;
 }
 
+static long __validate_layout(struct ceph_mds_client *mdsc,
+                             struct ceph_ioctl_layout *l)
+{
+       int i, err;
+
+       /* validate striping parameters */
+       if ((l->object_size & ~PAGE_MASK) ||
+           (l->stripe_unit & ~PAGE_MASK) ||
+           ((unsigned)l->object_size % (unsigned)l->stripe_unit))
+               return -EINVAL;
+
+       /* make sure it's a valid data pool */
+       mutex_lock(&mdsc->mutex);
+       err = -EINVAL;
+       for (i = 0; i < mdsc->mdsmap->m_num_data_pg_pools; i++)
+               if (mdsc->mdsmap->m_data_pg_pools[i] == l->data_pool) {
+                       err = 0;
+                       break;
+               }
+       mutex_unlock(&mdsc->mutex);
+       if (err)
+               return err;
+
+       return 0;
+}
+
 static long ceph_ioctl_set_layout(struct file *file, void __user *arg)
 {
        struct inode *inode = file->f_dentry->d_inode;
@@ -44,52 +69,40 @@ static long ceph_ioctl_set_layout(struct file *file, void __user *arg)
        struct ceph_ioctl_layout l;
        struct ceph_inode_info *ci = ceph_inode(file->f_dentry->d_inode);
        struct ceph_ioctl_layout nl;
-       int err, i;
+       int err;
 
        if (copy_from_user(&l, arg, sizeof(l)))
                return -EFAULT;
 
        /* validate changed params against current layout */
        err = ceph_do_getattr(file->f_dentry->d_inode, CEPH_STAT_CAP_LAYOUT);
-       if (!err) {
-               nl.stripe_unit = ceph_file_layout_su(ci->i_layout);
-               nl.stripe_count = ceph_file_layout_stripe_count(ci->i_layout);
-               nl.object_size = ceph_file_layout_object_size(ci->i_layout);
-               nl.data_pool = le32_to_cpu(ci->i_layout.fl_pg_pool);
-               nl.preferred_osd =
-                               (s32)le32_to_cpu(ci->i_layout.fl_pg_preferred);
-       } else
+       if (err)
                return err;
 
+       memset(&nl, 0, sizeof(nl));
        if (l.stripe_count)
                nl.stripe_count = l.stripe_count;
+       else
+               nl.stripe_count = ceph_file_layout_stripe_count(ci->i_layout);
        if (l.stripe_unit)
                nl.stripe_unit = l.stripe_unit;
+       else
+               nl.stripe_unit = ceph_file_layout_su(ci->i_layout);
        if (l.object_size)
                nl.object_size = l.object_size;
+       else
+               nl.object_size = ceph_file_layout_object_size(ci->i_layout);
        if (l.data_pool)
                nl.data_pool = l.data_pool;
-       if (l.preferred_osd)
-               nl.preferred_osd = l.preferred_osd;
+       else
+               nl.data_pool = ceph_file_layout_pg_pool(ci->i_layout);
 
-       if ((nl.object_size & ~PAGE_MASK) ||
-           (nl.stripe_unit & ~PAGE_MASK) ||
-           ((unsigned)nl.object_size % (unsigned)nl.stripe_unit))
-               return -EINVAL;
+       /* this is obsolete, and always -1 */
+       nl.preferred_osd = le64_to_cpu(-1);
 
-       /* make sure it's a valid data pool */
-       if (l.data_pool > 0) {
-               mutex_lock(&mdsc->mutex);
-               err = -EINVAL;
-               for (i = 0; i < mdsc->mdsmap->m_num_data_pg_pools; i++)
-                       if (mdsc->mdsmap->m_data_pg_pools[i] == l.data_pool) {
-                               err = 0;
-                               break;
-                       }
-               mutex_unlock(&mdsc->mutex);
-               if (err)
-                       return err;
-       }
+       err = __validate_layout(mdsc, &nl);
+       if (err)
+               return err;
 
        req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_SETLAYOUT,
                                       USE_AUTH_MDS);
@@ -106,8 +119,6 @@ static long ceph_ioctl_set_layout(struct file *file, void __user *arg)
        req->r_args.setlayout.layout.fl_object_size =
                cpu_to_le32(l.object_size);
        req->r_args.setlayout.layout.fl_pg_pool = cpu_to_le32(l.data_pool);
-       req->r_args.setlayout.layout.fl_pg_preferred =
-               cpu_to_le32(l.preferred_osd);
 
        parent_inode = ceph_get_dentry_parent_inode(file->f_dentry);
        err = ceph_mdsc_do_request(mdsc, parent_inode, req);
@@ -127,33 +138,16 @@ static long ceph_ioctl_set_layout_policy (struct file *file, void __user *arg)
        struct inode *inode = file->f_dentry->d_inode;
        struct ceph_mds_request *req;
        struct ceph_ioctl_layout l;
-       int err, i;
+       int err;
        struct ceph_mds_client *mdsc = ceph_sb_to_client(inode->i_sb)->mdsc;
 
        /* copy and validate */
        if (copy_from_user(&l, arg, sizeof(l)))
                return -EFAULT;
 
-       if ((l.object_size & ~PAGE_MASK) ||
-           (l.stripe_unit & ~PAGE_MASK) ||
-           !l.stripe_unit ||
-           (l.object_size &&
-               (unsigned)l.object_size % (unsigned)l.stripe_unit))
-               return -EINVAL;
-
-       /* make sure it's a valid data pool */
-       if (l.data_pool > 0) {
-               mutex_lock(&mdsc->mutex);
-               err = -EINVAL;
-               for (i = 0; i < mdsc->mdsmap->m_num_data_pg_pools; i++)
-                       if (mdsc->mdsmap->m_data_pg_pools[i] == l.data_pool) {
-                               err = 0;
-                               break;
-                       }
-               mutex_unlock(&mdsc->mutex);
-               if (err)
-                       return err;
-       }
+       err = __validate_layout(mdsc, &l);
+       if (err)
+               return err;
 
        req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_SETDIRLAYOUT,
                                       USE_AUTH_MDS);
@@ -171,8 +165,6 @@ static long ceph_ioctl_set_layout_policy (struct file *file, void __user *arg)
                        cpu_to_le32(l.object_size);
        req->r_args.setlayout.layout.fl_pg_pool =
                        cpu_to_le32(l.data_pool);
-       req->r_args.setlayout.layout.fl_pg_preferred =
-                       cpu_to_le32(l.preferred_osd);
 
        err = ceph_mdsc_do_request(mdsc, inode, req);
        ceph_mdsc_put_request(req);
index be4a604873331dc7c547950650a899b514f56d26..c77028afb1e1e6b52315a73d013a26fb5c9c7f5b 100644 (file)
@@ -34,6 +34,8 @@
 struct ceph_ioctl_layout {
        __u64 stripe_unit, stripe_count, object_size;
        __u64 data_pool;
+
+       /* obsolete.  new values ignored, always return -1 */
        __s64 preferred_osd;
 };
 
index 89971e137aab80454fed8a51a105d7df903b3101..200bc87eceb1cc417a1caa1a73e264cde79dda78 100644 (file)
@@ -334,10 +334,10 @@ void ceph_put_mds_session(struct ceph_mds_session *s)
        dout("mdsc put_session %p %d -> %d\n", s,
             atomic_read(&s->s_ref), atomic_read(&s->s_ref)-1);
        if (atomic_dec_and_test(&s->s_ref)) {
-               if (s->s_authorizer)
+               if (s->s_auth.authorizer)
                     s->s_mdsc->fsc->client->monc.auth->ops->destroy_authorizer(
                             s->s_mdsc->fsc->client->monc.auth,
-                            s->s_authorizer);
+                            s->s_auth.authorizer);
                kfree(s);
        }
 }
@@ -3395,39 +3395,33 @@ out:
 /*
  * authentication
  */
-static int get_authorizer(struct ceph_connection *con,
-                         void **buf, int *len, int *proto,
-                         void **reply_buf, int *reply_len, int force_new)
+
+/*
+ * Note: returned pointer is the address of a structure that's
+ * managed separately.  Caller must *not* attempt to free it.
+ */
+static struct ceph_auth_handshake *get_authorizer(struct ceph_connection *con,
+                                       int *proto, int force_new)
 {
        struct ceph_mds_session *s = con->private;
        struct ceph_mds_client *mdsc = s->s_mdsc;
        struct ceph_auth_client *ac = mdsc->fsc->client->monc.auth;
-       int ret = 0;
-
-       if (force_new && s->s_authorizer) {
-               ac->ops->destroy_authorizer(ac, s->s_authorizer);
-               s->s_authorizer = NULL;
-       }
-       if (s->s_authorizer == NULL) {
-               if (ac->ops->create_authorizer) {
-                       ret = ac->ops->create_authorizer(
-                               ac, CEPH_ENTITY_TYPE_MDS,
-                               &s->s_authorizer,
-                               &s->s_authorizer_buf,
-                               &s->s_authorizer_buf_len,
-                               &s->s_authorizer_reply_buf,
-                               &s->s_authorizer_reply_buf_len);
-                       if (ret)
-                               return ret;
-               }
-       }
+       struct ceph_auth_handshake *auth = &s->s_auth;
 
+       if (force_new && auth->authorizer) {
+               if (ac->ops && ac->ops->destroy_authorizer)
+                       ac->ops->destroy_authorizer(ac, auth->authorizer);
+               auth->authorizer = NULL;
+       }
+       if (!auth->authorizer && ac->ops && ac->ops->create_authorizer) {
+               int ret = ac->ops->create_authorizer(ac, CEPH_ENTITY_TYPE_MDS,
+                                                       auth);
+               if (ret)
+                       return ERR_PTR(ret);
+       }
        *proto = ac->protocol;
-       *buf = s->s_authorizer_buf;
-       *len = s->s_authorizer_buf_len;
-       *reply_buf = s->s_authorizer_reply_buf;
-       *reply_len = s->s_authorizer_reply_buf_len;
-       return 0;
+
+       return auth;
 }
 
 
@@ -3437,7 +3431,7 @@ static int verify_authorizer_reply(struct ceph_connection *con, int len)
        struct ceph_mds_client *mdsc = s->s_mdsc;
        struct ceph_auth_client *ac = mdsc->fsc->client->monc.auth;
 
-       return ac->ops->verify_authorizer_reply(ac, s->s_authorizer, len);
+       return ac->ops->verify_authorizer_reply(ac, s->s_auth.authorizer, len);
 }
 
 static int invalidate_authorizer(struct ceph_connection *con)
index 8c7c04ebb595a1a8bd2e9c1b177890f8ba234b9a..dd26846dd71de4267146b7deb43c7a5d7b9b976f 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/ceph/types.h>
 #include <linux/ceph/messenger.h>
 #include <linux/ceph/mdsmap.h>
+#include <linux/ceph/auth.h>
 
 /*
  * Some lock dependencies:
@@ -113,9 +114,7 @@ struct ceph_mds_session {
 
        struct ceph_connection s_con;
 
-       struct ceph_authorizer *s_authorizer;
-       void             *s_authorizer_buf, *s_authorizer_reply_buf;
-       size_t            s_authorizer_buf_len, s_authorizer_reply_buf_len;
+       struct ceph_auth_handshake s_auth;
 
        /* protected by s_gen_ttl_lock */
        spinlock_t        s_gen_ttl_lock;
index f04c0961f9937eb6f553978f10942800818528ba..e5206fc765620f3b550c2457e1f271cb9d81c8c8 100644 (file)
@@ -331,7 +331,7 @@ static int build_snap_context(struct ceph_snap_realm *realm)
 
        /* alloc new snap context */
        err = -ENOMEM;
-       if (num > (ULONG_MAX - sizeof(*snapc)) / sizeof(u64))
+       if (num > (SIZE_MAX - sizeof(*snapc)) / sizeof(u64))
                goto fail;
        snapc = kzalloc(sizeof(*snapc) + num*sizeof(u64), GFP_NOFS);
        if (!snapc)
index 35b86331d8a5ce84c311e9eb2730757f80149179..785cb3057c95a436c344da2d76ee24b86cb7954b 100644 (file)
@@ -118,15 +118,6 @@ static size_t ceph_vxattrcb_file_layout(struct ceph_inode_info *ci, char *val,
                (unsigned long long)ceph_file_layout_su(ci->i_layout),
                (unsigned long long)ceph_file_layout_stripe_count(ci->i_layout),
                (unsigned long long)ceph_file_layout_object_size(ci->i_layout));
-
-       if (ceph_file_layout_pg_preferred(ci->i_layout) >= 0) {
-               val += ret;
-               size -= ret;
-               ret += snprintf(val, size, "preferred_osd=%lld\n",
-                           (unsigned long long)ceph_file_layout_pg_preferred(
-                                   ci->i_layout));
-       }
-
        return ret;
 }
 
index 2b243af70aa325b3c6049ded6121473f98cfbe8e..a08306a8bec911ac72dfc76bf6fbbf79760d8db1 100644 (file)
@@ -158,3 +158,23 @@ config CIFS_NFSD_EXPORT
          depends on CIFS && EXPERIMENTAL && BROKEN
          help
           Allows NFS server to export a CIFS mounted share (nfsd over cifs)
+
+config CIFS_SMB2
+       bool "SMB2 network file system support (EXPERIMENTAL)"
+       depends on EXPERIMENTAL && INET && BROKEN
+       select NLS
+       select KEYS
+       select FSCACHE
+       select DNS_RESOLVER
+
+       help
+         This enables experimental support for the SMB2 (Server Message Block
+         version 2) protocol. The SMB2 protocol is the successor to the
+         popular CIFS and SMB network file sharing protocols. SMB2 is the
+         native file sharing mechanism for recent versions of Windows
+         operating systems (since Vista).  SMB2 enablement will eventually
+         allow users better performance, security and features, than would be
+         possible with cifs. Note that smb2 mount options also are simpler
+         (compared to cifs) due to protocol improvements.
+
+         Unless you are a developer or tester, say N.
index 005d524c3a4ae0c390c6f9b001249423f1dd3b09..4b4127544349290d4c86ded91eb377a9b87c33e1 100644 (file)
@@ -6,7 +6,7 @@ obj-$(CONFIG_CIFS) += cifs.o
 cifs-y := cifsfs.o cifssmb.o cifs_debug.o connect.o dir.o file.o inode.o \
          link.o misc.o netmisc.o smbencrypt.o transport.o asn1.o \
          cifs_unicode.o nterr.o xattr.o cifsencrypt.o \
-         readdir.o ioctl.o sess.o export.o
+         readdir.o ioctl.o sess.o export.o smb1ops.o
 
 cifs-$(CONFIG_CIFS_ACL) += cifsacl.o
 
@@ -15,3 +15,5 @@ cifs-$(CONFIG_CIFS_UPCALL) += cifs_spnego.o
 cifs-$(CONFIG_CIFS_DFS_UPCALL) += dns_resolve.o cifs_dfs_ref.o
 
 cifs-$(CONFIG_CIFS_FSCACHE) += fscache.o cache.o
+
+cifs-$(CONFIG_CIFS_SMB2) += smb2ops.o
index b7d782bab79731694adc8d55489d8e258aace167..22ab7b5b8da7eda6bf38d19aff3415eade104c5c 100644 (file)
@@ -608,11 +608,6 @@ Stats                      Lists summary resource usage information as well as per
                        in the kernel configuration.
 
 Configuration pseudo-files:
-MultiuserMount         If set to one, more than one CIFS session to 
-                       the same server ip address can be established
-                       if more than one uid accesses the same mount
-                       point and if the uids user/password mapping
-                       information is available. (default is 0)
 PacketSigningEnabled   If set to one, cifs packet signing is enabled
                        and will be used if the server requires 
                        it.  If set to two, cifs packet signing is
index 2704646294166bec7edf9c006c98d5229a1eaab9..e8140528ca5c426b2fe7b4b150592e70de19cdca 100644 (file)
@@ -57,19 +57,21 @@ cifs_dump_mem(char *label, void *data, int length)
        }
 }
 
-#ifdef CONFIG_CIFS_DEBUG2
 void cifs_dump_detail(void *buf)
 {
+#ifdef CONFIG_CIFS_DEBUG2
        struct smb_hdr *smb = (struct smb_hdr *)buf;
 
        cERROR(1, "Cmd: %d Err: 0x%x Flags: 0x%x Flgs2: 0x%x Mid: %d Pid: %d",
                  smb->Command, smb->Status.CifsError,
                  smb->Flags, smb->Flags2, smb->Mid, smb->Pid);
        cERROR(1, "smb buf %p len %d", smb, smbCalcSize(smb));
+#endif /* CONFIG_CIFS_DEBUG2 */
 }
 
 void cifs_dump_mids(struct TCP_Server_Info *server)
 {
+#ifdef CONFIG_CIFS_DEBUG2
        struct list_head *tmp;
        struct mid_q_entry *mid_entry;
 
@@ -102,8 +104,8 @@ void cifs_dump_mids(struct TCP_Server_Info *server)
                }
        }
        spin_unlock(&GlobalMid_Lock);
-}
 #endif /* CONFIG_CIFS_DEBUG2 */
+}
 
 #ifdef CONFIG_PROC_FS
 static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
@@ -420,7 +422,6 @@ static struct proc_dir_entry *proc_fs_cifs;
 static const struct file_operations cifsFYI_proc_fops;
 static const struct file_operations cifs_lookup_cache_proc_fops;
 static const struct file_operations traceSMB_proc_fops;
-static const struct file_operations cifs_multiuser_mount_proc_fops;
 static const struct file_operations cifs_security_flags_proc_fops;
 static const struct file_operations cifs_linux_ext_proc_fops;
 
@@ -440,8 +441,6 @@ cifs_proc_init(void)
        proc_create("traceSMB", 0, proc_fs_cifs, &traceSMB_proc_fops);
        proc_create("LinuxExtensionsEnabled", 0, proc_fs_cifs,
                    &cifs_linux_ext_proc_fops);
-       proc_create("MultiuserMount", 0, proc_fs_cifs,
-                   &cifs_multiuser_mount_proc_fops);
        proc_create("SecurityFlags", 0, proc_fs_cifs,
                    &cifs_security_flags_proc_fops);
        proc_create("LookupCacheEnabled", 0, proc_fs_cifs,
@@ -460,7 +459,6 @@ cifs_proc_clean(void)
 #ifdef CONFIG_CIFS_STATS
        remove_proc_entry("Stats", proc_fs_cifs);
 #endif
-       remove_proc_entry("MultiuserMount", proc_fs_cifs);
        remove_proc_entry("SecurityFlags", proc_fs_cifs);
        remove_proc_entry("LinuxExtensionsEnabled", proc_fs_cifs);
        remove_proc_entry("LookupCacheEnabled", proc_fs_cifs);
@@ -617,52 +615,6 @@ static const struct file_operations traceSMB_proc_fops = {
        .write          = traceSMB_proc_write,
 };
 
-static int cifs_multiuser_mount_proc_show(struct seq_file *m, void *v)
-{
-       seq_printf(m, "%d\n", multiuser_mount);
-       return 0;
-}
-
-static int cifs_multiuser_mount_proc_open(struct inode *inode, struct file *fh)
-{
-       return single_open(fh, cifs_multiuser_mount_proc_show, NULL);
-}
-
-static ssize_t cifs_multiuser_mount_proc_write(struct file *file,
-               const char __user *buffer, size_t count, loff_t *ppos)
-{
-       char c;
-       int rc;
-       static bool warned;
-
-       rc = get_user(c, buffer);
-       if (rc)
-               return rc;
-       if (c == '0' || c == 'n' || c == 'N')
-               multiuser_mount = 0;
-       else if (c == '1' || c == 'y' || c == 'Y') {
-               multiuser_mount = 1;
-               if (!warned) {
-                       warned = true;
-                       printk(KERN_WARNING "CIFS VFS: The legacy multiuser "
-                               "mount code is scheduled to be deprecated in "
-                               "3.5. Please switch to using the multiuser "
-                               "mount option.");
-               }
-       }
-
-       return count;
-}
-
-static const struct file_operations cifs_multiuser_mount_proc_fops = {
-       .owner          = THIS_MODULE,
-       .open           = cifs_multiuser_mount_proc_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-       .write          = cifs_multiuser_mount_proc_write,
-};
-
 static int cifs_security_flags_proc_show(struct seq_file *m, void *v)
 {
        seq_printf(m, "0x%x\n", global_secflags);
index 566e0ae8dc2cb64d8e10822da691caed0dc28a9d..c0c68bb492d7d98e43257bd4d433bf0676b06530 100644 (file)
 #define _H_CIFS_DEBUG
 
 void cifs_dump_mem(char *label, void *data, int length);
-#ifdef CONFIG_CIFS_DEBUG2
-#define DBG2 2
 void cifs_dump_detail(void *);
 void cifs_dump_mids(struct TCP_Server_Info *);
+#ifdef CONFIG_CIFS_DEBUG2
+#define DBG2 2
 #else
 #define DBG2 0
 #endif
index 0a0fa0250e99857481bb87d8c13d50a23fcba15d..8b6e344eb0ba3a483780f0b32cf0961ec258ca4c 100644 (file)
@@ -56,7 +56,6 @@ int traceSMB = 0;
 bool enable_oplocks = true;
 unsigned int linuxExtEnabled = 1;
 unsigned int lookupCacheEnabled = 1;
-unsigned int multiuser_mount = 0;
 unsigned int global_secflags = CIFSSEC_DEF;
 /* unsigned int ntlmv2_support = 0; */
 unsigned int sign_CIFS_PDUs = 1;
@@ -125,7 +124,7 @@ cifs_read_super(struct super_block *sb)
                goto out_no_root;
        }
 
-       /* do that *after* d_alloc_root() - we want NULL ->d_op for root here */
+       /* do that *after* d_make_root() - we want NULL ->d_op for root here */
        if (cifs_sb_master_tcon(cifs_sb)->nocase)
                sb->s_d_op = &cifs_ci_dentry_ops;
        else
@@ -329,6 +328,19 @@ cifs_show_security(struct seq_file *s, struct TCP_Server_Info *server)
                seq_printf(s, "i");
 }
 
+static void
+cifs_show_cache_flavor(struct seq_file *s, struct cifs_sb_info *cifs_sb)
+{
+       seq_printf(s, ",cache=");
+
+       if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_STRICT_IO)
+               seq_printf(s, "strict");
+       else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DIRECT_IO)
+               seq_printf(s, "none");
+       else
+               seq_printf(s, "loose");
+}
+
 /*
  * cifs_show_options() is for displaying mount options in /proc/mounts.
  * Not all settable options are displayed but most of the important
@@ -342,7 +354,9 @@ cifs_show_options(struct seq_file *s, struct dentry *root)
        struct sockaddr *srcaddr;
        srcaddr = (struct sockaddr *)&tcon->ses->server->srcaddr;
 
+       seq_printf(s, ",vers=%s", tcon->ses->server->vals->version_string);
        cifs_show_security(s, tcon->ses->server);
+       cifs_show_cache_flavor(s, cifs_sb);
 
        seq_printf(s, ",unc=%s", tcon->treeName);
 
@@ -408,8 +422,6 @@ cifs_show_options(struct seq_file *s, struct dentry *root)
                seq_printf(s, ",rwpidforward");
        if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL)
                seq_printf(s, ",forcemand");
-       if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DIRECT_IO)
-               seq_printf(s, ",directio");
        if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR)
                seq_printf(s, ",nouser_xattr");
        if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR)
@@ -432,8 +444,6 @@ cifs_show_options(struct seq_file *s, struct dentry *root)
                seq_printf(s, ",nostrictsync");
        if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM)
                seq_printf(s, ",noperm");
-       if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_STRICT_IO)
-               seq_printf(s, ",strictcache");
        if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_BACKUPUID)
                seq_printf(s, ",backupuid=%u", cifs_sb->mnt_backupuid);
        if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_BACKUPGID)
@@ -945,7 +955,6 @@ cifs_init_once(void *inode)
        struct cifsInodeInfo *cifsi = inode;
 
        inode_init_once(&cifsi->vfs_inode);
-       INIT_LIST_HEAD(&cifsi->llist);
        mutex_init(&cifsi->lock_mutex);
 }
 
index 4ff6313f0a9158958fbf0f63e4b21464b281c996..20350a93ed99105062743e1123e441af52b59a39 100644 (file)
@@ -43,6 +43,7 @@
 
 #define CIFS_MIN_RCV_POOL 4
 
+#define MAX_REOPEN_ATT 5 /* these many maximum attempts to reopen a file */
 /*
  * default attribute cache timeout (jiffies)
  */
@@ -150,6 +151,57 @@ struct cifs_cred {
  *****************************************************************
  */
 
+enum smb_version {
+       Smb_1 = 1,
+       Smb_21,
+};
+
+struct mid_q_entry;
+struct TCP_Server_Info;
+struct cifsFileInfo;
+struct cifs_ses;
+
+struct smb_version_operations {
+       int (*send_cancel)(struct TCP_Server_Info *, void *,
+                          struct mid_q_entry *);
+       bool (*compare_fids)(struct cifsFileInfo *, struct cifsFileInfo *);
+       /* setup request: allocate mid, sign message */
+       int (*setup_request)(struct cifs_ses *, struct kvec *, unsigned int,
+                            struct mid_q_entry **);
+       /* check response: verify signature, map error */
+       int (*check_receive)(struct mid_q_entry *, struct TCP_Server_Info *,
+                            bool);
+       void (*add_credits)(struct TCP_Server_Info *, const unsigned int);
+       void (*set_credits)(struct TCP_Server_Info *, const int);
+       int * (*get_credits_field)(struct TCP_Server_Info *);
+       /* data offset from read response message */
+       unsigned int (*read_data_offset)(char *);
+       /* data length from read response message */
+       unsigned int (*read_data_length)(char *);
+       /* map smb to linux error */
+       int (*map_error)(char *, bool);
+       /* find mid corresponding to the response message */
+       struct mid_q_entry * (*find_mid)(struct TCP_Server_Info *, char *);
+       void (*dump_detail)(void *);
+       /* verify the message */
+       int (*check_message)(char *, unsigned int);
+       bool (*is_oplock_break)(char *, struct TCP_Server_Info *);
+};
+
+struct smb_version_values {
+       char            *version_string;
+       __u32           large_lock_type;
+       __u32           exclusive_lock_type;
+       __u32           shared_lock_type;
+       __u32           unlock_lock_type;
+       size_t          header_size;
+       size_t          max_header_size;
+       size_t          read_rsp_size;
+};
+
+#define HEADER_SIZE(server) (server->vals->header_size)
+#define MAX_HEADER_SIZE(server) (server->vals->max_header_size)
+
 struct smb_vol {
        char *username;
        char *password;
@@ -205,6 +257,8 @@ struct smb_vol {
        bool sockopt_tcp_nodelay:1;
        unsigned short int port;
        unsigned long actimeo; /* attribute cache timeout (jiffies) */
+       struct smb_version_operations *ops;
+       struct smb_version_values *vals;
        char *prepath;
        struct sockaddr_storage srcaddr; /* allow binding to a local IP */
        struct nls_table *local_nls;
@@ -242,6 +296,8 @@ struct TCP_Server_Info {
        int srv_count; /* reference counter */
        /* 15 character server name + 0x20 16th byte indicating type = srv */
        char server_RFC1001_name[RFC1001_NAME_LEN_WITH_NULL];
+       struct smb_version_operations   *ops;
+       struct smb_version_values       *vals;
        enum statusEnum tcpStatus; /* what we think the status is */
        char *hostname; /* hostname portion of UNC string */
        struct socket *ssocket;
@@ -321,16 +377,6 @@ in_flight(struct TCP_Server_Info *server)
        return num;
 }
 
-static inline int*
-get_credits_field(struct TCP_Server_Info *server)
-{
-       /*
-        * This will change to switch statement when we reserve slots for echos
-        * and oplock breaks.
-        */
-       return &server->credits;
-}
-
 static inline bool
 has_credits(struct TCP_Server_Info *server, int *credits)
 {
@@ -341,16 +387,16 @@ has_credits(struct TCP_Server_Info *server, int *credits)
        return num > 0;
 }
 
-static inline size_t
-header_size(void)
+static inline void
+add_credits(struct TCP_Server_Info *server, const unsigned int add)
 {
-       return sizeof(struct smb_hdr);
+       server->ops->add_credits(server, add);
 }
 
-static inline size_t
-max_header_size(void)
+static inline void
+set_credits(struct TCP_Server_Info *server, const int val)
 {
-       return MAX_CIFS_HDR_SIZE;
+       server->ops->set_credits(server, val);
 }
 
 /*
@@ -547,8 +593,7 @@ struct cifsLockInfo {
        __u64 offset;
        __u64 length;
        __u32 pid;
-       __u8 type;
-       __u16 netfid;
+       __u32 type;
 };
 
 /*
@@ -573,6 +618,10 @@ struct cifs_search_info {
 struct cifsFileInfo {
        struct list_head tlist; /* pointer to next fid owned by tcon */
        struct list_head flist; /* next fid (file instance) for this inode */
+       struct list_head llist; /*
+                                * brlocks held by this fid, protected by
+                                * lock_mutex from cifsInodeInfo structure
+                                */
        unsigned int uid;       /* allows finding which FileInfo structure */
        __u32 pid;              /* process id who opened file */
        __u16 netfid;           /* file id from remote */
@@ -615,9 +664,12 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file);
  */
 
 struct cifsInodeInfo {
-       struct list_head llist;         /* brlocks for this inode */
        bool can_cache_brlcks;
-       struct mutex lock_mutex;        /* protect two fields above */
+       struct mutex lock_mutex;        /*
+                                        * protect the field above and llist
+                                        * from every cifsFileInfo structure
+                                        * from openFileList
+                                        */
        /* BB add in lists for dirty pages i.e. write caching info for oplock */
        struct list_head openFileList;
        __u32 cifsAttrs; /* e.g. DOS archive bit, sparse, compressed, system */
@@ -703,7 +755,6 @@ static inline void cifs_stats_bytes_read(struct cifs_tcon *tcon,
 
 #endif
 
-struct mid_q_entry;
 
 /*
  * This is the prototype for the mid receive function. This function is for
@@ -1042,12 +1093,7 @@ GLOBAL_EXTERN atomic_t smBufAllocCount;
 GLOBAL_EXTERN atomic_t midCount;
 
 /* Misc globals */
-GLOBAL_EXTERN unsigned int multiuser_mount; /* if enabled allows new sessions
-                               to be established on existing mount if we
-                               have the uid/password or Kerberos credential
-                               or equivalent for current user */
-/* enable or disable oplocks */
-GLOBAL_EXTERN bool enable_oplocks;
+GLOBAL_EXTERN bool enable_oplocks; /* enable or disable oplocks */
 GLOBAL_EXTERN unsigned int lookupCacheEnabled;
 GLOBAL_EXTERN unsigned int global_secflags;    /* if on, session setup sent
                                with more secure ntlmssp2 challenge/resp */
@@ -1074,4 +1120,11 @@ void cifs_oplock_break(struct work_struct *work);
 extern const struct slow_work_ops cifs_oplock_break_ops;
 extern struct workqueue_struct *cifsiod_wq;
 
+/* Operations for different SMB versions */
+#define SMB1_VERSION_STRING    "1.0"
+extern struct smb_version_operations smb1_operations;
+extern struct smb_version_values smb1_values;
+#define SMB21_VERSION_STRING   "2.1"
+extern struct smb_version_operations smb21_operations;
+extern struct smb_version_values smb21_values;
 #endif /* _CIFS_GLOB_H */
index 96192c1e380afb9475048f5d932e886fb4338d61..5ec21ecf7980e98a2d51ed9d2edf41c720952d4d 100644 (file)
@@ -78,6 +78,8 @@ extern int SendReceive(const unsigned int /* xid */ , struct cifs_ses *,
                        int * /* bytes returned */ , const int long_op);
 extern int SendReceiveNoRsp(const unsigned int xid, struct cifs_ses *ses,
                            char *in_buf, int flags);
+extern int cifs_setup_request(struct cifs_ses *, struct kvec *, unsigned int,
+                             struct mid_q_entry **);
 extern int cifs_check_receive(struct mid_q_entry *mid,
                        struct TCP_Server_Info *server, bool log_error);
 extern int SendReceive2(const unsigned int /* xid */ , struct cifs_ses *,
@@ -88,9 +90,6 @@ extern int SendReceiveBlockingLock(const unsigned int xid,
                        struct smb_hdr *in_buf ,
                        struct smb_hdr *out_buf,
                        int *bytes_returned);
-extern void cifs_add_credits(struct TCP_Server_Info *server,
-                            const unsigned int add);
-extern void cifs_set_credits(struct TCP_Server_Info *server, const int val);
 extern int checkSMB(char *buf, unsigned int length);
 extern bool is_valid_oplock_break(char *, struct TCP_Server_Info *);
 extern bool backup_cred(struct cifs_sb_info *);
@@ -192,11 +191,13 @@ extern int CIFSTCon(unsigned int xid, struct cifs_ses *ses,
 
 extern int CIFSFindFirst(const int xid, struct cifs_tcon *tcon,
                const char *searchName, const struct nls_table *nls_codepage,
-               __u16 *searchHandle, struct cifs_search_info *psrch_inf,
+               __u16 *searchHandle, __u16 search_flags,
+               struct cifs_search_info *psrch_inf,
                int map, const char dirsep);
 
 extern int CIFSFindNext(const int xid, struct cifs_tcon *tcon,
-               __u16 searchHandle, struct cifs_search_info *psrch_inf);
+               __u16 searchHandle, __u16 search_flags,
+               struct cifs_search_info *psrch_inf);
 
 extern int CIFSFindClose(const int, struct cifs_tcon *tcon,
                        const __u16 search_handle);
@@ -464,6 +465,9 @@ extern int SMBencrypt(unsigned char *passwd, const unsigned char *c8,
 
 /* asynchronous read support */
 struct cifs_readdata {
+       struct kref                     refcount;
+       struct list_head                list;
+       struct completion               done;
        struct cifsFileInfo             *cfile;
        struct address_space            *mapping;
        __u64                           offset;
@@ -472,12 +476,13 @@ struct cifs_readdata {
        int                             result;
        struct list_head                pages;
        struct work_struct              work;
+       int (*marshal_iov) (struct cifs_readdata *rdata,
+                           unsigned int remaining);
        unsigned int                    nr_iov;
        struct kvec                     iov[1];
 };
 
-struct cifs_readdata *cifs_readdata_alloc(unsigned int nr_pages);
-void cifs_readdata_free(struct cifs_readdata *rdata);
+void cifs_readdata_release(struct kref *refcount);
 int cifs_async_readv(struct cifs_readdata *rdata);
 
 /* asynchronous write support */
index da2f5446fa7ae3d3bbba6bb1b7a92cd1cc8743e4..b5ad716b2642138ebebdc18a975718f9a09855c8 100644 (file)
@@ -87,7 +87,6 @@ static struct {
 #endif /* CIFS_POSIX */
 
 /* Forward declarations */
-static void cifs_readv_complete(struct work_struct *work);
 
 /* Mark as invalid, all open files on tree connections since they
    were closed when session to server was lost */
@@ -461,7 +460,7 @@ CIFSSMBNegotiate(unsigned int xid, struct cifs_ses *ses)
                server->maxReq = min_t(unsigned int,
                                       le16_to_cpu(rsp->MaxMpxCount),
                                       cifs_max_pending);
-               cifs_set_credits(server, server->maxReq);
+               set_credits(server, server->maxReq);
                server->maxBuf = le16_to_cpu(rsp->MaxBufSize);
                server->max_vcs = le16_to_cpu(rsp->MaxNumberVcs);
                /* even though we do not use raw we might as well set this
@@ -569,7 +568,7 @@ CIFSSMBNegotiate(unsigned int xid, struct cifs_ses *ses)
           little endian */
        server->maxReq = min_t(unsigned int, le16_to_cpu(pSMBr->MaxMpxCount),
                               cifs_max_pending);
-       cifs_set_credits(server, server->maxReq);
+       set_credits(server, server->maxReq);
        /* probably no need to store and check maxvcs */
        server->maxBuf = le32_to_cpu(pSMBr->MaxBufferSize);
        server->max_rw = le32_to_cpu(pSMBr->MaxRawSize);
@@ -721,7 +720,7 @@ cifs_echo_callback(struct mid_q_entry *mid)
        struct TCP_Server_Info *server = mid->callback_data;
 
        DeleteMidQEntry(mid);
-       cifs_add_credits(server, 1);
+       add_credits(server, 1);
 }
 
 int
@@ -1385,28 +1384,6 @@ openRetry:
        return rc;
 }
 
-struct cifs_readdata *
-cifs_readdata_alloc(unsigned int nr_pages)
-{
-       struct cifs_readdata *rdata;
-
-       /* readdata + 1 kvec for each page */
-       rdata = kzalloc(sizeof(*rdata) +
-                       sizeof(struct kvec) * nr_pages, GFP_KERNEL);
-       if (rdata != NULL) {
-               INIT_WORK(&rdata->work, cifs_readv_complete);
-               INIT_LIST_HEAD(&rdata->pages);
-       }
-       return rdata;
-}
-
-void
-cifs_readdata_free(struct cifs_readdata *rdata)
-{
-       cifsFileInfo_put(rdata->cfile);
-       kfree(rdata);
-}
-
 /*
  * Discard any remaining data in the current SMB. To do this, we borrow the
  * current bigbuf.
@@ -1423,7 +1400,7 @@ cifs_readv_discard(struct TCP_Server_Info *server, struct mid_q_entry *mid)
 
                length = cifs_read_from_socket(server, server->bigbuf,
                                min_t(unsigned int, remaining,
-                                       CIFSMaxBufSize + max_header_size()));
+                                   CIFSMaxBufSize + MAX_HEADER_SIZE(server)));
                if (length < 0)
                        return length;
                server->total_read += length;
@@ -1434,38 +1411,14 @@ cifs_readv_discard(struct TCP_Server_Info *server, struct mid_q_entry *mid)
        return 0;
 }
 
-static inline size_t
-read_rsp_size(void)
-{
-       return sizeof(READ_RSP);
-}
-
-static inline unsigned int
-read_data_offset(char *buf)
-{
-       READ_RSP *rsp = (READ_RSP *)buf;
-       return le16_to_cpu(rsp->DataOffset);
-}
-
-static inline unsigned int
-read_data_length(char *buf)
-{
-       READ_RSP *rsp = (READ_RSP *)buf;
-       return (le16_to_cpu(rsp->DataLengthHigh) << 16) +
-              le16_to_cpu(rsp->DataLength);
-}
-
 static int
 cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
 {
        int length, len;
-       unsigned int data_offset, remaining, data_len;
+       unsigned int data_offset, data_len;
        struct cifs_readdata *rdata = mid->callback_data;
        char *buf = server->smallbuf;
        unsigned int buflen = get_rfc1002_length(buf) + 4;
-       u64 eof;
-       pgoff_t eof_index;
-       struct page *page, *tpage;
 
        cFYI(1, "%s: mid=%llu offset=%llu bytes=%u", __func__,
                mid->mid, rdata->offset, rdata->bytes);
@@ -1475,9 +1428,10 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
         * can if there's not enough data. At this point, we've read down to
         * the Mid.
         */
-       len = min_t(unsigned int, buflen, read_rsp_size()) - header_size() + 1;
+       len = min_t(unsigned int, buflen, server->vals->read_rsp_size) -
+                                                       HEADER_SIZE(server) + 1;
 
-       rdata->iov[0].iov_base = buf + header_size() - 1;
+       rdata->iov[0].iov_base = buf + HEADER_SIZE(server) - 1;
        rdata->iov[0].iov_len = len;
 
        length = cifs_readv_from_socket(server, rdata->iov, 1, len);
@@ -1486,7 +1440,7 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
        server->total_read += length;
 
        /* Was the SMB read successful? */
-       rdata->result = map_smb_to_linux_error(buf, false);
+       rdata->result = server->ops->map_error(buf, false);
        if (rdata->result != 0) {
                cFYI(1, "%s: server returned error %d", __func__,
                        rdata->result);
@@ -1494,14 +1448,15 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
        }
 
        /* Is there enough to get to the rest of the READ_RSP header? */
-       if (server->total_read < read_rsp_size()) {
+       if (server->total_read < server->vals->read_rsp_size) {
                cFYI(1, "%s: server returned short header. got=%u expected=%zu",
-                       __func__, server->total_read, read_rsp_size());
+                       __func__, server->total_read,
+                       server->vals->read_rsp_size);
                rdata->result = -EIO;
                return cifs_readv_discard(server, mid);
        }
 
-       data_offset = read_data_offset(buf) + 4;
+       data_offset = server->ops->read_data_offset(buf) + 4;
        if (data_offset < server->total_read) {
                /*
                 * win2k8 sometimes sends an offset of 0 when the read
@@ -1540,7 +1495,7 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
                rdata->iov[0].iov_base, rdata->iov[0].iov_len);
 
        /* how much data is in the response? */
-       data_len = read_data_length(buf);
+       data_len = server->ops->read_data_length(buf);
        if (data_offset + data_len > buflen) {
                /* data_len is corrupt -- discard frame */
                rdata->result = -EIO;
@@ -1548,64 +1503,8 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
        }
 
        /* marshal up the page array */
-       len = 0;
-       remaining = data_len;
-       rdata->nr_iov = 1;
-
-       /* determine the eof that the server (probably) has */
-       eof = CIFS_I(rdata->mapping->host)->server_eof;
-       eof_index = eof ? (eof - 1) >> PAGE_CACHE_SHIFT : 0;
-       cFYI(1, "eof=%llu eof_index=%lu", eof, eof_index);
-
-       list_for_each_entry_safe(page, tpage, &rdata->pages, lru) {
-               if (remaining >= PAGE_CACHE_SIZE) {
-                       /* enough data to fill the page */
-                       rdata->iov[rdata->nr_iov].iov_base = kmap(page);
-                       rdata->iov[rdata->nr_iov].iov_len = PAGE_CACHE_SIZE;
-                       cFYI(1, "%u: idx=%lu iov_base=%p iov_len=%zu",
-                               rdata->nr_iov, page->index,
-                               rdata->iov[rdata->nr_iov].iov_base,
-                               rdata->iov[rdata->nr_iov].iov_len);
-                       ++rdata->nr_iov;
-                       len += PAGE_CACHE_SIZE;
-                       remaining -= PAGE_CACHE_SIZE;
-               } else if (remaining > 0) {
-                       /* enough for partial page, fill and zero the rest */
-                       rdata->iov[rdata->nr_iov].iov_base = kmap(page);
-                       rdata->iov[rdata->nr_iov].iov_len = remaining;
-                       cFYI(1, "%u: idx=%lu iov_base=%p iov_len=%zu",
-                               rdata->nr_iov, page->index,
-                               rdata->iov[rdata->nr_iov].iov_base,
-                               rdata->iov[rdata->nr_iov].iov_len);
-                       memset(rdata->iov[rdata->nr_iov].iov_base + remaining,
-                               '\0', PAGE_CACHE_SIZE - remaining);
-                       ++rdata->nr_iov;
-                       len += remaining;
-                       remaining = 0;
-               } else if (page->index > eof_index) {
-                       /*
-                        * The VFS will not try to do readahead past the
-                        * i_size, but it's possible that we have outstanding
-                        * writes with gaps in the middle and the i_size hasn't
-                        * caught up yet. Populate those with zeroed out pages
-                        * to prevent the VFS from repeatedly attempting to
-                        * fill them until the writes are flushed.
-                        */
-                       zero_user(page, 0, PAGE_CACHE_SIZE);
-                       list_del(&page->lru);
-                       lru_cache_add_file(page);
-                       flush_dcache_page(page);
-                       SetPageUptodate(page);
-                       unlock_page(page);
-                       page_cache_release(page);
-               } else {
-                       /* no need to hold page hostage */
-                       list_del(&page->lru);
-                       lru_cache_add_file(page);
-                       unlock_page(page);
-                       page_cache_release(page);
-               }
-       }
+       len = rdata->marshal_iov(rdata, data_len);
+       data_len -= len;
 
        /* issue the read if we have any iovecs left to fill */
        if (rdata->nr_iov > 1) {
@@ -1621,7 +1520,7 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
        rdata->bytes = length;
 
        cFYI(1, "total_read=%u buflen=%u remaining=%u", server->total_read,
-               buflen, remaining);
+               buflen, data_len);
 
        /* discard anything left over */
        if (server->total_read < buflen)
@@ -1631,33 +1530,6 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
        return length;
 }
 
-static void
-cifs_readv_complete(struct work_struct *work)
-{
-       struct cifs_readdata *rdata = container_of(work,
-                                               struct cifs_readdata, work);
-       struct page *page, *tpage;
-
-       list_for_each_entry_safe(page, tpage, &rdata->pages, lru) {
-               list_del(&page->lru);
-               lru_cache_add_file(page);
-
-               if (rdata->result == 0) {
-                       kunmap(page);
-                       flush_dcache_page(page);
-                       SetPageUptodate(page);
-               }
-
-               unlock_page(page);
-
-               if (rdata->result == 0)
-                       cifs_readpage_to_fscache(rdata->mapping->host, page);
-
-               page_cache_release(page);
-       }
-       cifs_readdata_free(rdata);
-}
-
 static void
 cifs_readv_callback(struct mid_q_entry *mid)
 {
@@ -1691,7 +1563,7 @@ cifs_readv_callback(struct mid_q_entry *mid)
 
        queue_work(cifsiod_wq, &rdata->work);
        DeleteMidQEntry(mid);
-       cifs_add_credits(server, 1);
+       add_credits(server, 1);
 }
 
 /* cifs_async_readv - send an async write, and set up mid to handle result */
@@ -1744,12 +1616,15 @@ cifs_async_readv(struct cifs_readdata *rdata)
        rdata->iov[0].iov_base = smb;
        rdata->iov[0].iov_len = be32_to_cpu(smb->hdr.smb_buf_length) + 4;
 
+       kref_get(&rdata->refcount);
        rc = cifs_call_async(tcon->ses->server, rdata->iov, 1,
                             cifs_readv_receive, cifs_readv_callback,
                             rdata, false);
 
        if (rc == 0)
                cifs_stats_inc(&tcon->num_reads);
+       else
+               kref_put(&rdata->refcount, cifs_readdata_release);
 
        cifs_small_buf_release(smb);
        return rc;
@@ -2135,7 +2010,7 @@ cifs_writev_callback(struct mid_q_entry *mid)
 
        queue_work(cifsiod_wq, &wdata->work);
        DeleteMidQEntry(mid);
-       cifs_add_credits(tcon->ses->server, 1);
+       add_credits(tcon->ses->server, 1);
 }
 
 /* cifs_async_writev - send an async write, and set up mid to handle result */
@@ -4344,7 +4219,7 @@ int
 CIFSFindFirst(const int xid, struct cifs_tcon *tcon,
              const char *searchName,
              const struct nls_table *nls_codepage,
-             __u16 *pnetfid,
+             __u16 *pnetfid, __u16 search_flags,
              struct cifs_search_info *psrch_inf, int remap, const char dirsep)
 {
 /* level 257 SMB_ */
@@ -4416,8 +4291,7 @@ findFirstRetry:
            cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM |
                        ATTR_DIRECTORY);
        pSMB->SearchCount = cpu_to_le16(CIFSMaxBufSize/sizeof(FILE_UNIX_INFO));
-       pSMB->SearchFlags = cpu_to_le16(CIFS_SEARCH_CLOSE_AT_END |
-               CIFS_SEARCH_RETURN_RESUME);
+       pSMB->SearchFlags = cpu_to_le16(search_flags);
        pSMB->InformationLevel = cpu_to_le16(psrch_inf->info_level);
 
        /* BB what should we set StorageType to? Does it matter? BB */
@@ -4487,8 +4361,8 @@ findFirstRetry:
        return rc;
 }
 
-int CIFSFindNext(const int xid, struct cifs_tcon *tcon,
-                __u16 searchHandle, struct cifs_search_info *psrch_inf)
+int CIFSFindNext(const int xid, struct cifs_tcon *tcon, __u16 searchHandle,
+                __u16 search_flags, struct cifs_search_info *psrch_inf)
 {
        TRANSACTION2_FNEXT_REQ *pSMB = NULL;
        TRANSACTION2_FNEXT_RSP *pSMBr = NULL;
@@ -4531,8 +4405,7 @@ int CIFSFindNext(const int xid, struct cifs_tcon *tcon,
                cpu_to_le16(CIFSMaxBufSize / sizeof(FILE_UNIX_INFO));
        pSMB->InformationLevel = cpu_to_le16(psrch_inf->info_level);
        pSMB->ResumeKey = psrch_inf->resume_key;
-       pSMB->SearchFlags =
-             cpu_to_le16(CIFS_SEARCH_CLOSE_AT_END | CIFS_SEARCH_RETURN_RESUME);
+       pSMB->SearchFlags = cpu_to_le16(search_flags);
 
        name_len = psrch_inf->resume_name_len;
        params += name_len;
index e0b56d7a19c561be0b173a670d18f6d8435a12ed..ccafdedd0dbc4df14e04c0087d28b1dfa453be1f 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *   fs/cifs/connect.c
  *
- *   Copyright (C) International Business Machines  Corp., 2002,2009
+ *   Copyright (C) International Business Machines  Corp., 2002,2011
  *   Author(s): Steve French (sfrench@us.ibm.com)
  *
  *   This library is free software; you can redistribute it and/or modify
@@ -102,7 +102,7 @@ enum {
        Opt_srcaddr, Opt_prefixpath,
        Opt_iocharset, Opt_sockopt,
        Opt_netbiosname, Opt_servern,
-       Opt_ver, Opt_sec,
+       Opt_ver, Opt_vers, Opt_sec, Opt_cache,
 
        /* Mount options to be ignored */
        Opt_ignore,
@@ -210,9 +210,9 @@ static const match_table_t cifs_mount_option_tokens = {
        { Opt_netbiosname, "netbiosname=%s" },
        { Opt_servern, "servern=%s" },
        { Opt_ver, "ver=%s" },
-       { Opt_ver, "vers=%s" },
-       { Opt_ver, "version=%s" },
+       { Opt_vers, "vers=%s" },
        { Opt_sec, "sec=%s" },
+       { Opt_cache, "cache=%s" },
 
        { Opt_ignore, "cred" },
        { Opt_ignore, "credentials" },
@@ -261,6 +261,26 @@ static const match_table_t cifs_secflavor_tokens = {
        { Opt_sec_err, NULL }
 };
 
+/* cache flavors */
+enum {
+       Opt_cache_loose,
+       Opt_cache_strict,
+       Opt_cache_none,
+       Opt_cache_err
+};
+
+static const match_table_t cifs_cacheflavor_tokens = {
+       { Opt_cache_loose, "loose" },
+       { Opt_cache_strict, "strict" },
+       { Opt_cache_none, "none" },
+       { Opt_cache_err, NULL }
+};
+
+static const match_table_t cifs_smb_version_tokens = {
+       { Smb_1, SMB1_VERSION_STRING },
+       { Smb_21, SMB21_VERSION_STRING },
+};
+
 static int ip_connect(struct TCP_Server_Info *server);
 static int generic_ip_connect(struct TCP_Server_Info *server);
 static void tlink_rb_insert(struct rb_root *root, struct tcon_link *new_tlink);
@@ -549,7 +569,7 @@ allocate_buffers(struct TCP_Server_Info *server)
                }
        } else if (server->large_buf) {
                /* we are reusing a dirty large buf, clear its start */
-               memset(server->bigbuf, 0, header_size());
+               memset(server->bigbuf, 0, HEADER_SIZE(server));
        }
 
        if (!server->smallbuf) {
@@ -563,7 +583,7 @@ allocate_buffers(struct TCP_Server_Info *server)
                /* beginning of smb buffer is cleared in our buf_get */
        } else {
                /* if existing small buf clear beginning */
-               memset(server->smallbuf, 0, header_size());
+               memset(server->smallbuf, 0, HEADER_SIZE(server));
        }
 
        return true;
@@ -764,25 +784,6 @@ is_smb_response(struct TCP_Server_Info *server, unsigned char type)
        return false;
 }
 
-static struct mid_q_entry *
-find_mid(struct TCP_Server_Info *server, char *buffer)
-{
-       struct smb_hdr *buf = (struct smb_hdr *)buffer;
-       struct mid_q_entry *mid;
-
-       spin_lock(&GlobalMid_Lock);
-       list_for_each_entry(mid, &server->pending_mid_q, qhead) {
-               if (mid->mid == buf->Mid &&
-                   mid->mid_state == MID_REQUEST_SUBMITTED &&
-                   le16_to_cpu(mid->command) == buf->Command) {
-                       spin_unlock(&GlobalMid_Lock);
-                       return mid;
-               }
-       }
-       spin_unlock(&GlobalMid_Lock);
-       return NULL;
-}
-
 void
 dequeue_mid(struct mid_q_entry *mid, bool malformed)
 {
@@ -934,7 +935,7 @@ standard_receive3(struct TCP_Server_Info *server, struct mid_q_entry *mid)
        unsigned int pdu_length = get_rfc1002_length(buf);
 
        /* make sure this will fit in a large buffer */
-       if (pdu_length > CIFSMaxBufSize + max_header_size() - 4) {
+       if (pdu_length > CIFSMaxBufSize + MAX_HEADER_SIZE(server) - 4) {
                cERROR(1, "SMB response too long (%u bytes)",
                        pdu_length);
                cifs_reconnect(server);
@@ -950,8 +951,8 @@ standard_receive3(struct TCP_Server_Info *server, struct mid_q_entry *mid)
        }
 
        /* now read the rest */
-       length = cifs_read_from_socket(server, buf + header_size() - 1,
-                                      pdu_length - header_size() + 1 + 4);
+       length = cifs_read_from_socket(server, buf + HEADER_SIZE(server) - 1,
+                               pdu_length - HEADER_SIZE(server) + 1 + 4);
        if (length < 0)
                return length;
        server->total_read += length;
@@ -967,7 +968,7 @@ standard_receive3(struct TCP_Server_Info *server, struct mid_q_entry *mid)
         * 48 bytes is enough to display the header and a little bit
         * into the payload for debugging purposes.
         */
-       length = checkSMB(buf, server->total_read);
+       length = server->ops->check_message(buf, server->total_read);
        if (length != 0)
                cifs_dump_mem("Bad SMB: ", buf,
                        min_t(unsigned int, server->total_read, 48));
@@ -1025,7 +1026,7 @@ cifs_demultiplex_thread(void *p)
                        continue;
 
                /* make sure we have enough to get to the MID */
-               if (pdu_length < header_size() - 1 - 4) {
+               if (pdu_length < HEADER_SIZE(server) - 1 - 4) {
                        cERROR(1, "SMB response too short (%u bytes)",
                                pdu_length);
                        cifs_reconnect(server);
@@ -1035,12 +1036,12 @@ cifs_demultiplex_thread(void *p)
 
                /* read down to the MID */
                length = cifs_read_from_socket(server, buf + 4,
-                                              header_size() - 1 - 4);
+                                              HEADER_SIZE(server) - 1 - 4);
                if (length < 0)
                        continue;
                server->total_read += length;
 
-               mid_entry = find_mid(server, buf);
+               mid_entry = server->ops->find_mid(server, buf);
 
                if (!mid_entry || !mid_entry->receive)
                        length = standard_receive3(server, mid_entry);
@@ -1057,12 +1058,13 @@ cifs_demultiplex_thread(void *p)
                if (mid_entry != NULL) {
                        if (!mid_entry->multiRsp || mid_entry->multiEnd)
                                mid_entry->callback(mid_entry);
-               } else if (!is_valid_oplock_break(buf, server)) {
+               } else if (!server->ops->is_oplock_break(buf, server)) {
                        cERROR(1, "No task to wake, unknown frame received! "
                                   "NumMids %d", atomic_read(&midCount));
-                       cifs_dump_mem("Received Data is: ", buf, header_size());
+                       cifs_dump_mem("Received Data is: ", buf,
+                                     HEADER_SIZE(server));
 #ifdef CONFIG_CIFS_DEBUG2
-                       cifs_dump_detail(buf);
+                       server->ops->dump_detail(buf);
                        cifs_dump_mids(server);
 #endif /* CIFS_DEBUG2 */
 
@@ -1185,6 +1187,54 @@ static int cifs_parse_security_flavors(char *value,
        return 0;
 }
 
+static int
+cifs_parse_cache_flavor(char *value, struct smb_vol *vol)
+{
+       substring_t args[MAX_OPT_ARGS];
+
+       switch (match_token(value, cifs_cacheflavor_tokens, args)) {
+       case Opt_cache_loose:
+               vol->direct_io = false;
+               vol->strict_io = false;
+               break;
+       case Opt_cache_strict:
+               vol->direct_io = false;
+               vol->strict_io = true;
+               break;
+       case Opt_cache_none:
+               vol->direct_io = true;
+               vol->strict_io = false;
+               break;
+       default:
+               cERROR(1, "bad cache= option: %s", value);
+               return 1;
+       }
+       return 0;
+}
+
+static int
+cifs_parse_smb_version(char *value, struct smb_vol *vol)
+{
+       substring_t args[MAX_OPT_ARGS];
+
+       switch (match_token(value, cifs_smb_version_tokens, args)) {
+       case Smb_1:
+               vol->ops = &smb1_operations;
+               vol->vals = &smb1_values;
+               break;
+#ifdef CONFIG_CIFS_SMB2
+       case Smb_21:
+               vol->ops = &smb21_operations;
+               vol->vals = &smb21_values;
+               break;
+#endif
+       default:
+               cERROR(1, "Unknown vers= option specified: %s", value);
+               return 1;
+       }
+       return 0;
+}
+
 static int
 cifs_parse_mount_options(const char *mountdata, const char *devname,
                         struct smb_vol *vol)
@@ -1203,6 +1253,8 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
        char *string = NULL;
        char *tmp_end, *value;
        char delim;
+       bool cache_specified = false;
+       static bool cache_warned = false;
 
        separator[0] = ',';
        separator[1] = 0;
@@ -1236,6 +1288,10 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
 
        vol->actimeo = CIFS_DEF_ACTIMEO;
 
+       /* FIXME: add autonegotiation -- for now, SMB1 is default */
+       vol->ops = &smb1_operations;
+       vol->vals = &smb1_values;
+
        if (!mountdata)
                goto cifs_parse_mount_err;
 
@@ -1414,10 +1470,20 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
                        vol->seal = 1;
                        break;
                case Opt_direct:
-                       vol->direct_io = 1;
+                       cache_specified = true;
+                       vol->direct_io = true;
+                       vol->strict_io = false;
+                       cERROR(1, "The \"directio\" option will be removed in "
+                                 "3.7. Please switch to the \"cache=none\" "
+                                 "option.");
                        break;
                case Opt_strictcache:
-                       vol->strict_io = 1;
+                       cache_specified = true;
+                       vol->direct_io = false;
+                       vol->strict_io = true;
+                       cERROR(1, "The \"strictcache\" option will be removed "
+                               "in 3.7. Please switch to the \"cache=strict\" "
+                               "option.");
                        break;
                case Opt_noac:
                        printk(KERN_WARNING "CIFS: Mount option noac not "
@@ -1821,8 +1887,7 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
                        if (string == NULL)
                                goto out_nomem;
 
-                       if (strnicmp(string, "cifs", 4) == 0 ||
-                           strnicmp(string, "1", 1) == 0) {
+                       if (strnicmp(string, "1", 1) == 0) {
                                /* This is the default */
                                break;
                        }
@@ -1830,6 +1895,14 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
                        printk(KERN_WARNING "CIFS: Invalid version"
                                            " specified\n");
                        goto cifs_parse_mount_err;
+               case Opt_vers:
+                       string = match_strdup(args);
+                       if (string == NULL)
+                               goto out_nomem;
+
+                       if (cifs_parse_smb_version(string, vol) != 0)
+                               goto cifs_parse_mount_err;
+                       break;
                case Opt_sec:
                        string = match_strdup(args);
                        if (string == NULL)
@@ -1838,6 +1911,15 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
                        if (cifs_parse_security_flavors(string, vol) != 0)
                                goto cifs_parse_mount_err;
                        break;
+               case Opt_cache:
+                       cache_specified = true;
+                       string = match_strdup(args);
+                       if (string == NULL)
+                               goto out_nomem;
+
+                       if (cifs_parse_cache_flavor(string, vol) != 0)
+                               goto cifs_parse_mount_err;
+                       break;
                default:
                        /*
                         * An option we don't recognize. Save it off for later
@@ -1881,6 +1963,14 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
                printk(KERN_NOTICE "CIFS: ignoring forcegid mount option "
                                   "specified with no gid= option.\n");
 
+       /* FIXME: remove this block in 3.7 */
+       if (!cache_specified && !cache_warned) {
+               cache_warned = true;
+               printk(KERN_NOTICE "CIFS: no cache= option specified, using "
+                                  "\"cache=loose\". This default will change "
+                                  "to \"cache=strict\" in 3.7.\n");
+       }
+
        kfree(mountdata_copy);
        return 0;
 
@@ -2041,6 +2131,9 @@ match_security(struct TCP_Server_Info *server, struct smb_vol *vol)
 static int match_server(struct TCP_Server_Info *server, struct sockaddr *addr,
                         struct smb_vol *vol)
 {
+       if ((server->vals != vol->vals) || (server->ops != vol->ops))
+               return 0;
+
        if (!net_eq(cifs_net_ns(server), current->nsproxy->net_ns))
                return 0;
 
@@ -2163,6 +2256,8 @@ cifs_get_tcp_session(struct smb_vol *volume_info)
                goto out_err;
        }
 
+       tcp_ses->ops = volume_info->ops;
+       tcp_ses->vals = volume_info->vals;
        cifs_set_net_ns(tcp_ses, get_net(current->nsproxy->net_ns));
        tcp_ses->hostname = extract_hostname(volume_info->UNC);
        if (IS_ERR(tcp_ses->hostname)) {
@@ -3569,6 +3664,7 @@ cifs_setup_volume_info(struct smb_vol *volume_info, char *mount_data,
        if (cifs_parse_mount_options(mount_data, devname, volume_info))
                return -EINVAL;
 
+
        if (volume_info->nullauth) {
                cFYI(1, "Anonymous login");
                kfree(volume_info->username);
@@ -4010,11 +4106,11 @@ int cifs_negotiate_protocol(unsigned int xid, struct cifs_ses *ses)
        if (server->maxBuf != 0)
                return 0;
 
-       cifs_set_credits(server, 1);
+       set_credits(server, 1);
        rc = CIFSSMBNegotiate(xid, ses);
        if (rc == -EAGAIN) {
                /* retry only once on 1st time connection */
-               cifs_set_credits(server, 1);
+               set_credits(server, 1);
                rc = CIFSSMBNegotiate(xid, ses);
                if (rc == -EAGAIN)
                        rc = -EHOSTDOWN;
index 81725e9286e911f501e4a78d1d7c28768753d118..253170dfa71650704c8c2f6cdb69942635488404 100644 (file)
@@ -264,6 +264,7 @@ cifs_new_fileinfo(__u16 fileHandle, struct file *file,
        pCifsFile->tlink = cifs_get_tlink(tlink);
        mutex_init(&pCifsFile->fh_mutex);
        INIT_WORK(&pCifsFile->oplock_break, cifs_oplock_break);
+       INIT_LIST_HEAD(&pCifsFile->llist);
 
        spin_lock(&cifs_file_list_lock);
        list_add(&pCifsFile->tlist, &(tlink_tcon(tlink)->openFileList));
@@ -334,9 +335,7 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file)
         * is closed anyway.
         */
        mutex_lock(&cifsi->lock_mutex);
-       list_for_each_entry_safe(li, tmp, &cifsi->llist, llist) {
-               if (li->netfid != cifs_file->netfid)
-                       continue;
+       list_for_each_entry_safe(li, tmp, &cifs_file->llist, llist) {
                list_del(&li->llist);
                cifs_del_lock_waiters(li);
                kfree(li);
@@ -645,7 +644,7 @@ int cifs_closedir(struct inode *inode, struct file *file)
 }
 
 static struct cifsLockInfo *
-cifs_lock_init(__u64 offset, __u64 length, __u8 type, __u16 netfid)
+cifs_lock_init(__u64 offset, __u64 length, __u8 type)
 {
        struct cifsLockInfo *lock =
                kmalloc(sizeof(struct cifsLockInfo), GFP_KERNEL);
@@ -654,7 +653,6 @@ cifs_lock_init(__u64 offset, __u64 length, __u8 type, __u16 netfid)
        lock->offset = offset;
        lock->length = length;
        lock->type = type;
-       lock->netfid = netfid;
        lock->pid = current->tgid;
        INIT_LIST_HEAD(&lock->blist);
        init_waitqueue_head(&lock->block_q);
@@ -672,19 +670,20 @@ cifs_del_lock_waiters(struct cifsLockInfo *lock)
 }
 
 static bool
-__cifs_find_lock_conflict(struct cifsInodeInfo *cinode, __u64 offset,
-                       __u64 length, __u8 type, __u16 netfid,
-                       struct cifsLockInfo **conf_lock)
+cifs_find_fid_lock_conflict(struct cifsFileInfo *cfile, __u64 offset,
+                           __u64 length, __u8 type, struct cifsFileInfo *cur,
+                           struct cifsLockInfo **conf_lock)
 {
-       struct cifsLockInfo *li, *tmp;
+       struct cifsLockInfo *li;
+       struct TCP_Server_Info *server = tlink_tcon(cfile->tlink)->ses->server;
 
-       list_for_each_entry_safe(li, tmp, &cinode->llist, llist) {
+       list_for_each_entry(li, &cfile->llist, llist) {
                if (offset + length <= li->offset ||
                    offset >= li->offset + li->length)
                        continue;
-               else if ((type & LOCKING_ANDX_SHARED_LOCK) &&
-                        ((netfid == li->netfid && current->tgid == li->pid) ||
-                         type == li->type))
+               else if ((type & server->vals->shared_lock_type) &&
+                        ((server->ops->compare_fids(cur, cfile) &&
+                          current->tgid == li->pid) || type == li->type))
                        continue;
                else {
                        *conf_lock = li;
@@ -695,11 +694,23 @@ __cifs_find_lock_conflict(struct cifsInodeInfo *cinode, __u64 offset,
 }
 
 static bool
-cifs_find_lock_conflict(struct cifsInodeInfo *cinode, struct cifsLockInfo *lock,
-                       struct cifsLockInfo **conf_lock)
+cifs_find_lock_conflict(struct cifsFileInfo *cfile, __u64 offset, __u64 length,
+                       __u8 type, struct cifsLockInfo **conf_lock)
 {
-       return __cifs_find_lock_conflict(cinode, lock->offset, lock->length,
-                                        lock->type, lock->netfid, conf_lock);
+       bool rc = false;
+       struct cifsFileInfo *fid, *tmp;
+       struct cifsInodeInfo *cinode = CIFS_I(cfile->dentry->d_inode);
+
+       spin_lock(&cifs_file_list_lock);
+       list_for_each_entry_safe(fid, tmp, &cinode->openFileList, flist) {
+               rc = cifs_find_fid_lock_conflict(fid, offset, length, type,
+                                                cfile, conf_lock);
+               if (rc)
+                       break;
+       }
+       spin_unlock(&cifs_file_list_lock);
+
+       return rc;
 }
 
 /*
@@ -710,22 +721,24 @@ cifs_find_lock_conflict(struct cifsInodeInfo *cinode, struct cifsLockInfo *lock,
  * the server or 1 otherwise.
  */
 static int
-cifs_lock_test(struct cifsInodeInfo *cinode, __u64 offset, __u64 length,
-              __u8 type, __u16 netfid, struct file_lock *flock)
+cifs_lock_test(struct cifsFileInfo *cfile, __u64 offset, __u64 length,
+              __u8 type, struct file_lock *flock)
 {
        int rc = 0;
        struct cifsLockInfo *conf_lock;
+       struct cifsInodeInfo *cinode = CIFS_I(cfile->dentry->d_inode);
+       struct TCP_Server_Info *server = tlink_tcon(cfile->tlink)->ses->server;
        bool exist;
 
        mutex_lock(&cinode->lock_mutex);
 
-       exist = __cifs_find_lock_conflict(cinode, offset, length, type, netfid,
-                                         &conf_lock);
+       exist = cifs_find_lock_conflict(cfile, offset, length, type,
+                                       &conf_lock);
        if (exist) {
                flock->fl_start = conf_lock->offset;
                flock->fl_end = conf_lock->offset + conf_lock->length - 1;
                flock->fl_pid = conf_lock->pid;
-               if (conf_lock->type & LOCKING_ANDX_SHARED_LOCK)
+               if (conf_lock->type & server->vals->shared_lock_type)
                        flock->fl_type = F_RDLCK;
                else
                        flock->fl_type = F_WRLCK;
@@ -739,10 +752,11 @@ cifs_lock_test(struct cifsInodeInfo *cinode, __u64 offset, __u64 length,
 }
 
 static void
-cifs_lock_add(struct cifsInodeInfo *cinode, struct cifsLockInfo *lock)
+cifs_lock_add(struct cifsFileInfo *cfile, struct cifsLockInfo *lock)
 {
+       struct cifsInodeInfo *cinode = CIFS_I(cfile->dentry->d_inode);
        mutex_lock(&cinode->lock_mutex);
-       list_add_tail(&lock->llist, &cinode->llist);
+       list_add_tail(&lock->llist, &cfile->llist);
        mutex_unlock(&cinode->lock_mutex);
 }
 
@@ -753,10 +767,11 @@ cifs_lock_add(struct cifsInodeInfo *cinode, struct cifsLockInfo *lock)
  * 3) -EACCESS, if there is a lock that prevents us and wait is false.
  */
 static int
-cifs_lock_add_if(struct cifsInodeInfo *cinode, struct cifsLockInfo *lock,
+cifs_lock_add_if(struct cifsFileInfo *cfile, struct cifsLockInfo *lock,
                 bool wait)
 {
        struct cifsLockInfo *conf_lock;
+       struct cifsInodeInfo *cinode = CIFS_I(cfile->dentry->d_inode);
        bool exist;
        int rc = 0;
 
@@ -764,9 +779,10 @@ try_again:
        exist = false;
        mutex_lock(&cinode->lock_mutex);
 
-       exist = cifs_find_lock_conflict(cinode, lock, &conf_lock);
+       exist = cifs_find_lock_conflict(cfile, lock->offset, lock->length,
+                                       lock->type, &conf_lock);
        if (!exist && cinode->can_cache_brlcks) {
-               list_add_tail(&lock->llist, &cinode->llist);
+               list_add_tail(&lock->llist, &cfile->llist);
                mutex_unlock(&cinode->lock_mutex);
                return rc;
        }
@@ -888,7 +904,7 @@ cifs_push_mandatory_locks(struct cifsFileInfo *cfile)
        for (i = 0; i < 2; i++) {
                cur = buf;
                num = 0;
-               list_for_each_entry_safe(li, tmp, &cinode->llist, llist) {
+               list_for_each_entry_safe(li, tmp, &cfile->llist, llist) {
                        if (li->type != types[i])
                                continue;
                        cur->Pid = cpu_to_le16(li->pid);
@@ -898,7 +914,8 @@ cifs_push_mandatory_locks(struct cifsFileInfo *cfile)
                        cur->OffsetHigh = cpu_to_le32((u32)(li->offset>>32));
                        if (++num == max_num) {
                                stored_rc = cifs_lockv(xid, tcon, cfile->netfid,
-                                                      li->type, 0, num, buf);
+                                                      (__u8)li->type, 0, num,
+                                                      buf);
                                if (stored_rc)
                                        rc = stored_rc;
                                cur = buf;
@@ -909,7 +926,7 @@ cifs_push_mandatory_locks(struct cifsFileInfo *cfile)
 
                if (num) {
                        stored_rc = cifs_lockv(xid, tcon, cfile->netfid,
-                                              types[i], 0, num, buf);
+                                              (__u8)types[i], 0, num, buf);
                        if (stored_rc)
                                rc = stored_rc;
                }
@@ -1053,8 +1070,8 @@ cifs_push_locks(struct cifsFileInfo *cfile)
 }
 
 static void
-cifs_read_flock(struct file_lock *flock, __u8 *type, int *lock, int *unlock,
-               bool *wait_flag)
+cifs_read_flock(struct file_lock *flock, __u32 *type, int *lock, int *unlock,
+               bool *wait_flag, struct TCP_Server_Info *server)
 {
        if (flock->fl_flags & FL_POSIX)
                cFYI(1, "Posix");
@@ -1073,38 +1090,50 @@ cifs_read_flock(struct file_lock *flock, __u8 *type, int *lock, int *unlock,
            (~(FL_POSIX | FL_FLOCK | FL_SLEEP | FL_ACCESS | FL_LEASE)))
                cFYI(1, "Unknown lock flags 0x%x", flock->fl_flags);
 
-       *type = LOCKING_ANDX_LARGE_FILES;
+       *type = server->vals->large_lock_type;
        if (flock->fl_type == F_WRLCK) {
                cFYI(1, "F_WRLCK ");
+               *type |= server->vals->exclusive_lock_type;
                *lock = 1;
        } else if (flock->fl_type == F_UNLCK) {
                cFYI(1, "F_UNLCK");
+               *type |= server->vals->unlock_lock_type;
                *unlock = 1;
                /* Check if unlock includes more than one lock range */
        } else if (flock->fl_type == F_RDLCK) {
                cFYI(1, "F_RDLCK");
-               *type |= LOCKING_ANDX_SHARED_LOCK;
+               *type |= server->vals->shared_lock_type;
                *lock = 1;
        } else if (flock->fl_type == F_EXLCK) {
                cFYI(1, "F_EXLCK");
+               *type |= server->vals->exclusive_lock_type;
                *lock = 1;
        } else if (flock->fl_type == F_SHLCK) {
                cFYI(1, "F_SHLCK");
-               *type |= LOCKING_ANDX_SHARED_LOCK;
+               *type |= server->vals->shared_lock_type;
                *lock = 1;
        } else
                cFYI(1, "Unknown type of lock");
 }
 
 static int
-cifs_getlk(struct file *file, struct file_lock *flock, __u8 type,
+cifs_mandatory_lock(int xid, struct cifsFileInfo *cfile, __u64 offset,
+                   __u64 length, __u32 type, int lock, int unlock, bool wait)
+{
+       return CIFSSMBLock(xid, tlink_tcon(cfile->tlink), cfile->netfid,
+                          current->tgid, length, offset, unlock, lock,
+                          (__u8)type, wait, 0);
+}
+
+static int
+cifs_getlk(struct file *file, struct file_lock *flock, __u32 type,
           bool wait_flag, bool posix_lck, int xid)
 {
        int rc = 0;
        __u64 length = 1 + flock->fl_end - flock->fl_start;
        struct cifsFileInfo *cfile = (struct cifsFileInfo *)file->private_data;
        struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
-       struct cifsInodeInfo *cinode = CIFS_I(cfile->dentry->d_inode);
+       struct TCP_Server_Info *server = tcon->ses->server;
        __u16 netfid = cfile->netfid;
 
        if (posix_lck) {
@@ -1114,7 +1143,7 @@ cifs_getlk(struct file *file, struct file_lock *flock, __u8 type,
                if (!rc)
                        return rc;
 
-               if (type & LOCKING_ANDX_SHARED_LOCK)
+               if (type & server->vals->shared_lock_type)
                        posix_lock_type = CIFS_RDLCK;
                else
                        posix_lock_type = CIFS_WRLCK;
@@ -1124,38 +1153,35 @@ cifs_getlk(struct file *file, struct file_lock *flock, __u8 type,
                return rc;
        }
 
-       rc = cifs_lock_test(cinode, flock->fl_start, length, type, netfid,
-                           flock);
+       rc = cifs_lock_test(cfile, flock->fl_start, length, type, flock);
        if (!rc)
                return rc;
 
        /* BB we could chain these into one lock request BB */
-       rc = CIFSSMBLock(xid, tcon, netfid, current->tgid, length,
-                        flock->fl_start, 0, 1, type, 0, 0);
+       rc = cifs_mandatory_lock(xid, cfile, flock->fl_start, length, type,
+                                1, 0, false);
        if (rc == 0) {
-               rc = CIFSSMBLock(xid, tcon, netfid, current->tgid,
-                                length, flock->fl_start, 1, 0,
-                                type, 0, 0);
+               rc = cifs_mandatory_lock(xid, cfile, flock->fl_start, length,
+                                        type, 0, 1, false);
                flock->fl_type = F_UNLCK;
                if (rc != 0)
                        cERROR(1, "Error unlocking previously locked "
-                                  "range %d during test of lock", rc);
+                                 "range %d during test of lock", rc);
                return 0;
        }
 
-       if (type & LOCKING_ANDX_SHARED_LOCK) {
+       if (type & server->vals->shared_lock_type) {
                flock->fl_type = F_WRLCK;
                return 0;
        }
 
-       rc = CIFSSMBLock(xid, tcon, netfid, current->tgid, length,
-                        flock->fl_start, 0, 1,
-                        type | LOCKING_ANDX_SHARED_LOCK, 0, 0);
+       rc = cifs_mandatory_lock(xid, cfile, flock->fl_start, length,
+                                type | server->vals->shared_lock_type, 1, 0,
+                                false);
        if (rc == 0) {
-               rc = CIFSSMBLock(xid, tcon, netfid, current->tgid,
-                                length, flock->fl_start, 1, 0,
-                                type | LOCKING_ANDX_SHARED_LOCK,
-                                0, 0);
+               rc = cifs_mandatory_lock(xid, cfile, flock->fl_start, length,
+                                        type | server->vals->shared_lock_type,
+                                        0, 1, false);
                flock->fl_type = F_RDLCK;
                if (rc != 0)
                        cERROR(1, "Error unlocking previously locked "
@@ -1212,15 +1238,13 @@ cifs_unlock_range(struct cifsFileInfo *cfile, struct file_lock *flock, int xid)
        for (i = 0; i < 2; i++) {
                cur = buf;
                num = 0;
-               list_for_each_entry_safe(li, tmp, &cinode->llist, llist) {
+               list_for_each_entry_safe(li, tmp, &cfile->llist, llist) {
                        if (flock->fl_start > li->offset ||
                            (flock->fl_start + length) <
                            (li->offset + li->length))
                                continue;
                        if (current->tgid != li->pid)
                                continue;
-                       if (cfile->netfid != li->netfid)
-                               continue;
                        if (types[i] != li->type)
                                continue;
                        if (!cinode->can_cache_brlcks) {
@@ -1233,7 +1257,7 @@ cifs_unlock_range(struct cifsFileInfo *cfile, struct file_lock *flock, int xid)
                                        cpu_to_le32((u32)(li->offset>>32));
                                /*
                                 * We need to save a lock here to let us add
-                                * it again to the inode list if the unlock
+                                * it again to the file's list if the unlock
                                 * range request fails on the server.
                                 */
                                list_move(&li->llist, &tmp_llist);
@@ -1247,10 +1271,10 @@ cifs_unlock_range(struct cifsFileInfo *cfile, struct file_lock *flock, int xid)
                                                 * We failed on the unlock range
                                                 * request - add all locks from
                                                 * the tmp list to the head of
-                                                * the inode list.
+                                                * the file's list.
                                                 */
                                                cifs_move_llist(&tmp_llist,
-                                                               &cinode->llist);
+                                                               &cfile->llist);
                                                rc = stored_rc;
                                        } else
                                                /*
@@ -1265,7 +1289,7 @@ cifs_unlock_range(struct cifsFileInfo *cfile, struct file_lock *flock, int xid)
                        } else {
                                /*
                                 * We can cache brlock requests - simply remove
-                                * a lock from the inode list.
+                                * a lock from the file's list.
                                 */
                                list_del(&li->llist);
                                cifs_del_lock_waiters(li);
@@ -1276,7 +1300,7 @@ cifs_unlock_range(struct cifsFileInfo *cfile, struct file_lock *flock, int xid)
                        stored_rc = cifs_lockv(xid, tcon, cfile->netfid,
                                               types[i], num, 0, buf);
                        if (stored_rc) {
-                               cifs_move_llist(&tmp_llist, &cinode->llist);
+                               cifs_move_llist(&tmp_llist, &cfile->llist);
                                rc = stored_rc;
                        } else
                                cifs_free_llist(&tmp_llist);
@@ -1289,14 +1313,14 @@ cifs_unlock_range(struct cifsFileInfo *cfile, struct file_lock *flock, int xid)
 }
 
 static int
-cifs_setlk(struct file *file,  struct file_lock *flock, __u8 type,
+cifs_setlk(struct file *file,  struct file_lock *flock, __u32 type,
           bool wait_flag, bool posix_lck, int lock, int unlock, int xid)
 {
        int rc = 0;
        __u64 length = 1 + flock->fl_end - flock->fl_start;
        struct cifsFileInfo *cfile = (struct cifsFileInfo *)file->private_data;
        struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
-       struct cifsInodeInfo *cinode = CIFS_I(file->f_path.dentry->d_inode);
+       struct TCP_Server_Info *server = tcon->ses->server;
        __u16 netfid = cfile->netfid;
 
        if (posix_lck) {
@@ -1306,7 +1330,7 @@ cifs_setlk(struct file *file,  struct file_lock *flock, __u8 type,
                if (!rc || rc < 0)
                        return rc;
 
-               if (type & LOCKING_ANDX_SHARED_LOCK)
+               if (type & server->vals->shared_lock_type)
                        posix_lock_type = CIFS_RDLCK;
                else
                        posix_lock_type = CIFS_WRLCK;
@@ -1323,24 +1347,24 @@ cifs_setlk(struct file *file,  struct file_lock *flock, __u8 type,
        if (lock) {
                struct cifsLockInfo *lock;
 
-               lock = cifs_lock_init(flock->fl_start, length, type, netfid);
+               lock = cifs_lock_init(flock->fl_start, length, type);
                if (!lock)
                        return -ENOMEM;
 
-               rc = cifs_lock_add_if(cinode, lock, wait_flag);
+               rc = cifs_lock_add_if(cfile, lock, wait_flag);
                if (rc < 0)
                        kfree(lock);
                if (rc <= 0)
                        goto out;
 
-               rc = CIFSSMBLock(xid, tcon, netfid, current->tgid, length,
-                                flock->fl_start, 0, 1, type, wait_flag, 0);
+               rc = cifs_mandatory_lock(xid, cfile, flock->fl_start, length,
+                                        type, 1, 0, wait_flag);
                if (rc) {
                        kfree(lock);
                        goto out;
                }
 
-               cifs_lock_add(cinode, lock);
+               cifs_lock_add(cfile, lock);
        } else if (unlock)
                rc = cifs_unlock_range(cfile, flock, xid);
 
@@ -1361,7 +1385,7 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *flock)
        struct cifsInodeInfo *cinode;
        struct cifsFileInfo *cfile;
        __u16 netfid;
-       __u8 type;
+       __u32 type;
 
        rc = -EACCES;
        xid = GetXid();
@@ -1370,11 +1394,13 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *flock)
                "end: %lld", cmd, flock->fl_flags, flock->fl_type,
                flock->fl_start, flock->fl_end);
 
-       cifs_read_flock(flock, &type, &lock, &unlock, &wait_flag);
-
-       cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
        cfile = (struct cifsFileInfo *)file->private_data;
        tcon = tlink_tcon(cfile->tlink);
+
+       cifs_read_flock(flock, &type, &lock, &unlock, &wait_flag,
+                       tcon->ses->server);
+
+       cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
        netfid = cfile->netfid;
        cinode = CIFS_I(file->f_path.dentry->d_inode);
 
@@ -1539,10 +1565,11 @@ struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode,
 struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode,
                                        bool fsuid_only)
 {
-       struct cifsFileInfo *open_file;
+       struct cifsFileInfo *open_file, *inv_file = NULL;
        struct cifs_sb_info *cifs_sb;
        bool any_available = false;
        int rc;
+       unsigned int refind = 0;
 
        /* Having a null inode here (because mapping->host was set to zero by
        the VFS or MM) should not happen but we had reports of on oops (due to
@@ -1562,40 +1589,25 @@ struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode,
 
        spin_lock(&cifs_file_list_lock);
 refind_writable:
+       if (refind > MAX_REOPEN_ATT) {
+               spin_unlock(&cifs_file_list_lock);
+               return NULL;
+       }
        list_for_each_entry(open_file, &cifs_inode->openFileList, flist) {
                if (!any_available && open_file->pid != current->tgid)
                        continue;
                if (fsuid_only && open_file->uid != current_fsuid())
                        continue;
                if (OPEN_FMODE(open_file->f_flags) & FMODE_WRITE) {
-                       cifsFileInfo_get(open_file);
-
                        if (!open_file->invalidHandle) {
                                /* found a good writable file */
+                               cifsFileInfo_get(open_file);
                                spin_unlock(&cifs_file_list_lock);
                                return open_file;
+                       } else {
+                               if (!inv_file)
+                                       inv_file = open_file;
                        }
-
-                       spin_unlock(&cifs_file_list_lock);
-
-                       /* Had to unlock since following call can block */
-                       rc = cifs_reopen_file(open_file, false);
-                       if (!rc)
-                               return open_file;
-
-                       /* if it fails, try another handle if possible */
-                       cFYI(1, "wp failed on reopen file");
-                       cifsFileInfo_put(open_file);
-
-                       spin_lock(&cifs_file_list_lock);
-
-                       /* else we simply continue to the next entry. Thus
-                          we do not loop on reopen errors.  If we
-                          can not reopen the file, for example if we
-                          reconnected to a server with another client
-                          racing to delete or lock the file we would not
-                          make progress if we restarted before the beginning
-                          of the loop here. */
                }
        }
        /* couldn't find useable FH with same pid, try any available */
@@ -1603,7 +1615,30 @@ refind_writable:
                any_available = true;
                goto refind_writable;
        }
+
+       if (inv_file) {
+               any_available = false;
+               cifsFileInfo_get(inv_file);
+       }
+
        spin_unlock(&cifs_file_list_lock);
+
+       if (inv_file) {
+               rc = cifs_reopen_file(inv_file, false);
+               if (!rc)
+                       return inv_file;
+               else {
+                       spin_lock(&cifs_file_list_lock);
+                       list_move_tail(&inv_file->flist,
+                                       &cifs_inode->openFileList);
+                       spin_unlock(&cifs_file_list_lock);
+                       cifsFileInfo_put(inv_file);
+                       spin_lock(&cifs_file_list_lock);
+                       ++refind;
+                       goto refind_writable;
+               }
+       }
+
        return NULL;
 }
 
@@ -2339,24 +2374,224 @@ ssize_t cifs_strict_writev(struct kiocb *iocb, const struct iovec *iov,
        return cifs_user_writev(iocb, iov, nr_segs, pos);
 }
 
+static struct cifs_readdata *
+cifs_readdata_alloc(unsigned int nr_vecs, work_func_t complete)
+{
+       struct cifs_readdata *rdata;
+
+       rdata = kzalloc(sizeof(*rdata) +
+                       sizeof(struct kvec) * nr_vecs, GFP_KERNEL);
+       if (rdata != NULL) {
+               kref_init(&rdata->refcount);
+               INIT_LIST_HEAD(&rdata->list);
+               init_completion(&rdata->done);
+               INIT_WORK(&rdata->work, complete);
+               INIT_LIST_HEAD(&rdata->pages);
+       }
+       return rdata;
+}
+
+void
+cifs_readdata_release(struct kref *refcount)
+{
+       struct cifs_readdata *rdata = container_of(refcount,
+                                       struct cifs_readdata, refcount);
+
+       if (rdata->cfile)
+               cifsFileInfo_put(rdata->cfile);
+
+       kfree(rdata);
+}
+
+static int
+cifs_read_allocate_pages(struct list_head *list, unsigned int npages)
+{
+       int rc = 0;
+       struct page *page, *tpage;
+       unsigned int i;
+
+       for (i = 0; i < npages; i++) {
+               page = alloc_page(GFP_KERNEL|__GFP_HIGHMEM);
+               if (!page) {
+                       rc = -ENOMEM;
+                       break;
+               }
+               list_add(&page->lru, list);
+       }
+
+       if (rc) {
+               list_for_each_entry_safe(page, tpage, list, lru) {
+                       list_del(&page->lru);
+                       put_page(page);
+               }
+       }
+       return rc;
+}
+
+static void
+cifs_uncached_readdata_release(struct kref *refcount)
+{
+       struct page *page, *tpage;
+       struct cifs_readdata *rdata = container_of(refcount,
+                                       struct cifs_readdata, refcount);
+
+       list_for_each_entry_safe(page, tpage, &rdata->pages, lru) {
+               list_del(&page->lru);
+               put_page(page);
+       }
+       cifs_readdata_release(refcount);
+}
+
+static int
+cifs_retry_async_readv(struct cifs_readdata *rdata)
+{
+       int rc;
+
+       do {
+               if (rdata->cfile->invalidHandle) {
+                       rc = cifs_reopen_file(rdata->cfile, true);
+                       if (rc != 0)
+                               continue;
+               }
+               rc = cifs_async_readv(rdata);
+       } while (rc == -EAGAIN);
+
+       return rc;
+}
+
+/**
+ * cifs_readdata_to_iov - copy data from pages in response to an iovec
+ * @rdata:     the readdata response with list of pages holding data
+ * @iov:       vector in which we should copy the data
+ * @nr_segs:   number of segments in vector
+ * @offset:    offset into file of the first iovec
+ * @copied:    used to return the amount of data copied to the iov
+ *
+ * This function copies data from a list of pages in a readdata response into
+ * an array of iovecs. It will first calculate where the data should go
+ * based on the info in the readdata and then copy the data into that spot.
+ */
+static ssize_t
+cifs_readdata_to_iov(struct cifs_readdata *rdata, const struct iovec *iov,
+                       unsigned long nr_segs, loff_t offset, ssize_t *copied)
+{
+       int rc = 0;
+       struct iov_iter ii;
+       size_t pos = rdata->offset - offset;
+       struct page *page, *tpage;
+       ssize_t remaining = rdata->bytes;
+       unsigned char *pdata;
+
+       /* set up iov_iter and advance to the correct offset */
+       iov_iter_init(&ii, iov, nr_segs, iov_length(iov, nr_segs), 0);
+       iov_iter_advance(&ii, pos);
+
+       *copied = 0;
+       list_for_each_entry_safe(page, tpage, &rdata->pages, lru) {
+               ssize_t copy;
+
+               /* copy a whole page or whatever's left */
+               copy = min_t(ssize_t, remaining, PAGE_SIZE);
+
+               /* ...but limit it to whatever space is left in the iov */
+               copy = min_t(ssize_t, copy, iov_iter_count(&ii));
+
+               /* go while there's data to be copied and no errors */
+               if (copy && !rc) {
+                       pdata = kmap(page);
+                       rc = memcpy_toiovecend(ii.iov, pdata, ii.iov_offset,
+                                               (int)copy);
+                       kunmap(page);
+                       if (!rc) {
+                               *copied += copy;
+                               remaining -= copy;
+                               iov_iter_advance(&ii, copy);
+                       }
+               }
+
+               list_del(&page->lru);
+               put_page(page);
+       }
+
+       return rc;
+}
+
+static void
+cifs_uncached_readv_complete(struct work_struct *work)
+{
+       struct cifs_readdata *rdata = container_of(work,
+                                               struct cifs_readdata, work);
+
+       /* if the result is non-zero then the pages weren't kmapped */
+       if (rdata->result == 0) {
+               struct page *page;
+
+               list_for_each_entry(page, &rdata->pages, lru)
+                       kunmap(page);
+       }
+
+       complete(&rdata->done);
+       kref_put(&rdata->refcount, cifs_uncached_readdata_release);
+}
+
+static int
+cifs_uncached_read_marshal_iov(struct cifs_readdata *rdata,
+                               unsigned int remaining)
+{
+       int len = 0;
+       struct page *page, *tpage;
+
+       rdata->nr_iov = 1;
+       list_for_each_entry_safe(page, tpage, &rdata->pages, lru) {
+               if (remaining >= PAGE_SIZE) {
+                       /* enough data to fill the page */
+                       rdata->iov[rdata->nr_iov].iov_base = kmap(page);
+                       rdata->iov[rdata->nr_iov].iov_len = PAGE_SIZE;
+                       cFYI(1, "%u: idx=%lu iov_base=%p iov_len=%zu",
+                               rdata->nr_iov, page->index,
+                               rdata->iov[rdata->nr_iov].iov_base,
+                               rdata->iov[rdata->nr_iov].iov_len);
+                       ++rdata->nr_iov;
+                       len += PAGE_SIZE;
+                       remaining -= PAGE_SIZE;
+               } else if (remaining > 0) {
+                       /* enough for partial page, fill and zero the rest */
+                       rdata->iov[rdata->nr_iov].iov_base = kmap(page);
+                       rdata->iov[rdata->nr_iov].iov_len = remaining;
+                       cFYI(1, "%u: idx=%lu iov_base=%p iov_len=%zu",
+                               rdata->nr_iov, page->index,
+                               rdata->iov[rdata->nr_iov].iov_base,
+                               rdata->iov[rdata->nr_iov].iov_len);
+                       memset(rdata->iov[rdata->nr_iov].iov_base + remaining,
+                               '\0', PAGE_SIZE - remaining);
+                       ++rdata->nr_iov;
+                       len += remaining;
+                       remaining = 0;
+               } else {
+                       /* no need to hold page hostage */
+                       list_del(&page->lru);
+                       put_page(page);
+               }
+       }
+
+       return len;
+}
+
 static ssize_t
 cifs_iovec_read(struct file *file, const struct iovec *iov,
                 unsigned long nr_segs, loff_t *poffset)
 {
-       int rc;
-       int xid;
-       ssize_t total_read;
-       unsigned int bytes_read = 0;
+       ssize_t rc;
        size_t len, cur_len;
-       int iov_offset = 0;
+       ssize_t total_read = 0;
+       loff_t offset = *poffset;
+       unsigned int npages;
        struct cifs_sb_info *cifs_sb;
-       struct cifs_tcon *pTcon;
+       struct cifs_tcon *tcon;
        struct cifsFileInfo *open_file;
-       struct smb_com_read_rsp *pSMBr;
-       struct cifs_io_parms io_parms;
-       char *read_data;
-       unsigned int rsize;
-       __u32 pid;
+       struct cifs_readdata *rdata, *tmp;
+       struct list_head rdata_list;
+       pid_t pid;
 
        if (!nr_segs)
                return 0;
@@ -2365,14 +2600,10 @@ cifs_iovec_read(struct file *file, const struct iovec *iov,
        if (!len)
                return 0;
 
-       xid = GetXid();
+       INIT_LIST_HEAD(&rdata_list);
        cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
-
-       /* FIXME: set up handlers for larger reads and/or convert to async */
-       rsize = min_t(unsigned int, cifs_sb->rsize, CIFSMaxBufSize);
-
        open_file = file->private_data;
-       pTcon = tlink_tcon(open_file->tlink);
+       tcon = tlink_tcon(open_file->tlink);
 
        if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD)
                pid = open_file->pid;
@@ -2382,56 +2613,78 @@ cifs_iovec_read(struct file *file, const struct iovec *iov,
        if ((file->f_flags & O_ACCMODE) == O_WRONLY)
                cFYI(1, "attempting read on write only file instance");
 
-       for (total_read = 0; total_read < len; total_read += bytes_read) {
-               cur_len = min_t(const size_t, len - total_read, rsize);
-               rc = -EAGAIN;
-               read_data = NULL;
+       do {
+               cur_len = min_t(const size_t, len - total_read, cifs_sb->rsize);
+               npages = DIV_ROUND_UP(cur_len, PAGE_SIZE);
 
-               while (rc == -EAGAIN) {
-                       int buf_type = CIFS_NO_BUFFER;
-                       if (open_file->invalidHandle) {
-                               rc = cifs_reopen_file(open_file, true);
-                               if (rc != 0)
-                                       break;
-                       }
-                       io_parms.netfid = open_file->netfid;
-                       io_parms.pid = pid;
-                       io_parms.tcon = pTcon;
-                       io_parms.offset = *poffset;
-                       io_parms.length = cur_len;
-                       rc = CIFSSMBRead(xid, &io_parms, &bytes_read,
-                                        &read_data, &buf_type);
-                       pSMBr = (struct smb_com_read_rsp *)read_data;
-                       if (read_data) {
-                               char *data_offset = read_data + 4 +
-                                               le16_to_cpu(pSMBr->DataOffset);
-                               if (memcpy_toiovecend(iov, data_offset,
-                                                     iov_offset, bytes_read))
-                                       rc = -EFAULT;
-                               if (buf_type == CIFS_SMALL_BUFFER)
-                                       cifs_small_buf_release(read_data);
-                               else if (buf_type == CIFS_LARGE_BUFFER)
-                                       cifs_buf_release(read_data);
-                               read_data = NULL;
-                               iov_offset += bytes_read;
-                       }
+               /* allocate a readdata struct */
+               rdata = cifs_readdata_alloc(npages,
+                                           cifs_uncached_readv_complete);
+               if (!rdata) {
+                       rc = -ENOMEM;
+                       goto error;
                }
 
-               if (rc || (bytes_read == 0)) {
-                       if (total_read) {
-                               break;
-                       } else {
-                               FreeXid(xid);
-                               return rc;
+               rc = cifs_read_allocate_pages(&rdata->pages, npages);
+               if (rc)
+                       goto error;
+
+               rdata->cfile = cifsFileInfo_get(open_file);
+               rdata->offset = offset;
+               rdata->bytes = cur_len;
+               rdata->pid = pid;
+               rdata->marshal_iov = cifs_uncached_read_marshal_iov;
+
+               rc = cifs_retry_async_readv(rdata);
+error:
+               if (rc) {
+                       kref_put(&rdata->refcount,
+                                cifs_uncached_readdata_release);
+                       break;
+               }
+
+               list_add_tail(&rdata->list, &rdata_list);
+               offset += cur_len;
+               len -= cur_len;
+       } while (len > 0);
+
+       /* if at least one read request send succeeded, then reset rc */
+       if (!list_empty(&rdata_list))
+               rc = 0;
+
+       /* the loop below should proceed in the order of increasing offsets */
+restart_loop:
+       list_for_each_entry_safe(rdata, tmp, &rdata_list, list) {
+               if (!rc) {
+                       ssize_t copied;
+
+                       /* FIXME: freezable sleep too? */
+                       rc = wait_for_completion_killable(&rdata->done);
+                       if (rc)
+                               rc = -EINTR;
+                       else if (rdata->result)
+                               rc = rdata->result;
+                       else {
+                               rc = cifs_readdata_to_iov(rdata, iov,
+                                                       nr_segs, *poffset,
+                                                       &copied);
+                               total_read += copied;
+                       }
+
+                       /* resend call if it's a retryable error */
+                       if (rc == -EAGAIN) {
+                               rc = cifs_retry_async_readv(rdata);
+                               goto restart_loop;
                        }
-               } else {
-                       cifs_stats_bytes_read(pTcon, bytes_read);
-                       *poffset += bytes_read;
                }
+               list_del_init(&rdata->list);
+               kref_put(&rdata->refcount, cifs_uncached_readdata_release);
        }
 
-       FreeXid(xid);
-       return total_read;
+       cifs_stats_bytes_read(tcon, total_read);
+       *poffset += total_read;
+
+       return total_read ? total_read : rc;
 }
 
 ssize_t cifs_user_readv(struct kiocb *iocb, const struct iovec *iov,
@@ -2606,6 +2859,100 @@ int cifs_file_mmap(struct file *file, struct vm_area_struct *vma)
        return rc;
 }
 
+static void
+cifs_readv_complete(struct work_struct *work)
+{
+       struct cifs_readdata *rdata = container_of(work,
+                                               struct cifs_readdata, work);
+       struct page *page, *tpage;
+
+       list_for_each_entry_safe(page, tpage, &rdata->pages, lru) {
+               list_del(&page->lru);
+               lru_cache_add_file(page);
+
+               if (rdata->result == 0) {
+                       kunmap(page);
+                       flush_dcache_page(page);
+                       SetPageUptodate(page);
+               }
+
+               unlock_page(page);
+
+               if (rdata->result == 0)
+                       cifs_readpage_to_fscache(rdata->mapping->host, page);
+
+               page_cache_release(page);
+       }
+       kref_put(&rdata->refcount, cifs_readdata_release);
+}
+
+static int
+cifs_readpages_marshal_iov(struct cifs_readdata *rdata, unsigned int remaining)
+{
+       int len = 0;
+       struct page *page, *tpage;
+       u64 eof;
+       pgoff_t eof_index;
+
+       /* determine the eof that the server (probably) has */
+       eof = CIFS_I(rdata->mapping->host)->server_eof;
+       eof_index = eof ? (eof - 1) >> PAGE_CACHE_SHIFT : 0;
+       cFYI(1, "eof=%llu eof_index=%lu", eof, eof_index);
+
+       rdata->nr_iov = 1;
+       list_for_each_entry_safe(page, tpage, &rdata->pages, lru) {
+               if (remaining >= PAGE_CACHE_SIZE) {
+                       /* enough data to fill the page */
+                       rdata->iov[rdata->nr_iov].iov_base = kmap(page);
+                       rdata->iov[rdata->nr_iov].iov_len = PAGE_CACHE_SIZE;
+                       cFYI(1, "%u: idx=%lu iov_base=%p iov_len=%zu",
+                               rdata->nr_iov, page->index,
+                               rdata->iov[rdata->nr_iov].iov_base,
+                               rdata->iov[rdata->nr_iov].iov_len);
+                       ++rdata->nr_iov;
+                       len += PAGE_CACHE_SIZE;
+                       remaining -= PAGE_CACHE_SIZE;
+               } else if (remaining > 0) {
+                       /* enough for partial page, fill and zero the rest */
+                       rdata->iov[rdata->nr_iov].iov_base = kmap(page);
+                       rdata->iov[rdata->nr_iov].iov_len = remaining;
+                       cFYI(1, "%u: idx=%lu iov_base=%p iov_len=%zu",
+                               rdata->nr_iov, page->index,
+                               rdata->iov[rdata->nr_iov].iov_base,
+                               rdata->iov[rdata->nr_iov].iov_len);
+                       memset(rdata->iov[rdata->nr_iov].iov_base + remaining,
+                               '\0', PAGE_CACHE_SIZE - remaining);
+                       ++rdata->nr_iov;
+                       len += remaining;
+                       remaining = 0;
+               } else if (page->index > eof_index) {
+                       /*
+                        * The VFS will not try to do readahead past the
+                        * i_size, but it's possible that we have outstanding
+                        * writes with gaps in the middle and the i_size hasn't
+                        * caught up yet. Populate those with zeroed out pages
+                        * to prevent the VFS from repeatedly attempting to
+                        * fill them until the writes are flushed.
+                        */
+                       zero_user(page, 0, PAGE_CACHE_SIZE);
+                       list_del(&page->lru);
+                       lru_cache_add_file(page);
+                       flush_dcache_page(page);
+                       SetPageUptodate(page);
+                       unlock_page(page);
+                       page_cache_release(page);
+               } else {
+                       /* no need to hold page hostage */
+                       list_del(&page->lru);
+                       lru_cache_add_file(page);
+                       unlock_page(page);
+                       page_cache_release(page);
+               }
+       }
+
+       return len;
+}
+
 static int cifs_readpages(struct file *file, struct address_space *mapping,
        struct list_head *page_list, unsigned num_pages)
 {
@@ -2708,7 +3055,7 @@ static int cifs_readpages(struct file *file, struct address_space *mapping,
                        nr_pages++;
                }
 
-               rdata = cifs_readdata_alloc(nr_pages);
+               rdata = cifs_readdata_alloc(nr_pages, cifs_readv_complete);
                if (!rdata) {
                        /* best to give up if we're out of mem */
                        list_for_each_entry_safe(page, tpage, &tmplist, lru) {
@@ -2722,24 +3069,16 @@ static int cifs_readpages(struct file *file, struct address_space *mapping,
                }
 
                spin_lock(&cifs_file_list_lock);
-               cifsFileInfo_get(open_file);
                spin_unlock(&cifs_file_list_lock);
-               rdata->cfile = open_file;
+               rdata->cfile = cifsFileInfo_get(open_file);
                rdata->mapping = mapping;
                rdata->offset = offset;
                rdata->bytes = bytes;
                rdata->pid = pid;
+               rdata->marshal_iov = cifs_readpages_marshal_iov;
                list_splice_init(&tmplist, &rdata->pages);
 
-               do {
-                       if (open_file->invalidHandle) {
-                               rc = cifs_reopen_file(open_file, true);
-                               if (rc != 0)
-                                       continue;
-                       }
-                       rc = cifs_async_readv(rdata);
-               } while (rc == -EAGAIN);
-
+               rc = cifs_retry_async_readv(rdata);
                if (rc != 0) {
                        list_for_each_entry_safe(page, tpage, &rdata->pages,
                                                 lru) {
@@ -2748,9 +3087,11 @@ static int cifs_readpages(struct file *file, struct address_space *mapping,
                                unlock_page(page);
                                page_cache_release(page);
                        }
-                       cifs_readdata_free(rdata);
+                       kref_put(&rdata->refcount, cifs_readdata_release);
                        break;
                }
+
+               kref_put(&rdata->refcount, cifs_readdata_release);
        }
 
        return rc;
index 4221b5e48a426af74b540105ec255026291e8c05..6d2667f0c98c3801d41960b784ff64f2899f2e33 100644 (file)
@@ -51,7 +51,15 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
        cifs_sb = CIFS_SB(inode->i_sb);
 
        switch (command) {
+               static bool warned = false;
                case CIFS_IOC_CHECKUMOUNT:
+                       if (!warned) {
+                               warned = true;
+                               cERROR(1, "the CIFS_IOC_CHECKMOUNT ioctl will "
+                                         "be deprecated in 3.7. Please "
+                                         "migrate away from the use of "
+                                         "umount.cifs");
+                       }
                        cFYI(1, "User unmount attempted");
                        if (cifs_sb->mnt_uid == current_uid())
                                rc = 0;
index c29d1aa2c54f30a76c25aa6ba843008b567ca96f..e2552d2b2e42c551fde38d91e3ead83c6cfccd47 100644 (file)
@@ -306,8 +306,6 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ ,
                const struct cifs_tcon *treeCon, int word_count
                /* length of fixed section (word count) in two byte units  */)
 {
-       struct list_head *temp_item;
-       struct cifs_ses *ses;
        char *temp = (char *) buffer;
 
        memset(temp, 0, 256); /* bigger than MAX_CIFS_HDR_SIZE */
@@ -337,51 +335,6 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ ,
                        /* Uid is not converted */
                        buffer->Uid = treeCon->ses->Suid;
                        buffer->Mid = GetNextMid(treeCon->ses->server);
-                       if (multiuser_mount != 0) {
-               /* For the multiuser case, there are few obvious technically  */
-               /* possible mechanisms to match the local linux user (uid)    */
-               /* to a valid remote smb user (smb_uid):                      */
-               /*      1) Query Winbind (or other local pam/nss daemon       */
-               /*        for userid/password/logon_domain or credential      */
-               /*      2) Query Winbind for uid to sid to username mapping   */
-               /*         and see if we have a matching password for existing*/
-               /*         session for that user perhas getting password by   */
-               /*         adding a new pam_cifs module that stores passwords */
-               /*         so that the cifs vfs can get at that for all logged*/
-               /*         on users                                           */
-               /*      3) (Which is the mechanism we have chosen)            */
-               /*         Search through sessions to the same server for a   */
-               /*         a match on the uid that was passed in on mount     */
-               /*         with the current processes uid (or euid?) and use  */
-               /*         that smb uid.   If no existing smb session for     */
-               /*         that uid found, use the default smb session ie     */
-               /*         the smb session for the volume mounted which is    */
-               /*         the same as would be used if the multiuser mount   */
-               /*         flag were disabled.  */
-
-               /*  BB Add support for establishing new tCon and SMB Session  */
-               /*      with userid/password pairs found on the smb session   */
-               /*      for other target tcp/ip addresses               BB    */
-                               if (current_fsuid() != treeCon->ses->linux_uid) {
-                                       cFYI(1, "Multiuser mode and UID "
-                                                "did not match tcon uid");
-                                       spin_lock(&cifs_tcp_ses_lock);
-                                       list_for_each(temp_item, &treeCon->ses->server->smb_ses_list) {
-                                               ses = list_entry(temp_item, struct cifs_ses, smb_ses_list);
-                                               if (ses->linux_uid == current_fsuid()) {
-                                                       if (ses->server == treeCon->ses->server) {
-                                                               cFYI(1, "found matching uid substitute right smb_uid");
-                                                               buffer->Uid = ses->Suid;
-                                                               break;
-                                                       } else {
-                               /* BB eventually call cifs_setup_session here */
-                                                               cFYI(1, "local UID found but no smb sess with this server exists");
-                                                       }
-                                               }
-                                       }
-                                       spin_unlock(&cifs_tcp_ses_lock);
-                               }
-                       }
                }
                if (treeCon->Flags & SMB_SHARE_IS_IN_DFS)
                        buffer->Flags2 |= SMBFLG2_DFS;
@@ -700,22 +653,3 @@ backup_cred(struct cifs_sb_info *cifs_sb)
 
        return false;
 }
-
-void
-cifs_add_credits(struct TCP_Server_Info *server, const unsigned int add)
-{
-       spin_lock(&server->req_lock);
-       server->credits += add;
-       server->in_flight--;
-       spin_unlock(&server->req_lock);
-       wake_up(&server->request_q);
-}
-
-void
-cifs_set_credits(struct TCP_Server_Info *server, const int val)
-{
-       spin_lock(&server->req_lock);
-       server->credits = val;
-       server->oplocks = val > 1 ? enable_oplocks : false;
-       spin_unlock(&server->req_lock);
-}
index e2bbc683e0184a736509b7a41337b2fda401e4a3..0a8224d1c4c5f2df8545f2c84f9e668feba2e0e9 100644 (file)
@@ -219,6 +219,7 @@ int get_symlink_reparse_path(char *full_path, struct cifs_sb_info *cifs_sb,
 
 static int initiate_cifs_search(const int xid, struct file *file)
 {
+       __u16 search_flags;
        int rc = 0;
        char *full_path = NULL;
        struct cifsFileInfo *cifsFile;
@@ -270,8 +271,12 @@ ffirst_retry:
                cifsFile->srch_inf.info_level = SMB_FIND_FILE_DIRECTORY_INFO;
        }
 
+       search_flags = CIFS_SEARCH_CLOSE_AT_END | CIFS_SEARCH_RETURN_RESUME;
+       if (backup_cred(cifs_sb))
+               search_flags |= CIFS_SEARCH_BACKUP_SEARCH;
+
        rc = CIFSFindFirst(xid, pTcon, full_path, cifs_sb->local_nls,
-               &cifsFile->netfid, &cifsFile->srch_inf,
+               &cifsFile->netfid, search_flags, &cifsFile->srch_inf,
                cifs_sb->mnt_cifs_flags &
                        CIFS_MOUNT_MAP_SPECIAL_CHR, CIFS_DIR_SEP(cifs_sb));
        if (rc == 0)
@@ -502,11 +507,13 @@ static int cifs_save_resume_key(const char *current_entry,
 static int find_cifs_entry(const int xid, struct cifs_tcon *pTcon,
        struct file *file, char **ppCurrentEntry, int *num_to_ret)
 {
+       __u16 search_flags;
        int rc = 0;
        int pos_in_buf = 0;
        loff_t first_entry_in_buffer;
        loff_t index_to_find = file->f_pos;
        struct cifsFileInfo *cifsFile = file->private_data;
+       struct cifs_sb_info *cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
        /* check if index in the buffer */
 
        if ((cifsFile == NULL) || (ppCurrentEntry == NULL) ||
@@ -560,10 +567,14 @@ static int find_cifs_entry(const int xid, struct cifs_tcon *pTcon,
                                                cifsFile);
        }
 
+       search_flags = CIFS_SEARCH_CLOSE_AT_END | CIFS_SEARCH_RETURN_RESUME;
+       if (backup_cred(cifs_sb))
+               search_flags |= CIFS_SEARCH_BACKUP_SEARCH;
+
        while ((index_to_find >= cifsFile->srch_inf.index_of_last_entry) &&
              (rc == 0) && !cifsFile->srch_inf.endOfSearch) {
                cFYI(1, "calling findnext2");
-               rc = CIFSFindNext(xid, pTcon, cifsFile->netfid,
+               rc = CIFSFindNext(xid, pTcon, cifsFile->netfid, search_flags,
                                  &cifsFile->srch_inf);
                /* FindFirst/Next set last_entry to NULL on malformed reply */
                if (cifsFile->srch_inf.last_entry)
diff --git a/fs/cifs/smb1ops.c b/fs/cifs/smb1ops.c
new file mode 100644 (file)
index 0000000..d9d615f
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ *  SMB1 (CIFS) version specific operations
+ *
+ *  Copyright (c) 2012, Jeff Layton <jlayton@redhat.com>
+ *
+ *  This library is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License v2 as published
+ *  by the Free Software Foundation.
+ *
+ *  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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "cifsglob.h"
+#include "cifsproto.h"
+#include "cifs_debug.h"
+#include "cifspdu.h"
+
+/*
+ * An NT cancel request header looks just like the original request except:
+ *
+ * The Command is SMB_COM_NT_CANCEL
+ * The WordCount is zeroed out
+ * The ByteCount is zeroed out
+ *
+ * This function mangles an existing request buffer into a
+ * SMB_COM_NT_CANCEL request and then sends it.
+ */
+static int
+send_nt_cancel(struct TCP_Server_Info *server, void *buf,
+              struct mid_q_entry *mid)
+{
+       int rc = 0;
+       struct smb_hdr *in_buf = (struct smb_hdr *)buf;
+
+       /* -4 for RFC1001 length and +2 for BCC field */
+       in_buf->smb_buf_length = cpu_to_be32(sizeof(struct smb_hdr) - 4  + 2);
+       in_buf->Command = SMB_COM_NT_CANCEL;
+       in_buf->WordCount = 0;
+       put_bcc(0, in_buf);
+
+       mutex_lock(&server->srv_mutex);
+       rc = cifs_sign_smb(in_buf, server, &mid->sequence_number);
+       if (rc) {
+               mutex_unlock(&server->srv_mutex);
+               return rc;
+       }
+       rc = smb_send(server, in_buf, be32_to_cpu(in_buf->smb_buf_length));
+       mutex_unlock(&server->srv_mutex);
+
+       cFYI(1, "issued NT_CANCEL for mid %u, rc = %d",
+               in_buf->Mid, rc);
+
+       return rc;
+}
+
+static bool
+cifs_compare_fids(struct cifsFileInfo *ob1, struct cifsFileInfo *ob2)
+{
+       return ob1->netfid == ob2->netfid;
+}
+
+static unsigned int
+cifs_read_data_offset(char *buf)
+{
+       READ_RSP *rsp = (READ_RSP *)buf;
+       return le16_to_cpu(rsp->DataOffset);
+}
+
+static unsigned int
+cifs_read_data_length(char *buf)
+{
+       READ_RSP *rsp = (READ_RSP *)buf;
+       return (le16_to_cpu(rsp->DataLengthHigh) << 16) +
+              le16_to_cpu(rsp->DataLength);
+}
+
+static struct mid_q_entry *
+cifs_find_mid(struct TCP_Server_Info *server, char *buffer)
+{
+       struct smb_hdr *buf = (struct smb_hdr *)buffer;
+       struct mid_q_entry *mid;
+
+       spin_lock(&GlobalMid_Lock);
+       list_for_each_entry(mid, &server->pending_mid_q, qhead) {
+               if (mid->mid == buf->Mid &&
+                   mid->mid_state == MID_REQUEST_SUBMITTED &&
+                   le16_to_cpu(mid->command) == buf->Command) {
+                       spin_unlock(&GlobalMid_Lock);
+                       return mid;
+               }
+       }
+       spin_unlock(&GlobalMid_Lock);
+       return NULL;
+}
+
+static void
+cifs_add_credits(struct TCP_Server_Info *server, const unsigned int add)
+{
+       spin_lock(&server->req_lock);
+       server->credits += add;
+       server->in_flight--;
+       spin_unlock(&server->req_lock);
+       wake_up(&server->request_q);
+}
+
+static void
+cifs_set_credits(struct TCP_Server_Info *server, const int val)
+{
+       spin_lock(&server->req_lock);
+       server->credits = val;
+       server->oplocks = val > 1 ? enable_oplocks : false;
+       spin_unlock(&server->req_lock);
+}
+
+static int *
+cifs_get_credits_field(struct TCP_Server_Info *server)
+{
+       return &server->credits;
+}
+
+struct smb_version_operations smb1_operations = {
+       .send_cancel = send_nt_cancel,
+       .compare_fids = cifs_compare_fids,
+       .setup_request = cifs_setup_request,
+       .check_receive = cifs_check_receive,
+       .add_credits = cifs_add_credits,
+       .set_credits = cifs_set_credits,
+       .get_credits_field = cifs_get_credits_field,
+       .read_data_offset = cifs_read_data_offset,
+       .read_data_length = cifs_read_data_length,
+       .map_error = map_smb_to_linux_error,
+       .find_mid = cifs_find_mid,
+       .check_message = checkSMB,
+       .dump_detail = cifs_dump_detail,
+       .is_oplock_break = is_valid_oplock_break,
+};
+
+struct smb_version_values smb1_values = {
+       .version_string = SMB1_VERSION_STRING,
+       .large_lock_type = LOCKING_ANDX_LARGE_FILES,
+       .exclusive_lock_type = 0,
+       .shared_lock_type = LOCKING_ANDX_SHARED_LOCK,
+       .unlock_lock_type = 0,
+       .header_size = sizeof(struct smb_hdr),
+       .max_header_size = MAX_CIFS_HDR_SIZE,
+       .read_rsp_size = sizeof(READ_RSP),
+};
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
new file mode 100644 (file)
index 0000000..f065e89
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ *  SMB2 version specific operations
+ *
+ *  Copyright (c) 2012, Jeff Layton <jlayton@redhat.com>
+ *
+ *  This library is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License v2 as published
+ *  by the Free Software Foundation.
+ *
+ *  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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "cifsglob.h"
+
+struct smb_version_operations smb21_operations = {
+};
+
+struct smb_version_values smb21_values = {
+       .version_string = SMB21_VERSION_STRING,
+};
index 0961336513d5334d99084eee82addeb1b582ffbf..1b36ffe6a47b5f6888519fea797aebdbe3a9da97 100644 (file)
@@ -304,7 +304,8 @@ wait_for_free_credits(struct TCP_Server_Info *server, const int optype,
 static int
 wait_for_free_request(struct TCP_Server_Info *server, const int optype)
 {
-       return wait_for_free_credits(server, optype, get_credits_field(server));
+       return wait_for_free_credits(server, optype,
+                                    server->ops->get_credits_field(server));
 }
 
 static int allocate_mid(struct cifs_ses *ses, struct smb_hdr *in_buf,
@@ -396,7 +397,7 @@ cifs_call_async(struct TCP_Server_Info *server, struct kvec *iov,
        rc = cifs_setup_async_request(server, iov, nvec, &mid);
        if (rc) {
                mutex_unlock(&server->srv_mutex);
-               cifs_add_credits(server, 1);
+               add_credits(server, 1);
                wake_up(&server->request_q);
                return rc;
        }
@@ -418,7 +419,7 @@ cifs_call_async(struct TCP_Server_Info *server, struct kvec *iov,
        return rc;
 out_err:
        delete_mid(mid);
-       cifs_add_credits(server, 1);
+       add_credits(server, 1);
        wake_up(&server->request_q);
        return rc;
 }
@@ -483,41 +484,11 @@ cifs_sync_mid_result(struct mid_q_entry *mid, struct TCP_Server_Info *server)
        return rc;
 }
 
-/*
- * An NT cancel request header looks just like the original request except:
- *
- * The Command is SMB_COM_NT_CANCEL
- * The WordCount is zeroed out
- * The ByteCount is zeroed out
- *
- * This function mangles an existing request buffer into a
- * SMB_COM_NT_CANCEL request and then sends it.
- */
-static int
-send_nt_cancel(struct TCP_Server_Info *server, struct smb_hdr *in_buf,
-               struct mid_q_entry *mid)
+static inline int
+send_cancel(struct TCP_Server_Info *server, void *buf, struct mid_q_entry *mid)
 {
-       int rc = 0;
-
-       /* -4 for RFC1001 length and +2 for BCC field */
-       in_buf->smb_buf_length = cpu_to_be32(sizeof(struct smb_hdr) - 4  + 2);
-       in_buf->Command = SMB_COM_NT_CANCEL;
-       in_buf->WordCount = 0;
-       put_bcc(0, in_buf);
-
-       mutex_lock(&server->srv_mutex);
-       rc = cifs_sign_smb(in_buf, server, &mid->sequence_number);
-       if (rc) {
-               mutex_unlock(&server->srv_mutex);
-               return rc;
-       }
-       rc = smb_send(server, in_buf, be32_to_cpu(in_buf->smb_buf_length));
-       mutex_unlock(&server->srv_mutex);
-
-       cFYI(1, "issued NT_CANCEL for mid %u, rc = %d",
-               in_buf->Mid, rc);
-
-       return rc;
+       return server->ops->send_cancel ?
+                               server->ops->send_cancel(server, buf, mid) : 0;
 }
 
 int
@@ -544,7 +515,7 @@ cifs_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server,
        return map_smb_to_linux_error(mid->resp_buf, log_error);
 }
 
-static int
+int
 cifs_setup_request(struct cifs_ses *ses, struct kvec *iov,
                   unsigned int nvec, struct mid_q_entry **ret_mid)
 {
@@ -607,12 +578,12 @@ SendReceive2(const unsigned int xid, struct cifs_ses *ses,
 
        mutex_lock(&ses->server->srv_mutex);
 
-       rc = cifs_setup_request(ses, iov, n_vec, &midQ);
+       rc = ses->server->ops->setup_request(ses, iov, n_vec, &midQ);
        if (rc) {
                mutex_unlock(&ses->server->srv_mutex);
                cifs_small_buf_release(buf);
                /* Update # of requests on wire to server */
-               cifs_add_credits(ses->server, 1);
+               add_credits(ses->server, 1);
                return rc;
        }
 
@@ -636,13 +607,13 @@ SendReceive2(const unsigned int xid, struct cifs_ses *ses,
 
        rc = wait_for_response(ses->server, midQ);
        if (rc != 0) {
-               send_nt_cancel(ses->server, (struct smb_hdr *)buf, midQ);
+               send_cancel(ses->server, buf, midQ);
                spin_lock(&GlobalMid_Lock);
                if (midQ->mid_state == MID_REQUEST_SUBMITTED) {
                        midQ->callback = DeleteMidQEntry;
                        spin_unlock(&GlobalMid_Lock);
                        cifs_small_buf_release(buf);
-                       cifs_add_credits(ses->server, 1);
+                       add_credits(ses->server, 1);
                        return rc;
                }
                spin_unlock(&GlobalMid_Lock);
@@ -652,7 +623,7 @@ SendReceive2(const unsigned int xid, struct cifs_ses *ses,
 
        rc = cifs_sync_mid_result(midQ, ses->server);
        if (rc != 0) {
-               cifs_add_credits(ses->server, 1);
+               add_credits(ses->server, 1);
                return rc;
        }
 
@@ -670,14 +641,15 @@ SendReceive2(const unsigned int xid, struct cifs_ses *ses,
        else
                *pRespBufType = CIFS_SMALL_BUFFER;
 
-       rc = cifs_check_receive(midQ, ses->server, flags & CIFS_LOG_ERROR);
+       rc = ses->server->ops->check_receive(midQ, ses->server,
+                                            flags & CIFS_LOG_ERROR);
 
        /* mark it so buf will not be freed by delete_mid */
        if ((flags & CIFS_NO_RESP) == 0)
                midQ->resp_buf = NULL;
 out:
        delete_mid(midQ);
-       cifs_add_credits(ses->server, 1);
+       add_credits(ses->server, 1);
 
        return rc;
 }
@@ -727,7 +699,7 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses,
        if (rc) {
                mutex_unlock(&ses->server->srv_mutex);
                /* Update # of requests on wire to server */
-               cifs_add_credits(ses->server, 1);
+               add_credits(ses->server, 1);
                return rc;
        }
 
@@ -753,13 +725,13 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses,
 
        rc = wait_for_response(ses->server, midQ);
        if (rc != 0) {
-               send_nt_cancel(ses->server, in_buf, midQ);
+               send_cancel(ses->server, in_buf, midQ);
                spin_lock(&GlobalMid_Lock);
                if (midQ->mid_state == MID_REQUEST_SUBMITTED) {
                        /* no longer considered to be "in-flight" */
                        midQ->callback = DeleteMidQEntry;
                        spin_unlock(&GlobalMid_Lock);
-                       cifs_add_credits(ses->server, 1);
+                       add_credits(ses->server, 1);
                        return rc;
                }
                spin_unlock(&GlobalMid_Lock);
@@ -767,7 +739,7 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses,
 
        rc = cifs_sync_mid_result(midQ, ses->server);
        if (rc != 0) {
-               cifs_add_credits(ses->server, 1);
+               add_credits(ses->server, 1);
                return rc;
        }
 
@@ -783,7 +755,7 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses,
        rc = cifs_check_receive(midQ, ses->server, 0);
 out:
        delete_mid(midQ);
-       cifs_add_credits(ses->server, 1);
+       add_credits(ses->server, 1);
 
        return rc;
 }
@@ -898,7 +870,7 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon,
                if (in_buf->Command == SMB_COM_TRANSACTION2) {
                        /* POSIX lock. We send a NT_CANCEL SMB to cause the
                           blocking lock to return. */
-                       rc = send_nt_cancel(ses->server, in_buf, midQ);
+                       rc = send_cancel(ses->server, in_buf, midQ);
                        if (rc) {
                                delete_mid(midQ);
                                return rc;
@@ -919,7 +891,7 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon,
 
                rc = wait_for_response(ses->server, midQ);
                if (rc) {
-                       send_nt_cancel(ses->server, in_buf, midQ);
+                       send_cancel(ses->server, in_buf, midQ);
                        spin_lock(&GlobalMid_Lock);
                        if (midQ->mid_state == MID_REQUEST_SUBMITTED) {
                                /* no longer considered to be "in-flight" */
index 0781e619a62a48babf8969fa5453994d784ba13c..6161255fac45648efdfe437d9d880d390268d14f 100644 (file)
@@ -532,7 +532,7 @@ out:
 ssize_t compat_rw_copy_check_uvector(int type,
                const struct compat_iovec __user *uvector, unsigned long nr_segs,
                unsigned long fast_segs, struct iovec *fast_pointer,
-               struct iovec **ret_pointer, int check_access)
+               struct iovec **ret_pointer)
 {
        compat_ssize_t tot_len;
        struct iovec *iov = *ret_pointer = fast_pointer;
@@ -579,7 +579,7 @@ ssize_t compat_rw_copy_check_uvector(int type,
                }
                if (len < 0)    /* size_t not fitting in compat_ssize_t .. */
                        goto out;
-               if (check_access &&
+               if (type >= 0 &&
                    !access_ok(vrfy_dir(type), compat_ptr(buf), len)) {
                        ret = -EFAULT;
                        goto out;
@@ -871,12 +871,12 @@ asmlinkage long compat_sys_old_readdir(unsigned int fd,
 {
        int error;
        struct file *file;
+       int fput_needed;
        struct compat_readdir_callback buf;
 
-       error = -EBADF;
-       file = fget(fd);
+       file = fget_light(fd, &fput_needed);
        if (!file)
-               goto out;
+               return -EBADF;
 
        buf.result = 0;
        buf.dirent = dirent;
@@ -885,8 +885,7 @@ asmlinkage long compat_sys_old_readdir(unsigned int fd,
        if (buf.result)
                error = buf.result;
 
-       fput(file);
-out:
+       fput_light(file, fput_needed);
        return error;
 }
 
@@ -953,16 +952,15 @@ asmlinkage long compat_sys_getdents(unsigned int fd,
        struct file * file;
        struct compat_linux_dirent __user * lastdirent;
        struct compat_getdents_callback buf;
+       int fput_needed;
        int error;
 
-       error = -EFAULT;
        if (!access_ok(VERIFY_WRITE, dirent, count))
-               goto out;
+               return -EFAULT;
 
-       error = -EBADF;
-       file = fget(fd);
+       file = fget_light(fd, &fput_needed);
        if (!file)
-               goto out;
+               return -EBADF;
 
        buf.current_dir = dirent;
        buf.previous = NULL;
@@ -979,8 +977,7 @@ asmlinkage long compat_sys_getdents(unsigned int fd,
                else
                        error = count - buf.count;
        }
-       fput(file);
-out:
+       fput_light(file, fput_needed);
        return error;
 }
 
@@ -1041,16 +1038,15 @@ asmlinkage long compat_sys_getdents64(unsigned int fd,
        struct file * file;
        struct linux_dirent64 __user * lastdirent;
        struct compat_getdents_callback64 buf;
+       int fput_needed;
        int error;
 
-       error = -EFAULT;
        if (!access_ok(VERIFY_WRITE, dirent, count))
-               goto out;
+               return -EFAULT;
 
-       error = -EBADF;
-       file = fget(fd);
+       file = fget_light(fd, &fput_needed);
        if (!file)
-               goto out;
+               return -EBADF;
 
        buf.current_dir = dirent;
        buf.previous = NULL;
@@ -1068,8 +1064,7 @@ asmlinkage long compat_sys_getdents64(unsigned int fd,
                else
                        error = count - buf.count;
        }
-       fput(file);
-out:
+       fput_light(file, fput_needed);
        return error;
 }
 #endif /* ! __ARCH_OMIT_COMPAT_SYS_GETDENTS64 */
@@ -1094,7 +1089,7 @@ static ssize_t compat_do_readv_writev(int type, struct file *file,
                goto out;
 
        tot_len = compat_rw_copy_check_uvector(type, uvector, nr_segs,
-                                              UIO_FASTIOV, iovstack, &iov, 1);
+                                              UIO_FASTIOV, iovstack, &iov);
        if (tot_len == 0) {
                ret = 0;
                goto out;
@@ -1547,7 +1542,6 @@ asmlinkage long compat_sys_old_select(struct compat_sel_arg_struct __user *arg)
                                 compat_ptr(a.exp), compat_ptr(a.tvp));
 }
 
-#ifdef HAVE_SET_RESTORE_SIGMASK
 static long do_compat_pselect(int n, compat_ulong_t __user *inp,
        compat_ulong_t __user *outp, compat_ulong_t __user *exp,
        struct compat_timespec __user *tsp, compat_sigset_t __user *sigmask,
@@ -1670,11 +1664,9 @@ asmlinkage long compat_sys_ppoll(struct pollfd __user *ufds,
 
        return ret;
 }
-#endif /* HAVE_SET_RESTORE_SIGMASK */
 
 #ifdef CONFIG_EPOLL
 
-#ifdef HAVE_SET_RESTORE_SIGMASK
 asmlinkage long compat_sys_epoll_pwait(int epfd,
                        struct compat_epoll_event __user *events,
                        int maxevents, int timeout,
@@ -1718,7 +1710,6 @@ asmlinkage long compat_sys_epoll_pwait(int epfd,
 
        return err;
 }
-#endif /* HAVE_SET_RESTORE_SIGMASK */
 
 #endif /* CONFIG_EPOLL */
 
index 4435d8b329044da3b48c83dfe555409464797d0d..85c9e2bff8e65126eaca755e14d1ee4a15a27170 100644 (file)
@@ -683,8 +683,6 @@ EXPORT_SYMBOL(dget_parent);
 /**
  * d_find_alias - grab a hashed alias of inode
  * @inode: inode in question
- * @want_discon:  flag, used by d_splice_alias, to request
- *          that only a DISCONNECTED alias be returned.
  *
  * If inode has a hashed alias, or is a directory and has any alias,
  * acquire the reference to alias and return it. Otherwise return NULL.
@@ -693,10 +691,9 @@ EXPORT_SYMBOL(dget_parent);
  * of a filesystem.
  *
  * If the inode has an IS_ROOT, DCACHE_DISCONNECTED alias, then prefer
- * any other hashed alias over that one unless @want_discon is set,
- * in which case only return an IS_ROOT, DCACHE_DISCONNECTED alias.
+ * any other hashed alias over that.
  */
-static struct dentry *__d_find_alias(struct inode *inode, int want_discon)
+static struct dentry *__d_find_alias(struct inode *inode)
 {
        struct dentry *alias, *discon_alias;
 
@@ -708,7 +705,7 @@ again:
                        if (IS_ROOT(alias) &&
                            (alias->d_flags & DCACHE_DISCONNECTED)) {
                                discon_alias = alias;
-                       } else if (!want_discon) {
+                       } else {
                                __dget_dlock(alias);
                                spin_unlock(&alias->d_lock);
                                return alias;
@@ -739,7 +736,7 @@ struct dentry *d_find_alias(struct inode *inode)
 
        if (!list_empty(&inode->i_dentry)) {
                spin_lock(&inode->i_lock);
-               de = __d_find_alias(inode, 0);
+               de = __d_find_alias(inode);
                spin_unlock(&inode->i_lock);
        }
        return de;
@@ -1650,9 +1647,8 @@ struct dentry *d_splice_alias(struct inode *inode, struct dentry *dentry)
 
        if (inode && S_ISDIR(inode->i_mode)) {
                spin_lock(&inode->i_lock);
-               new = __d_find_alias(inode, 1);
+               new = __d_find_any_alias(inode);
                if (new) {
-                       BUG_ON(!(new->d_flags & DCACHE_DISCONNECTED));
                        spin_unlock(&inode->i_lock);
                        security_d_instantiate(new, inode);
                        d_move(new, dentry);
@@ -2482,7 +2478,7 @@ struct dentry *d_materialise_unique(struct dentry *dentry, struct inode *inode)
                struct dentry *alias;
 
                /* Does an aliased dentry already exist? */
-               alias = __d_find_alias(inode, 0);
+               alias = __d_find_alias(inode);
                if (alias) {
                        actual = alias;
                        write_seqlock(&rename_lock);
@@ -2575,7 +2571,7 @@ static int prepend_path(const struct path *path,
        bool slash = false;
        int error = 0;
 
-       br_read_lock(vfsmount_lock);
+       br_read_lock(&vfsmount_lock);
        while (dentry != root->dentry || vfsmnt != root->mnt) {
                struct dentry * parent;
 
@@ -2606,7 +2602,7 @@ static int prepend_path(const struct path *path,
                error = prepend(buffer, buflen, "/", 1);
 
 out:
-       br_read_unlock(vfsmount_lock);
+       br_read_unlock(&vfsmount_lock);
        return error;
 
 global_root:
index f4aadd15b61376ee97a4a7bababe2eb659850820..0c85fae37666db4b18fb2bf9ebf692ae04bcbf40 100644 (file)
@@ -145,50 +145,6 @@ struct dio {
 
 static struct kmem_cache *dio_cache __read_mostly;
 
-static void __inode_dio_wait(struct inode *inode)
-{
-       wait_queue_head_t *wq = bit_waitqueue(&inode->i_state, __I_DIO_WAKEUP);
-       DEFINE_WAIT_BIT(q, &inode->i_state, __I_DIO_WAKEUP);
-
-       do {
-               prepare_to_wait(wq, &q.wait, TASK_UNINTERRUPTIBLE);
-               if (atomic_read(&inode->i_dio_count))
-                       schedule();
-       } while (atomic_read(&inode->i_dio_count));
-       finish_wait(wq, &q.wait);
-}
-
-/**
- * inode_dio_wait - wait for outstanding DIO requests to finish
- * @inode: inode to wait for
- *
- * Waits for all pending direct I/O requests to finish so that we can
- * proceed with a truncate or equivalent operation.
- *
- * Must be called under a lock that serializes taking new references
- * to i_dio_count, usually by inode->i_mutex.
- */
-void inode_dio_wait(struct inode *inode)
-{
-       if (atomic_read(&inode->i_dio_count))
-               __inode_dio_wait(inode);
-}
-EXPORT_SYMBOL(inode_dio_wait);
-
-/*
- * inode_dio_done - signal finish of a direct I/O requests
- * @inode: inode the direct I/O happens on
- *
- * This is called once we've finished processing a direct I/O request,
- * and is used to wake up callers waiting for direct I/O to be quiesced.
- */
-void inode_dio_done(struct inode *inode)
-{
-       if (atomic_dec_and_test(&inode->i_dio_count))
-               wake_up_bit(&inode->i_state, __I_DIO_WAKEUP);
-}
-EXPORT_SYMBOL(inode_dio_done);
-
 /*
  * How many pages are in the queue?
  */
index ab35b113003b900ad3592217d64e6cef82fe8f9d..a07441a0a8789a9ee1e43f5be0d2b43ec3ee04e8 100644 (file)
@@ -660,11 +660,10 @@ static int ecryptfs_readlink_lower(struct dentry *dentry, char **buf,
 {
        struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry);
        char *lower_buf;
-       size_t lower_bufsiz = PATH_MAX;
        mm_segment_t old_fs;
        int rc;
 
-       lower_buf = kmalloc(lower_bufsiz, GFP_KERNEL);
+       lower_buf = kmalloc(PATH_MAX, GFP_KERNEL);
        if (!lower_buf) {
                rc = -ENOMEM;
                goto out;
@@ -673,58 +672,29 @@ static int ecryptfs_readlink_lower(struct dentry *dentry, char **buf,
        set_fs(get_ds());
        rc = lower_dentry->d_inode->i_op->readlink(lower_dentry,
                                                   (char __user *)lower_buf,
-                                                  lower_bufsiz);
+                                                  PATH_MAX);
        set_fs(old_fs);
        if (rc < 0)
                goto out;
-       lower_bufsiz = rc;
        rc = ecryptfs_decode_and_decrypt_filename(buf, bufsiz, dentry,
-                                                 lower_buf, lower_bufsiz);
+                                                 lower_buf, rc);
 out:
        kfree(lower_buf);
        return rc;
 }
 
-static int
-ecryptfs_readlink(struct dentry *dentry, char __user *buf, int bufsiz)
+static void *ecryptfs_follow_link(struct dentry *dentry, struct nameidata *nd)
 {
-       char *kbuf;
-       size_t kbufsiz, copied;
+       char *buf;
+       size_t len = PATH_MAX;
        int rc;
 
-       rc = ecryptfs_readlink_lower(dentry, &kbuf, &kbufsiz);
+       rc = ecryptfs_readlink_lower(dentry, &buf, &len);
        if (rc)
                goto out;
-       copied = min_t(size_t, bufsiz, kbufsiz);
-       rc = copy_to_user(buf, kbuf, copied) ? -EFAULT : copied;
-       kfree(kbuf);
        fsstack_copy_attr_atime(dentry->d_inode,
                                ecryptfs_dentry_to_lower(dentry)->d_inode);
-out:
-       return rc;
-}
-
-static void *ecryptfs_follow_link(struct dentry *dentry, struct nameidata *nd)
-{
-       char *buf;
-       int len = PAGE_SIZE, rc;
-       mm_segment_t old_fs;
-
-       /* Released in ecryptfs_put_link(); only release here on error */
-       buf = kmalloc(len, GFP_KERNEL);
-       if (!buf) {
-               buf = ERR_PTR(-ENOMEM);
-               goto out;
-       }
-       old_fs = get_fs();
-       set_fs(get_ds());
-       rc = dentry->d_inode->i_op->readlink(dentry, (char __user *)buf, len);
-       set_fs(old_fs);
-       if (rc < 0) {
-               kfree(buf);
-               buf = ERR_PTR(rc);
-       } else
-               buf[rc] = '\0';
+       buf[len] = '\0';
 out:
        nd_set_link(nd, buf);
        return NULL;
@@ -1153,7 +1123,7 @@ out:
 }
 
 const struct inode_operations ecryptfs_symlink_iops = {
-       .readlink = ecryptfs_readlink,
+       .readlink = generic_readlink,
        .follow_link = ecryptfs_follow_link,
        .put_link = ecryptfs_put_link,
        .permission = ecryptfs_permission,
index dba15fecf23e376f12ad2cb367424cf202d3a819..d81b9f654086d1cdb3899767cf6e1e5bf05e9f6e 100644 (file)
@@ -46,20 +46,16 @@ struct eventfd_ctx {
  * value, and we signal this as overflow condition by returining a POLLERR
  * to poll(2).
  *
- * Returns @n in case of success, a non-negative number lower than @n in case
- * of overflow, or the following error codes:
- *
- * -EINVAL    : The value of @n is negative.
+ * Returns the amount by which the counter was incrememnted.  This will be less
+ * than @n if the counter has overflowed.
  */
-int eventfd_signal(struct eventfd_ctx *ctx, int n)
+__u64 eventfd_signal(struct eventfd_ctx *ctx, __u64 n)
 {
        unsigned long flags;
 
-       if (n < 0)
-               return -EINVAL;
        spin_lock_irqsave(&ctx->wqh.lock, flags);
        if (ULLONG_MAX - ctx->count < n)
-               n = (int) (ULLONG_MAX - ctx->count);
+               n = ULLONG_MAX - ctx->count;
        ctx->count += n;
        if (waitqueue_active(&ctx->wqh))
                wake_up_locked_poll(&ctx->wqh, POLLIN);
index 079d1be65ba9e61e6f0452ecffc0cd23a6c3987d..74598f67efebb85e71204a832c929dd20ea96771 100644 (file)
@@ -1853,8 +1853,6 @@ error_return:
        return error;
 }
 
-#ifdef HAVE_SET_RESTORE_SIGMASK
-
 /*
  * Implement the event wait interface for the eventpoll file. It is the kernel
  * part of the user space epoll_pwait(2).
@@ -1899,8 +1897,6 @@ SYSCALL_DEFINE6(epoll_pwait, int, epfd, struct epoll_event __user *, events,
        return error;
 }
 
-#endif /* HAVE_SET_RESTORE_SIGMASK */
-
 static int __init eventpoll_init(void)
 {
        struct sysinfo si;
index 52c9e2ff6e6bd8b6f763e56ceafda431731cea9b..a79786a8d2c88d5b6c580859ef12496f43b4b0f4 100644 (file)
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -280,10 +280,6 @@ static int __bprm_mm_init(struct linux_binprm *bprm)
        vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);
        INIT_LIST_HEAD(&vma->anon_vma_chain);
 
-       err = security_file_mmap(NULL, 0, 0, 0, vma->vm_start, 1);
-       if (err)
-               goto err;
-
        err = insert_vm_struct(mm, vma);
        if (err)
                goto err;
index b05acb7961355dfb680e49f3145a11065f6ac851..b0201ca6e9c6e0b7837917420bb3dfe1dc06b88f 100644 (file)
@@ -304,24 +304,23 @@ out:
 
 /**
  * export_encode_fh - default export_operations->encode_fh function
- * @dentry:  the dentry to encode
+ * @inode:   the object to encode
  * @fh:      where to store the file handle fragment
  * @max_len: maximum length to store there
- * @connectable: whether to store parent information
+ * @parent:  parent directory inode, if wanted
  *
  * This default encode_fh function assumes that the 32 inode number
  * is suitable for locating an inode, and that the generation number
  * can be used to check that it is still valid.  It places them in the
  * filehandle fragment where export_decode_fh expects to find them.
  */
-static int export_encode_fh(struct dentry *dentry, struct fid *fid,
-               int *max_len, int connectable)
+static int export_encode_fh(struct inode *inode, struct fid *fid,
+               int *max_len, struct inode *parent)
 {
-       struct inode * inode = dentry->d_inode;
        int len = *max_len;
        int type = FILEID_INO32_GEN;
 
-       if (connectable && (len < 4)) {
+       if (parent && (len < 4)) {
                *max_len = 4;
                return 255;
        } else if (len < 2) {
@@ -332,14 +331,9 @@ static int export_encode_fh(struct dentry *dentry, struct fid *fid,
        len = 2;
        fid->i32.ino = inode->i_ino;
        fid->i32.gen = inode->i_generation;
-       if (connectable && !S_ISDIR(inode->i_mode)) {
-               struct inode *parent;
-
-               spin_lock(&dentry->d_lock);
-               parent = dentry->d_parent->d_inode;
+       if (parent) {
                fid->i32.parent_ino = parent->i_ino;
                fid->i32.parent_gen = parent->i_generation;
-               spin_unlock(&dentry->d_lock);
                len = 4;
                type = FILEID_INO32_GEN_PARENT;
        }
@@ -352,11 +346,22 @@ int exportfs_encode_fh(struct dentry *dentry, struct fid *fid, int *max_len,
 {
        const struct export_operations *nop = dentry->d_sb->s_export_op;
        int error;
+       struct dentry *p = NULL;
+       struct inode *inode = dentry->d_inode, *parent = NULL;
 
+       if (connectable && !S_ISDIR(inode->i_mode)) {
+               p = dget_parent(dentry);
+               /*
+                * note that while p might've ceased to be our parent already,
+                * it's still pinned by and still positive.
+                */
+               parent = p->d_inode;
+       }
        if (nop->encode_fh)
-               error = nop->encode_fh(dentry, fid->raw, max_len, connectable);
+               error = nop->encode_fh(inode, fid->raw, max_len, parent);
        else
-               error = export_encode_fh(dentry, fid, max_len, connectable);
+               error = export_encode_fh(inode, fid, max_len, parent);
+       dput(p);
 
        return error;
 }
index 9ed1bb1f319f381b700a6386a4d8d068d04e0fdf..c22f17021b6eee7ca942a3525eb9f4fd23de6011 100644 (file)
@@ -2,6 +2,8 @@ config EXT4_FS
        tristate "The Extended 4 (ext4) filesystem"
        select JBD2
        select CRC16
+       select CRYPTO
+       select CRYPTO_CRC32C
        help
          This is the next generation of the ext3 filesystem.
 
index c45c41129a35b7346463e0f18e847b78a52e0426..99b6324290db916466d8b5c0633e9fa216d21798 100644 (file)
@@ -168,12 +168,14 @@ void ext4_init_block_bitmap(struct super_block *sb, struct buffer_head *bh,
 
        /* If checksum is bad mark all blocks used to prevent allocation
         * essentially implementing a per-group read-only flag. */
-       if (!ext4_group_desc_csum_verify(sbi, block_group, gdp)) {
+       if (!ext4_group_desc_csum_verify(sb, block_group, gdp)) {
                ext4_error(sb, "Checksum bad for group %u", block_group);
                ext4_free_group_clusters_set(sb, gdp, 0);
                ext4_free_inodes_set(sb, gdp, 0);
                ext4_itable_unused_set(sb, gdp, 0);
                memset(bh->b_data, 0xff, sb->s_blocksize);
+               ext4_block_bitmap_csum_set(sb, block_group, gdp, bh,
+                                          EXT4_BLOCKS_PER_GROUP(sb) / 8);
                return;
        }
        memset(bh->b_data, 0, sb->s_blocksize);
@@ -210,6 +212,9 @@ void ext4_init_block_bitmap(struct super_block *sb, struct buffer_head *bh,
         */
        ext4_mark_bitmap_end(num_clusters_in_group(sb, block_group),
                             sb->s_blocksize * 8, bh->b_data);
+       ext4_block_bitmap_csum_set(sb, block_group, gdp, bh,
+                                  EXT4_BLOCKS_PER_GROUP(sb) / 8);
+       ext4_group_desc_csum_set(sb, block_group, gdp);
 }
 
 /* Return the number of free blocks in a block group.  It is used when
@@ -276,9 +281,9 @@ struct ext4_group_desc * ext4_get_group_desc(struct super_block *sb,
 }
 
 static int ext4_valid_block_bitmap(struct super_block *sb,
-                                       struct ext4_group_desc *desc,
-                                       unsigned int block_group,
-                                       struct buffer_head *bh)
+                                  struct ext4_group_desc *desc,
+                                  unsigned int block_group,
+                                  struct buffer_head *bh)
 {
        ext4_grpblk_t offset;
        ext4_grpblk_t next_zero_bit;
@@ -325,6 +330,23 @@ err_out:
                        block_group, bitmap_blk);
        return 0;
 }
+
+void ext4_validate_block_bitmap(struct super_block *sb,
+                              struct ext4_group_desc *desc,
+                              unsigned int block_group,
+                              struct buffer_head *bh)
+{
+       if (buffer_verified(bh))
+               return;
+
+       ext4_lock_group(sb, block_group);
+       if (ext4_valid_block_bitmap(sb, desc, block_group, bh) &&
+           ext4_block_bitmap_csum_verify(sb, block_group, desc, bh,
+                                         EXT4_BLOCKS_PER_GROUP(sb) / 8))
+               set_buffer_verified(bh);
+       ext4_unlock_group(sb, block_group);
+}
+
 /**
  * ext4_read_block_bitmap()
  * @sb:                        super block
@@ -355,12 +377,12 @@ ext4_read_block_bitmap_nowait(struct super_block *sb, ext4_group_t block_group)
        }
 
        if (bitmap_uptodate(bh))
-               return bh;
+               goto verify;
 
        lock_buffer(bh);
        if (bitmap_uptodate(bh)) {
                unlock_buffer(bh);
-               return bh;
+               goto verify;
        }
        ext4_lock_group(sb, block_group);
        if (desc->bg_flags & cpu_to_le16(EXT4_BG_BLOCK_UNINIT)) {
@@ -379,7 +401,7 @@ ext4_read_block_bitmap_nowait(struct super_block *sb, ext4_group_t block_group)
                 */
                set_bitmap_uptodate(bh);
                unlock_buffer(bh);
-               return bh;
+               goto verify;
        }
        /*
         * submit the buffer_head for reading
@@ -390,6 +412,9 @@ ext4_read_block_bitmap_nowait(struct super_block *sb, ext4_group_t block_group)
        get_bh(bh);
        submit_bh(READ, bh);
        return bh;
+verify:
+       ext4_validate_block_bitmap(sb, desc, block_group, bh);
+       return bh;
 }
 
 /* Returns 0 on success, 1 on error */
@@ -412,7 +437,7 @@ int ext4_wait_block_bitmap(struct super_block *sb, ext4_group_t block_group,
        }
        clear_buffer_new(bh);
        /* Panic or remount fs read-only if block bitmap is invalid */
-       ext4_valid_block_bitmap(sb, desc, block_group, bh);
+       ext4_validate_block_bitmap(sb, desc, block_group, bh);
        return 0;
 }
 
index fa3af81ac565c16dba6237edc89c3c7d70c5fc61..b319721da26ae32010adcd46db7e2d98ec50887a 100644 (file)
@@ -29,3 +29,86 @@ unsigned int ext4_count_free(struct buffer_head *map, unsigned int numchars)
 
 #endif  /*  EXT4FS_DEBUG  */
 
+int ext4_inode_bitmap_csum_verify(struct super_block *sb, ext4_group_t group,
+                                 struct ext4_group_desc *gdp,
+                                 struct buffer_head *bh, int sz)
+{
+       __u32 hi;
+       __u32 provided, calculated;
+       struct ext4_sb_info *sbi = EXT4_SB(sb);
+
+       if (!EXT4_HAS_RO_COMPAT_FEATURE(sb,
+                                       EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+               return 1;
+
+       provided = le16_to_cpu(gdp->bg_inode_bitmap_csum_lo);
+       calculated = ext4_chksum(sbi, sbi->s_csum_seed, (__u8 *)bh->b_data, sz);
+       if (sbi->s_desc_size >= EXT4_BG_INODE_BITMAP_CSUM_HI_END) {
+               hi = le16_to_cpu(gdp->bg_inode_bitmap_csum_hi);
+               provided |= (hi << 16);
+       } else
+               calculated &= 0xFFFF;
+
+       return provided == calculated;
+}
+
+void ext4_inode_bitmap_csum_set(struct super_block *sb, ext4_group_t group,
+                               struct ext4_group_desc *gdp,
+                               struct buffer_head *bh, int sz)
+{
+       __u32 csum;
+       struct ext4_sb_info *sbi = EXT4_SB(sb);
+
+       if (!EXT4_HAS_RO_COMPAT_FEATURE(sb,
+                                       EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+               return;
+
+       csum = ext4_chksum(sbi, sbi->s_csum_seed, (__u8 *)bh->b_data, sz);
+       gdp->bg_inode_bitmap_csum_lo = cpu_to_le16(csum & 0xFFFF);
+       if (sbi->s_desc_size >= EXT4_BG_INODE_BITMAP_CSUM_HI_END)
+               gdp->bg_inode_bitmap_csum_hi = cpu_to_le16(csum >> 16);
+}
+
+int ext4_block_bitmap_csum_verify(struct super_block *sb, ext4_group_t group,
+                                 struct ext4_group_desc *gdp,
+                                 struct buffer_head *bh, int sz)
+{
+       __u32 hi;
+       __u32 provided, calculated;
+       struct ext4_sb_info *sbi = EXT4_SB(sb);
+
+       if (!EXT4_HAS_RO_COMPAT_FEATURE(sb,
+                                       EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+               return 1;
+
+       provided = le16_to_cpu(gdp->bg_block_bitmap_csum_lo);
+       calculated = ext4_chksum(sbi, sbi->s_csum_seed, (__u8 *)bh->b_data, sz);
+       if (sbi->s_desc_size >= EXT4_BG_BLOCK_BITMAP_CSUM_HI_END) {
+               hi = le16_to_cpu(gdp->bg_block_bitmap_csum_hi);
+               provided |= (hi << 16);
+       } else
+               calculated &= 0xFFFF;
+
+       if (provided == calculated)
+               return 1;
+
+       ext4_error(sb, "Bad block bitmap checksum: block_group = %u", group);
+       return 0;
+}
+
+void ext4_block_bitmap_csum_set(struct super_block *sb, ext4_group_t group,
+                               struct ext4_group_desc *gdp,
+                               struct buffer_head *bh, int sz)
+{
+       __u32 csum;
+       struct ext4_sb_info *sbi = EXT4_SB(sb);
+
+       if (!EXT4_HAS_RO_COMPAT_FEATURE(sb,
+                       EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+               return;
+
+       csum = ext4_chksum(sbi, sbi->s_csum_seed, (__u8 *)bh->b_data, sz);
+       gdp->bg_block_bitmap_csum_lo = cpu_to_le16(csum & 0xFFFF);
+       if (sbi->s_desc_size >= EXT4_BG_BLOCK_BITMAP_CSUM_HI_END)
+               gdp->bg_block_bitmap_csum_hi = cpu_to_le16(csum >> 16);
+}
index b86786202643bdd8044ee85fb72a0a21bc2c9bef..aa39e600d15954244aead38f7aed30513ce86d65 100644 (file)
@@ -179,6 +179,18 @@ static int ext4_readdir(struct file *filp,
                        continue;
                }
 
+               /* Check the checksum */
+               if (!buffer_verified(bh) &&
+                   !ext4_dirent_csum_verify(inode,
+                               (struct ext4_dir_entry *)bh->b_data)) {
+                       EXT4_ERROR_FILE(filp, 0, "directory fails checksum "
+                                       "at offset %llu",
+                                       (unsigned long long)filp->f_pos);
+                       filp->f_pos += sb->s_blocksize - offset;
+                       continue;
+               }
+               set_buffer_verified(bh);
+
 revalidate:
                /* If the dir block has changed since the last call to
                 * readdir(2), then we might be pointing to an invalid
index c21b1de51afbb42191adea4fc4a357e3906c8489..cfc4e01b3c8370c642681824ef55b13a66683c0d 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/wait.h>
 #include <linux/blockgroup_lock.h>
 #include <linux/percpu_counter.h>
+#include <crypto/hash.h>
 #ifdef __KERNEL__
 #include <linux/compat.h>
 #endif
@@ -298,7 +299,9 @@ struct ext4_group_desc
        __le16  bg_free_inodes_count_lo;/* Free inodes count */
        __le16  bg_used_dirs_count_lo;  /* Directories count */
        __le16  bg_flags;               /* EXT4_BG_flags (INODE_UNINIT, etc) */
-       __u32   bg_reserved[2];         /* Likely block/inode bitmap checksum */
+       __le32  bg_exclude_bitmap_lo;   /* Exclude bitmap for snapshots */
+       __le16  bg_block_bitmap_csum_lo;/* crc32c(s_uuid+grp_num+bbitmap) LE */
+       __le16  bg_inode_bitmap_csum_lo;/* crc32c(s_uuid+grp_num+ibitmap) LE */
        __le16  bg_itable_unused_lo;    /* Unused inodes count */
        __le16  bg_checksum;            /* crc16(sb_uuid+group+desc) */
        __le32  bg_block_bitmap_hi;     /* Blocks bitmap block MSB */
@@ -308,9 +311,19 @@ struct ext4_group_desc
        __le16  bg_free_inodes_count_hi;/* Free inodes count MSB */
        __le16  bg_used_dirs_count_hi;  /* Directories count MSB */
        __le16  bg_itable_unused_hi;    /* Unused inodes count MSB */
-       __u32   bg_reserved2[3];
+       __le32  bg_exclude_bitmap_hi;   /* Exclude bitmap block MSB */
+       __le16  bg_block_bitmap_csum_hi;/* crc32c(s_uuid+grp_num+bbitmap) BE */
+       __le16  bg_inode_bitmap_csum_hi;/* crc32c(s_uuid+grp_num+ibitmap) BE */
+       __u32   bg_reserved;
 };
 
+#define EXT4_BG_INODE_BITMAP_CSUM_HI_END       \
+       (offsetof(struct ext4_group_desc, bg_inode_bitmap_csum_hi) + \
+        sizeof(__le16))
+#define EXT4_BG_BLOCK_BITMAP_CSUM_HI_END       \
+       (offsetof(struct ext4_group_desc, bg_block_bitmap_csum_hi) + \
+        sizeof(__le16))
+
 /*
  * Structure of a flex block group info
  */
@@ -650,7 +663,8 @@ struct ext4_inode {
                        __le16  l_i_file_acl_high;
                        __le16  l_i_uid_high;   /* these 2 fields */
                        __le16  l_i_gid_high;   /* were reserved2[0] */
-                       __u32   l_i_reserved2;
+                       __le16  l_i_checksum_lo;/* crc32c(uuid+inum+inode) LE */
+                       __le16  l_i_reserved;
                } linux2;
                struct {
                        __le16  h_i_reserved1;  /* Obsoleted fragment number/size which are removed in ext4 */
@@ -666,7 +680,7 @@ struct ext4_inode {
                } masix2;
        } osd2;                         /* OS dependent 2 */
        __le16  i_extra_isize;
-       __le16  i_pad1;
+       __le16  i_checksum_hi;  /* crc32c(uuid+inum+inode) BE */
        __le32  i_ctime_extra;  /* extra Change time      (nsec << 2 | epoch) */
        __le32  i_mtime_extra;  /* extra Modification time(nsec << 2 | epoch) */
        __le32  i_atime_extra;  /* extra Access time      (nsec << 2 | epoch) */
@@ -768,7 +782,7 @@ do {                                                                               \
 #define i_gid_low      i_gid
 #define i_uid_high     osd2.linux2.l_i_uid_high
 #define i_gid_high     osd2.linux2.l_i_gid_high
-#define i_reserved2    osd2.linux2.l_i_reserved2
+#define i_checksum_lo  osd2.linux2.l_i_checksum_lo
 
 #elif defined(__GNU__)
 
@@ -908,6 +922,9 @@ struct ext4_inode_info {
         */
        tid_t i_sync_tid;
        tid_t i_datasync_tid;
+
+       /* Precomputed uuid+inum+igen checksum for seeding inode checksums */
+       __u32 i_csum_seed;
 };
 
 /*
@@ -1001,6 +1018,9 @@ extern void ext4_set_bits(void *bm, int cur, int len);
 #define EXT4_ERRORS_PANIC              3       /* Panic */
 #define EXT4_ERRORS_DEFAULT            EXT4_ERRORS_CONTINUE
 
+/* Metadata checksum algorithm codes */
+#define EXT4_CRC32C_CHKSUM             1
+
 /*
  * Structure of the super block
  */
@@ -1087,7 +1107,7 @@ struct ext4_super_block {
        __le64  s_mmp_block;            /* Block for multi-mount protection */
        __le32  s_raid_stripe_width;    /* blocks on all data disks (N*stride)*/
        __u8    s_log_groups_per_flex;  /* FLEX_BG group size */
-       __u8    s_reserved_char_pad;
+       __u8    s_checksum_type;        /* metadata checksum algorithm used */
        __le16  s_reserved_pad;
        __le64  s_kbytes_written;       /* nr of lifetime kilobytes written */
        __le32  s_snapshot_inum;        /* Inode number of active snapshot */
@@ -1113,7 +1133,8 @@ struct ext4_super_block {
        __le32  s_usr_quota_inum;       /* inode for tracking user quota */
        __le32  s_grp_quota_inum;       /* inode for tracking group quota */
        __le32  s_overhead_clusters;    /* overhead blocks/clusters in fs */
-       __le32  s_reserved[109];        /* Padding to the end of the block */
+       __le32  s_reserved[108];        /* Padding to the end of the block */
+       __le32  s_checksum;             /* crc32c(superblock) */
 };
 
 #define EXT4_S_ERR_LEN (EXT4_S_ERR_END - EXT4_S_ERR_START)
@@ -1176,6 +1197,7 @@ struct ext4_sb_info {
        struct proc_dir_entry *s_proc;
        struct kobject s_kobj;
        struct completion s_kobj_unregister;
+       struct super_block *s_sb;
 
        /* Journaling */
        struct journal_s *s_journal;
@@ -1266,6 +1288,12 @@ struct ext4_sb_info {
 
        /* record the last minlen when FITRIM is called. */
        atomic_t s_last_trim_minblks;
+
+       /* Reference to checksum algorithm driver via cryptoapi */
+       struct crypto_shash *s_chksum_driver;
+
+       /* Precomputed FS UUID checksum for seeding other checksums */
+       __u32 s_csum_seed;
 };
 
 static inline struct ext4_sb_info *EXT4_SB(struct super_block *sb)
@@ -1414,6 +1442,12 @@ static inline void ext4_clear_state_flags(struct ext4_inode_info *ei)
 #define EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE     0x0040
 #define EXT4_FEATURE_RO_COMPAT_QUOTA           0x0100
 #define EXT4_FEATURE_RO_COMPAT_BIGALLOC                0x0200
+/*
+ * METADATA_CSUM also enables group descriptor checksums (GDT_CSUM).  When
+ * METADATA_CSUM is set, group descriptor checksums use the same algorithm as
+ * all other data structures' checksums.  However, the METADATA_CSUM and
+ * GDT_CSUM bits are mutually exclusive.
+ */
 #define EXT4_FEATURE_RO_COMPAT_METADATA_CSUM   0x0400
 
 #define EXT4_FEATURE_INCOMPAT_COMPRESSION      0x0001
@@ -1461,7 +1495,8 @@ static inline void ext4_clear_state_flags(struct ext4_inode_info *ei)
                                         EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE | \
                                         EXT4_FEATURE_RO_COMPAT_BTREE_DIR |\
                                         EXT4_FEATURE_RO_COMPAT_HUGE_FILE |\
-                                        EXT4_FEATURE_RO_COMPAT_BIGALLOC)
+                                        EXT4_FEATURE_RO_COMPAT_BIGALLOC |\
+                                        EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)
 
 /*
  * Default values for user and/or group using reserved blocks
@@ -1526,6 +1561,18 @@ struct ext4_dir_entry_2 {
        char    name[EXT4_NAME_LEN];    /* File name */
 };
 
+/*
+ * This is a bogus directory entry at the end of each leaf block that
+ * records checksums.
+ */
+struct ext4_dir_entry_tail {
+       __le32  det_reserved_zero1;     /* Pretend to be unused */
+       __le16  det_rec_len;            /* 12 */
+       __u8    det_reserved_zero2;     /* Zero name length */
+       __u8    det_reserved_ft;        /* 0xDE, fake file type */
+       __le32  det_checksum;           /* crc32c(uuid+inum+dirblock) */
+};
+
 /*
  * Ext4 directory file types.  Only the low 3 bits are used.  The
  * other bits are reserved for now.
@@ -1541,6 +1588,8 @@ struct ext4_dir_entry_2 {
 
 #define EXT4_FT_MAX            8
 
+#define EXT4_FT_DIR_CSUM       0xDE
+
 /*
  * EXT4_DIR_PAD defines the directory entries boundaries
  *
@@ -1609,6 +1658,25 @@ static inline __le16 ext4_rec_len_to_disk(unsigned len, unsigned blocksize)
 #define DX_HASH_HALF_MD4_UNSIGNED      4
 #define DX_HASH_TEA_UNSIGNED           5
 
+static inline u32 ext4_chksum(struct ext4_sb_info *sbi, u32 crc,
+                             const void *address, unsigned int length)
+{
+       struct {
+               struct shash_desc shash;
+               char ctx[crypto_shash_descsize(sbi->s_chksum_driver)];
+       } desc;
+       int err;
+
+       desc.shash.tfm = sbi->s_chksum_driver;
+       desc.shash.flags = 0;
+       *(u32 *)desc.ctx = crc;
+
+       err = crypto_shash_update(&desc.shash, address, length);
+       BUG_ON(err);
+
+       return *(u32 *)desc.ctx;
+}
+
 #ifdef __KERNEL__
 
 /* hash info structure used by the directory hash */
@@ -1741,7 +1809,8 @@ struct mmp_struct {
        __le16  mmp_check_interval;
 
        __le16  mmp_pad1;
-       __le32  mmp_pad2[227];
+       __le32  mmp_pad2[226];
+       __le32  mmp_checksum;           /* crc32c(uuid+mmp_block) */
 };
 
 /* arguments passed to the mmp thread */
@@ -1784,8 +1853,24 @@ struct mmpd_data {
 
 /* bitmap.c */
 extern unsigned int ext4_count_free(struct buffer_head *, unsigned);
+void ext4_inode_bitmap_csum_set(struct super_block *sb, ext4_group_t group,
+                               struct ext4_group_desc *gdp,
+                               struct buffer_head *bh, int sz);
+int ext4_inode_bitmap_csum_verify(struct super_block *sb, ext4_group_t group,
+                                 struct ext4_group_desc *gdp,
+                                 struct buffer_head *bh, int sz);
+void ext4_block_bitmap_csum_set(struct super_block *sb, ext4_group_t group,
+                               struct ext4_group_desc *gdp,
+                               struct buffer_head *bh, int sz);
+int ext4_block_bitmap_csum_verify(struct super_block *sb, ext4_group_t group,
+                                 struct ext4_group_desc *gdp,
+                                 struct buffer_head *bh, int sz);
 
 /* balloc.c */
+extern void ext4_validate_block_bitmap(struct super_block *sb,
+                                      struct ext4_group_desc *desc,
+                                      unsigned int block_group,
+                                      struct buffer_head *bh);
 extern unsigned int ext4_block_group(struct super_block *sb,
                        ext4_fsblk_t blocknr);
 extern ext4_grpblk_t ext4_block_group_offset(struct super_block *sb,
@@ -1864,7 +1949,7 @@ extern void ext4_end_bitmap_read(struct buffer_head *bh, int uptodate);
 /* mballoc.c */
 extern long ext4_mb_stats;
 extern long ext4_mb_max_to_scan;
-extern int ext4_mb_init(struct super_block *, int);
+extern int ext4_mb_init(struct super_block *);
 extern int ext4_mb_release(struct super_block *);
 extern ext4_fsblk_t ext4_mb_new_blocks(handle_t *,
                                struct ext4_allocation_request *, int *);
@@ -1936,6 +2021,8 @@ extern long ext4_compat_ioctl(struct file *, unsigned int, unsigned long);
 extern int ext4_ext_migrate(struct inode *);
 
 /* namei.c */
+extern int ext4_dirent_csum_verify(struct inode *inode,
+                                  struct ext4_dir_entry *dirent);
 extern int ext4_orphan_add(handle_t *, struct inode *);
 extern int ext4_orphan_del(handle_t *, struct inode *);
 extern int ext4_htree_fill_tree(struct file *dir_file, __u32 start_hash,
@@ -1950,6 +2037,10 @@ extern int ext4_group_extend(struct super_block *sb,
 extern int ext4_resize_fs(struct super_block *sb, ext4_fsblk_t n_blocks_count);
 
 /* super.c */
+extern int ext4_superblock_csum_verify(struct super_block *sb,
+                                      struct ext4_super_block *es);
+extern void ext4_superblock_csum_set(struct super_block *sb,
+                                    struct ext4_super_block *es);
 extern void *ext4_kvmalloc(size_t size, gfp_t flags);
 extern void *ext4_kvzalloc(size_t size, gfp_t flags);
 extern void ext4_kvfree(void *ptr);
@@ -2025,10 +2116,17 @@ extern void ext4_used_dirs_set(struct super_block *sb,
                                struct ext4_group_desc *bg, __u32 count);
 extern void ext4_itable_unused_set(struct super_block *sb,
                                   struct ext4_group_desc *bg, __u32 count);
-extern __le16 ext4_group_desc_csum(struct ext4_sb_info *sbi, __u32 group,
-                                  struct ext4_group_desc *gdp);
-extern int ext4_group_desc_csum_verify(struct ext4_sb_info *sbi, __u32 group,
+extern int ext4_group_desc_csum_verify(struct super_block *sb, __u32 group,
                                       struct ext4_group_desc *gdp);
+extern void ext4_group_desc_csum_set(struct super_block *sb, __u32 group,
+                                    struct ext4_group_desc *gdp);
+
+static inline int ext4_has_group_desc_csum(struct super_block *sb)
+{
+       return EXT4_HAS_RO_COMPAT_FEATURE(sb,
+                                         EXT4_FEATURE_RO_COMPAT_GDT_CSUM |
+                                         EXT4_FEATURE_RO_COMPAT_METADATA_CSUM);
+}
 
 static inline ext4_fsblk_t ext4_blocks_count(struct ext4_super_block *es)
 {
@@ -2225,6 +2323,9 @@ static inline void ext4_unlock_group(struct super_block *sb,
 
 static inline void ext4_mark_super_dirty(struct super_block *sb)
 {
+       struct ext4_super_block *es = EXT4_SB(sb)->s_es;
+
+       ext4_superblock_csum_set(sb, es);
        if (EXT4_SB(sb)->s_journal == NULL)
                sb->s_dirt =1;
 }
@@ -2314,6 +2415,9 @@ extern int ext4_bio_write_page(struct ext4_io_submit *io,
 
 /* mmp.c */
 extern int ext4_multi_mount_protect(struct super_block *, ext4_fsblk_t);
+extern void ext4_mmp_csum_set(struct super_block *sb, struct mmp_struct *mmp);
+extern int ext4_mmp_csum_verify(struct super_block *sb,
+                               struct mmp_struct *mmp);
 
 /* BH_Uninit flag: blocks are allocated but uninitialized on disk */
 enum ext4_state_bits {
index 0f58b86e3a0206e19626f361f453aa80b2838857..cb1b2c919963290fd10d09ba12f6d8c53ace9fa6 100644 (file)
  * ext4_inode has i_block array (60 bytes total).
  * The first 12 bytes store ext4_extent_header;
  * the remainder stores an array of ext4_extent.
+ * For non-inode extent blocks, ext4_extent_tail
+ * follows the array.
  */
 
+/*
+ * This is the extent tail on-disk structure.
+ * All other extent structures are 12 bytes long.  It turns out that
+ * block_size % 12 >= 4 for at least all powers of 2 greater than 512, which
+ * covers all valid ext4 block sizes.  Therefore, this tail structure can be
+ * crammed into the end of the block without having to rebalance the tree.
+ */
+struct ext4_extent_tail {
+       __le32  et_checksum;    /* crc32c(uuid+inum+extent_block) */
+};
+
 /*
  * This is the extent on-disk structure.
  * It's used at the bottom of the tree.
@@ -101,6 +114,17 @@ struct ext4_extent_header {
 
 #define EXT4_EXT_MAGIC         cpu_to_le16(0xf30a)
 
+#define EXT4_EXTENT_TAIL_OFFSET(hdr) \
+       (sizeof(struct ext4_extent_header) + \
+        (sizeof(struct ext4_extent) * le16_to_cpu((hdr)->eh_max)))
+
+static inline struct ext4_extent_tail *
+find_ext4_extent_tail(struct ext4_extent_header *eh)
+{
+       return (struct ext4_extent_tail *)(((void *)eh) +
+                                          EXT4_EXTENT_TAIL_OFFSET(eh));
+}
+
 /*
  * Array of ext4_ext_path contains path to some extent.
  * Creation/lookup routines use it for traversal/splitting/etc.
index aca17901758249d4329d780714e328ef42851e35..90f7c2e84db1bef3fdb90931f9124cfe052705dd 100644 (file)
@@ -138,16 +138,23 @@ int __ext4_handle_dirty_metadata(const char *where, unsigned int line,
 }
 
 int __ext4_handle_dirty_super(const char *where, unsigned int line,
-                             handle_t *handle, struct super_block *sb)
+                             handle_t *handle, struct super_block *sb,
+                             int now)
 {
        struct buffer_head *bh = EXT4_SB(sb)->s_sbh;
        int err = 0;
 
        if (ext4_handle_valid(handle)) {
+               ext4_superblock_csum_set(sb,
+                               (struct ext4_super_block *)bh->b_data);
                err = jbd2_journal_dirty_metadata(handle, bh);
                if (err)
                        ext4_journal_abort_handle(where, line, __func__,
                                                  bh, handle, err);
+       } else if (now) {
+               ext4_superblock_csum_set(sb,
+                               (struct ext4_super_block *)bh->b_data);
+               mark_buffer_dirty(bh);
        } else
                sb->s_dirt = 1;
        return err;
index 83b20fcf9400b11b28185470f8feef309faa8252..f440e8f1841f4e2521486bd94ae19ed83aa896ab 100644 (file)
@@ -213,7 +213,8 @@ int __ext4_handle_dirty_metadata(const char *where, unsigned int line,
                                 struct buffer_head *bh);
 
 int __ext4_handle_dirty_super(const char *where, unsigned int line,
-                             handle_t *handle, struct super_block *sb);
+                             handle_t *handle, struct super_block *sb,
+                             int now);
 
 #define ext4_journal_get_write_access(handle, bh) \
        __ext4_journal_get_write_access(__func__, __LINE__, (handle), (bh))
@@ -225,8 +226,10 @@ int __ext4_handle_dirty_super(const char *where, unsigned int line,
 #define ext4_handle_dirty_metadata(handle, inode, bh) \
        __ext4_handle_dirty_metadata(__func__, __LINE__, (handle), (inode), \
                                     (bh))
+#define ext4_handle_dirty_super_now(handle, sb) \
+       __ext4_handle_dirty_super(__func__, __LINE__, (handle), (sb), 1)
 #define ext4_handle_dirty_super(handle, sb) \
-       __ext4_handle_dirty_super(__func__, __LINE__, (handle), (sb))
+       __ext4_handle_dirty_super(__func__, __LINE__, (handle), (sb), 0)
 
 handle_t *ext4_journal_start_sb(struct super_block *sb, int nblocks);
 int __ext4_journal_stop(const char *where, unsigned int line, handle_t *handle);
index abcdeab67f5232b66d4aa5a6cbb88838094f6247..91341ec6e06a94f2f400d10039a64585cd17ed2e 100644 (file)
 #define EXT4_EXT_MARK_UNINIT1  0x2  /* mark first half uninitialized */
 #define EXT4_EXT_MARK_UNINIT2  0x4  /* mark second half uninitialized */
 
+static __le32 ext4_extent_block_csum(struct inode *inode,
+                                    struct ext4_extent_header *eh)
+{
+       struct ext4_inode_info *ei = EXT4_I(inode);
+       struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
+       __u32 csum;
+
+       csum = ext4_chksum(sbi, ei->i_csum_seed, (__u8 *)eh,
+                          EXT4_EXTENT_TAIL_OFFSET(eh));
+       return cpu_to_le32(csum);
+}
+
+static int ext4_extent_block_csum_verify(struct inode *inode,
+                                        struct ext4_extent_header *eh)
+{
+       struct ext4_extent_tail *et;
+
+       if (!EXT4_HAS_RO_COMPAT_FEATURE(inode->i_sb,
+               EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+               return 1;
+
+       et = find_ext4_extent_tail(eh);
+       if (et->et_checksum != ext4_extent_block_csum(inode, eh))
+               return 0;
+       return 1;
+}
+
+static void ext4_extent_block_csum_set(struct inode *inode,
+                                      struct ext4_extent_header *eh)
+{
+       struct ext4_extent_tail *et;
+
+       if (!EXT4_HAS_RO_COMPAT_FEATURE(inode->i_sb,
+               EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+               return;
+
+       et = find_ext4_extent_tail(eh);
+       et->et_checksum = ext4_extent_block_csum(inode, eh);
+}
+
 static int ext4_split_extent(handle_t *handle,
                                struct inode *inode,
                                struct ext4_ext_path *path,
@@ -117,6 +157,7 @@ static int __ext4_ext_dirty(const char *where, unsigned int line,
 {
        int err;
        if (path->p_bh) {
+               ext4_extent_block_csum_set(inode, ext_block_hdr(path->p_bh));
                /* path points to block */
                err = __ext4_handle_dirty_metadata(where, line, handle,
                                                   inode, path->p_bh);
@@ -391,6 +432,12 @@ static int __ext4_ext_check(const char *function, unsigned int line,
                error_msg = "invalid extent entries";
                goto corrupted;
        }
+       /* Verify checksum on non-root extent tree nodes */
+       if (ext_depth(inode) != depth &&
+           !ext4_extent_block_csum_verify(inode, eh)) {
+               error_msg = "extent tree corrupted";
+               goto corrupted;
+       }
        return 0;
 
 corrupted:
@@ -412,6 +459,26 @@ int ext4_ext_check_inode(struct inode *inode)
        return ext4_ext_check(inode, ext_inode_hdr(inode), ext_depth(inode));
 }
 
+static int __ext4_ext_check_block(const char *function, unsigned int line,
+                                 struct inode *inode,
+                                 struct ext4_extent_header *eh,
+                                 int depth,
+                                 struct buffer_head *bh)
+{
+       int ret;
+
+       if (buffer_verified(bh))
+               return 0;
+       ret = ext4_ext_check(inode, eh, depth);
+       if (ret)
+               return ret;
+       set_buffer_verified(bh);
+       return ret;
+}
+
+#define ext4_ext_check_block(inode, eh, depth, bh)     \
+       __ext4_ext_check_block(__func__, __LINE__, inode, eh, depth, bh)
+
 #ifdef EXT_DEBUG
 static void ext4_ext_show_path(struct inode *inode, struct ext4_ext_path *path)
 {
@@ -536,7 +603,7 @@ ext4_ext_binsearch_idx(struct inode *inode,
        }
 
        path->p_idx = l - 1;
-       ext_debug("  -> %d->%lld ", le32_to_cpu(path->p_idx->ei_block),
+       ext_debug("  -> %u->%lld ", le32_to_cpu(path->p_idx->ei_block),
                  ext4_idx_pblock(path->p_idx));
 
 #ifdef CHECK_BINSEARCH
@@ -668,8 +735,6 @@ ext4_ext_find_extent(struct inode *inode, ext4_lblk_t block,
        i = depth;
        /* walk through the tree */
        while (i) {
-               int need_to_validate = 0;
-
                ext_debug("depth %d: num %d, max %d\n",
                          ppos, le16_to_cpu(eh->eh_entries), le16_to_cpu(eh->eh_max));
 
@@ -688,8 +753,6 @@ ext4_ext_find_extent(struct inode *inode, ext4_lblk_t block,
                                put_bh(bh);
                                goto err;
                        }
-                       /* validate the extent entries */
-                       need_to_validate = 1;
                }
                eh = ext_block_hdr(bh);
                ppos++;
@@ -703,7 +766,7 @@ ext4_ext_find_extent(struct inode *inode, ext4_lblk_t block,
                path[ppos].p_hdr = eh;
                i--;
 
-               if (need_to_validate && ext4_ext_check(inode, eh, i))
+               if (ext4_ext_check_block(inode, eh, i, bh))
                        goto err;
        }
 
@@ -914,6 +977,7 @@ static int ext4_ext_split(handle_t *handle, struct inode *inode,
                le16_add_cpu(&neh->eh_entries, m);
        }
 
+       ext4_extent_block_csum_set(inode, neh);
        set_buffer_uptodate(bh);
        unlock_buffer(bh);
 
@@ -992,6 +1056,7 @@ static int ext4_ext_split(handle_t *handle, struct inode *inode,
                                sizeof(struct ext4_extent_idx) * m);
                        le16_add_cpu(&neh->eh_entries, m);
                }
+               ext4_extent_block_csum_set(inode, neh);
                set_buffer_uptodate(bh);
                unlock_buffer(bh);
 
@@ -1089,6 +1154,7 @@ static int ext4_ext_grow_indepth(handle_t *handle, struct inode *inode,
        else
                neh->eh_max = cpu_to_le16(ext4_ext_space_block(inode, 0));
        neh->eh_magic = EXT4_EXT_MAGIC;
+       ext4_extent_block_csum_set(inode, neh);
        set_buffer_uptodate(bh);
        unlock_buffer(bh);
 
@@ -1344,7 +1410,8 @@ got_index:
                        return -EIO;
                eh = ext_block_hdr(bh);
                /* subtract from p_depth to get proper eh_depth */
-               if (ext4_ext_check(inode, eh, path->p_depth - depth)) {
+               if (ext4_ext_check_block(inode, eh,
+                                        path->p_depth - depth, bh)) {
                        put_bh(bh);
                        return -EIO;
                }
@@ -1357,7 +1424,7 @@ got_index:
        if (bh == NULL)
                return -EIO;
        eh = ext_block_hdr(bh);
-       if (ext4_ext_check(inode, eh, path->p_depth - depth)) {
+       if (ext4_ext_check_block(inode, eh, path->p_depth - depth, bh)) {
                put_bh(bh);
                return -EIO;
        }
@@ -2644,8 +2711,8 @@ cont:
                                err = -EIO;
                                break;
                        }
-                       if (ext4_ext_check(inode, ext_block_hdr(bh),
-                                                       depth - i - 1)) {
+                       if (ext4_ext_check_block(inode, ext_block_hdr(bh),
+                                                       depth - i - 1, bh)) {
                                err = -EIO;
                                break;
                        }
@@ -4722,8 +4789,8 @@ int ext4_ext_punch_hole(struct file *file, loff_t offset, loff_t length)
 
        /* Now release the pages */
        if (last_page_offset > first_page_offset) {
-               truncate_inode_pages_range(mapping, first_page_offset,
-                                          last_page_offset-1);
+               truncate_pagecache_range(inode, first_page_offset,
+                                        last_page_offset - 1);
        }
 
        /* finish any pending end_io work */
index cb70f1812a70f5ca8452e98776cd309ad6638055..8c7642a00054fd1ddf649e733e4b6efb5a0eb14b 100644 (file)
@@ -95,7 +95,7 @@ ext4_file_write(struct kiocb *iocb, const struct iovec *iov,
 {
        struct inode *inode = iocb->ki_filp->f_path.dentry->d_inode;
        int unaligned_aio = 0;
-       int ret;
+       ssize_t ret;
 
        /*
         * If we have encountered a bitmap-format file, the size limit
index 9f9acac6c43f4ac8006e363b5567b4a88dd56615..d48e8b14928cf993c50c33fe9b18a90203c2c492 100644 (file)
@@ -70,24 +70,27 @@ static unsigned ext4_init_inode_bitmap(struct super_block *sb,
                                       ext4_group_t block_group,
                                       struct ext4_group_desc *gdp)
 {
-       struct ext4_sb_info *sbi = EXT4_SB(sb);
-
        J_ASSERT_BH(bh, buffer_locked(bh));
 
        /* If checksum is bad mark all blocks and inodes use to prevent
         * allocation, essentially implementing a per-group read-only flag. */
-       if (!ext4_group_desc_csum_verify(sbi, block_group, gdp)) {
+       if (!ext4_group_desc_csum_verify(sb, block_group, gdp)) {
                ext4_error(sb, "Checksum bad for group %u", block_group);
                ext4_free_group_clusters_set(sb, gdp, 0);
                ext4_free_inodes_set(sb, gdp, 0);
                ext4_itable_unused_set(sb, gdp, 0);
                memset(bh->b_data, 0xff, sb->s_blocksize);
+               ext4_inode_bitmap_csum_set(sb, block_group, gdp, bh,
+                                          EXT4_INODES_PER_GROUP(sb) / 8);
                return 0;
        }
 
        memset(bh->b_data, 0, (EXT4_INODES_PER_GROUP(sb) + 7) / 8);
        ext4_mark_bitmap_end(EXT4_INODES_PER_GROUP(sb), sb->s_blocksize * 8,
                        bh->b_data);
+       ext4_inode_bitmap_csum_set(sb, block_group, gdp, bh,
+                                  EXT4_INODES_PER_GROUP(sb) / 8);
+       ext4_group_desc_csum_set(sb, block_group, gdp);
 
        return EXT4_INODES_PER_GROUP(sb);
 }
@@ -128,12 +131,12 @@ ext4_read_inode_bitmap(struct super_block *sb, ext4_group_t block_group)
                return NULL;
        }
        if (bitmap_uptodate(bh))
-               return bh;
+               goto verify;
 
        lock_buffer(bh);
        if (bitmap_uptodate(bh)) {
                unlock_buffer(bh);
-               return bh;
+               goto verify;
        }
 
        ext4_lock_group(sb, block_group);
@@ -141,6 +144,7 @@ ext4_read_inode_bitmap(struct super_block *sb, ext4_group_t block_group)
                ext4_init_inode_bitmap(sb, bh, block_group, desc);
                set_bitmap_uptodate(bh);
                set_buffer_uptodate(bh);
+               set_buffer_verified(bh);
                ext4_unlock_group(sb, block_group);
                unlock_buffer(bh);
                return bh;
@@ -154,7 +158,7 @@ ext4_read_inode_bitmap(struct super_block *sb, ext4_group_t block_group)
                 */
                set_bitmap_uptodate(bh);
                unlock_buffer(bh);
-               return bh;
+               goto verify;
        }
        /*
         * submit the buffer_head for reading
@@ -171,6 +175,20 @@ ext4_read_inode_bitmap(struct super_block *sb, ext4_group_t block_group)
                           block_group, bitmap_blk);
                return NULL;
        }
+
+verify:
+       ext4_lock_group(sb, block_group);
+       if (!buffer_verified(bh) &&
+           !ext4_inode_bitmap_csum_verify(sb, block_group, desc, bh,
+                                          EXT4_INODES_PER_GROUP(sb) / 8)) {
+               ext4_unlock_group(sb, block_group);
+               put_bh(bh);
+               ext4_error(sb, "Corrupt inode bitmap - block_group = %u, "
+                          "inode_bitmap = %llu", block_group, bitmap_blk);
+               return NULL;
+       }
+       ext4_unlock_group(sb, block_group);
+       set_buffer_verified(bh);
        return bh;
 }
 
@@ -276,7 +294,9 @@ void ext4_free_inode(handle_t *handle, struct inode *inode)
                ext4_used_dirs_set(sb, gdp, count);
                percpu_counter_dec(&sbi->s_dirs_counter);
        }
-       gdp->bg_checksum = ext4_group_desc_csum(sbi, block_group, gdp);
+       ext4_inode_bitmap_csum_set(sb, block_group, gdp, bitmap_bh,
+                                  EXT4_INODES_PER_GROUP(sb) / 8);
+       ext4_group_desc_csum_set(sb, block_group, gdp);
        ext4_unlock_group(sb, block_group);
 
        percpu_counter_inc(&sbi->s_freeinodes_counter);
@@ -488,10 +508,12 @@ fallback_retry:
        for (i = 0; i < ngroups; i++) {
                grp = (parent_group + i) % ngroups;
                desc = ext4_get_group_desc(sb, grp, NULL);
-               grp_free = ext4_free_inodes_count(sb, desc);
-               if (desc && grp_free && grp_free >= avefreei) {
-                       *group = grp;
-                       return 0;
+               if (desc) {
+                       grp_free = ext4_free_inodes_count(sb, desc);
+                       if (grp_free && grp_free >= avefreei) {
+                               *group = grp;
+                               return 0;
+                       }
                }
        }
 
@@ -709,7 +731,7 @@ repeat_in_this_group:
 
 got:
        /* We may have to initialize the block bitmap if it isn't already */
-       if (EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_GDT_CSUM) &&
+       if (ext4_has_group_desc_csum(sb) &&
            gdp->bg_flags & cpu_to_le16(EXT4_BG_BLOCK_UNINIT)) {
                struct buffer_head *block_bitmap_bh;
 
@@ -731,8 +753,11 @@ got:
                        gdp->bg_flags &= cpu_to_le16(~EXT4_BG_BLOCK_UNINIT);
                        ext4_free_group_clusters_set(sb, gdp,
                                ext4_free_clusters_after_init(sb, group, gdp));
-                       gdp->bg_checksum = ext4_group_desc_csum(sbi, group,
-                                                               gdp);
+                       ext4_block_bitmap_csum_set(sb, group, gdp,
+                                                  block_bitmap_bh,
+                                                  EXT4_BLOCKS_PER_GROUP(sb) /
+                                                  8);
+                       ext4_group_desc_csum_set(sb, group, gdp);
                }
                ext4_unlock_group(sb, group);
 
@@ -751,7 +776,7 @@ got:
                goto fail;
 
        /* Update the relevant bg descriptor fields */
-       if (EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) {
+       if (ext4_has_group_desc_csum(sb)) {
                int free;
                struct ext4_group_info *grp = ext4_get_group_info(sb, group);
 
@@ -772,7 +797,10 @@ got:
                        ext4_itable_unused_set(sb, gdp,
                                        (EXT4_INODES_PER_GROUP(sb) - ino));
                up_read(&grp->alloc_sem);
+       } else {
+               ext4_lock_group(sb, group);
        }
+
        ext4_free_inodes_set(sb, gdp, ext4_free_inodes_count(sb, gdp) - 1);
        if (S_ISDIR(mode)) {
                ext4_used_dirs_set(sb, gdp, ext4_used_dirs_count(sb, gdp) + 1);
@@ -782,10 +810,12 @@ got:
                        atomic_inc(&sbi->s_flex_groups[f].used_dirs);
                }
        }
-       if (EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) {
-               gdp->bg_checksum = ext4_group_desc_csum(sbi, group, gdp);
-               ext4_unlock_group(sb, group);
+       if (ext4_has_group_desc_csum(sb)) {
+               ext4_inode_bitmap_csum_set(sb, group, gdp, inode_bitmap_bh,
+                                          EXT4_INODES_PER_GROUP(sb) / 8);
+               ext4_group_desc_csum_set(sb, group, gdp);
        }
+       ext4_unlock_group(sb, group);
 
        BUFFER_TRACE(inode_bitmap_bh, "call ext4_handle_dirty_metadata");
        err = ext4_handle_dirty_metadata(handle, NULL, inode_bitmap_bh);
@@ -850,6 +880,19 @@ got:
        inode->i_generation = sbi->s_next_generation++;
        spin_unlock(&sbi->s_next_gen_lock);
 
+       /* Precompute checksum seed for inode metadata */
+       if (EXT4_HAS_RO_COMPAT_FEATURE(sb,
+                       EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) {
+               __u32 csum;
+               struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
+               __le32 inum = cpu_to_le32(inode->i_ino);
+               __le32 gen = cpu_to_le32(inode->i_generation);
+               csum = ext4_chksum(sbi, sbi->s_csum_seed, (__u8 *)&inum,
+                                  sizeof(inum));
+               ei->i_csum_seed = ext4_chksum(sbi, csum, (__u8 *)&gen,
+                                             sizeof(gen));
+       }
+
        ext4_clear_state_flags(ei); /* Only relevant on 32-bit archs */
        ext4_set_inode_state(inode, EXT4_STATE_NEW);
 
@@ -1140,7 +1183,7 @@ int ext4_init_inode_table(struct super_block *sb, ext4_group_t group,
 skip_zeroout:
        ext4_lock_group(sb, group);
        gdp->bg_flags |= cpu_to_le16(EXT4_BG_INODE_ZEROED);
-       gdp->bg_checksum = ext4_group_desc_csum(sbi, group, gdp);
+       ext4_group_desc_csum_set(sb, group, gdp);
        ext4_unlock_group(sb, group);
 
        BUFFER_TRACE(group_desc_bh,
index 07eaf565fdcb2ad4fba4f92c6fe55a01b2fea17b..02bc8cbe7281b3d47c3449a1c4b8e4220685ba52 100644 (file)
 
 #define MPAGE_DA_EXTENT_TAIL 0x01
 
+static __u32 ext4_inode_csum(struct inode *inode, struct ext4_inode *raw,
+                             struct ext4_inode_info *ei)
+{
+       struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
+       __u16 csum_lo;
+       __u16 csum_hi = 0;
+       __u32 csum;
+
+       csum_lo = raw->i_checksum_lo;
+       raw->i_checksum_lo = 0;
+       if (EXT4_INODE_SIZE(inode->i_sb) > EXT4_GOOD_OLD_INODE_SIZE &&
+           EXT4_FITS_IN_INODE(raw, ei, i_checksum_hi)) {
+               csum_hi = raw->i_checksum_hi;
+               raw->i_checksum_hi = 0;
+       }
+
+       csum = ext4_chksum(sbi, ei->i_csum_seed, (__u8 *)raw,
+                          EXT4_INODE_SIZE(inode->i_sb));
+
+       raw->i_checksum_lo = csum_lo;
+       if (EXT4_INODE_SIZE(inode->i_sb) > EXT4_GOOD_OLD_INODE_SIZE &&
+           EXT4_FITS_IN_INODE(raw, ei, i_checksum_hi))
+               raw->i_checksum_hi = csum_hi;
+
+       return csum;
+}
+
+static int ext4_inode_csum_verify(struct inode *inode, struct ext4_inode *raw,
+                                 struct ext4_inode_info *ei)
+{
+       __u32 provided, calculated;
+
+       if (EXT4_SB(inode->i_sb)->s_es->s_creator_os !=
+           cpu_to_le32(EXT4_OS_LINUX) ||
+           !EXT4_HAS_RO_COMPAT_FEATURE(inode->i_sb,
+               EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+               return 1;
+
+       provided = le16_to_cpu(raw->i_checksum_lo);
+       calculated = ext4_inode_csum(inode, raw, ei);
+       if (EXT4_INODE_SIZE(inode->i_sb) > EXT4_GOOD_OLD_INODE_SIZE &&
+           EXT4_FITS_IN_INODE(raw, ei, i_checksum_hi))
+               provided |= ((__u32)le16_to_cpu(raw->i_checksum_hi)) << 16;
+       else
+               calculated &= 0xFFFF;
+
+       return provided == calculated;
+}
+
+static void ext4_inode_csum_set(struct inode *inode, struct ext4_inode *raw,
+                               struct ext4_inode_info *ei)
+{
+       __u32 csum;
+
+       if (EXT4_SB(inode->i_sb)->s_es->s_creator_os !=
+           cpu_to_le32(EXT4_OS_LINUX) ||
+           !EXT4_HAS_RO_COMPAT_FEATURE(inode->i_sb,
+               EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+               return;
+
+       csum = ext4_inode_csum(inode, raw, ei);
+       raw->i_checksum_lo = cpu_to_le16(csum & 0xFFFF);
+       if (EXT4_INODE_SIZE(inode->i_sb) > EXT4_GOOD_OLD_INODE_SIZE &&
+           EXT4_FITS_IN_INODE(raw, ei, i_checksum_hi))
+               raw->i_checksum_hi = cpu_to_le16(csum >> 16);
+}
+
 static inline int ext4_begin_ordered_truncate(struct inode *inode,
                                              loff_t new_size)
 {
@@ -3517,8 +3584,7 @@ make_io:
                                b = table;
                        end = b + EXT4_SB(sb)->s_inode_readahead_blks;
                        num = EXT4_INODES_PER_GROUP(sb);
-                       if (EXT4_HAS_RO_COMPAT_FEATURE(sb,
-                                      EXT4_FEATURE_RO_COMPAT_GDT_CSUM))
+                       if (ext4_has_group_desc_csum(sb))
                                num -= ext4_itable_unused_count(sb, gdp);
                        table += num / inodes_per_block;
                        if (end > table)
@@ -3646,6 +3712,39 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino)
        if (ret < 0)
                goto bad_inode;
        raw_inode = ext4_raw_inode(&iloc);
+
+       if (EXT4_INODE_SIZE(inode->i_sb) > EXT4_GOOD_OLD_INODE_SIZE) {
+               ei->i_extra_isize = le16_to_cpu(raw_inode->i_extra_isize);
+               if (EXT4_GOOD_OLD_INODE_SIZE + ei->i_extra_isize >
+                   EXT4_INODE_SIZE(inode->i_sb)) {
+                       EXT4_ERROR_INODE(inode, "bad extra_isize (%u != %u)",
+                               EXT4_GOOD_OLD_INODE_SIZE + ei->i_extra_isize,
+                               EXT4_INODE_SIZE(inode->i_sb));
+                       ret = -EIO;
+                       goto bad_inode;
+               }
+       } else
+               ei->i_extra_isize = 0;
+
+       /* Precompute checksum seed for inode metadata */
+       if (EXT4_HAS_RO_COMPAT_FEATURE(sb,
+                       EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) {
+               struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
+               __u32 csum;
+               __le32 inum = cpu_to_le32(inode->i_ino);
+               __le32 gen = raw_inode->i_generation;
+               csum = ext4_chksum(sbi, sbi->s_csum_seed, (__u8 *)&inum,
+                                  sizeof(inum));
+               ei->i_csum_seed = ext4_chksum(sbi, csum, (__u8 *)&gen,
+                                             sizeof(gen));
+       }
+
+       if (!ext4_inode_csum_verify(inode, raw_inode, ei)) {
+               EXT4_ERROR_INODE(inode, "checksum invalid");
+               ret = -EIO;
+               goto bad_inode;
+       }
+
        inode->i_mode = le16_to_cpu(raw_inode->i_mode);
        i_uid = (uid_t)le16_to_cpu(raw_inode->i_uid_low);
        i_gid = (gid_t)le16_to_cpu(raw_inode->i_gid_low);
@@ -3725,12 +3824,6 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino)
        }
 
        if (EXT4_INODE_SIZE(inode->i_sb) > EXT4_GOOD_OLD_INODE_SIZE) {
-               ei->i_extra_isize = le16_to_cpu(raw_inode->i_extra_isize);
-               if (EXT4_GOOD_OLD_INODE_SIZE + ei->i_extra_isize >
-                   EXT4_INODE_SIZE(inode->i_sb)) {
-                       ret = -EIO;
-                       goto bad_inode;
-               }
                if (ei->i_extra_isize == 0) {
                        /* The extra space is currently unused. Use it. */
                        ei->i_extra_isize = sizeof(struct ext4_inode) -
@@ -3742,8 +3835,7 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino)
                        if (*magic == cpu_to_le32(EXT4_XATTR_MAGIC))
                                ext4_set_inode_state(inode, EXT4_STATE_XATTR);
                }
-       } else
-               ei->i_extra_isize = 0;
+       }
 
        EXT4_INODE_GET_XTIME(i_ctime, inode, raw_inode);
        EXT4_INODE_GET_XTIME(i_mtime, inode, raw_inode);
@@ -3942,7 +4034,7 @@ static int ext4_do_update_inode(handle_t *handle,
                        EXT4_SET_RO_COMPAT_FEATURE(sb,
                                        EXT4_FEATURE_RO_COMPAT_LARGE_FILE);
                        ext4_handle_sync(handle);
-                       err = ext4_handle_dirty_super(handle, sb);
+                       err = ext4_handle_dirty_super_now(handle, sb);
                }
        }
        raw_inode->i_generation = cpu_to_le32(inode->i_generation);
@@ -3969,6 +4061,8 @@ static int ext4_do_update_inode(handle_t *handle,
                raw_inode->i_extra_isize = cpu_to_le16(ei->i_extra_isize);
        }
 
+       ext4_inode_csum_set(inode, raw_inode, ei);
+
        BUFFER_TRACE(bh, "call ext4_handle_dirty_metadata");
        rc = ext4_handle_dirty_metadata(handle, NULL, bh);
        if (!err)
@@ -4213,7 +4307,8 @@ int ext4_getattr(struct vfsmount *mnt, struct dentry *dentry,
         * will return the blocks that include the delayed allocation
         * blocks for this file.
         */
-       delalloc_blocks = EXT4_I(inode)->i_reserved_data_blocks;
+       delalloc_blocks = EXT4_C2B(EXT4_SB(inode->i_sb),
+                               EXT4_I(inode)->i_reserved_data_blocks);
 
        stat->blocks += (delalloc_blocks << inode->i_sb->s_blocksize_bits)>>9;
        return 0;
index 6eee25591b8159bc96d35a16f94f94c0855a35b9..8ad112ae0ade2f21a953ccc03b687939b0b81310 100644 (file)
@@ -38,7 +38,7 @@ long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
                handle_t *handle = NULL;
                int err, migrate = 0;
                struct ext4_iloc iloc;
-               unsigned int oldflags;
+               unsigned int oldflags, mask, i;
                unsigned int jflag;
 
                if (!inode_owner_or_capable(inode))
@@ -115,8 +115,14 @@ long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
                if (err)
                        goto flags_err;
 
-               flags = flags & EXT4_FL_USER_MODIFIABLE;
-               flags |= oldflags & ~EXT4_FL_USER_MODIFIABLE;
+               for (i = 0, mask = 1; i < 32; i++, mask <<= 1) {
+                       if (!(mask & EXT4_FL_USER_MODIFIABLE))
+                               continue;
+                       if (mask & flags)
+                               ext4_set_inode_flag(inode, i);
+                       else
+                               ext4_clear_inode_flag(inode, i);
+               }
                ei->i_flags = flags;
 
                ext4_set_inode_flags(inode);
@@ -152,6 +158,13 @@ flags_out:
                if (!inode_owner_or_capable(inode))
                        return -EPERM;
 
+               if (EXT4_HAS_RO_COMPAT_FEATURE(inode->i_sb,
+                               EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) {
+                       ext4_warning(sb, "Setting inode version is not "
+                                    "supported with metadata_csum enabled.");
+                       return -ENOTTY;
+               }
+
                err = mnt_want_write_file(filp);
                if (err)
                        return err;
index 99ab428bcfa089822e74b433aee7b1bf4076e34d..1cd6994fc446008b74dc9b77863edf0f24e14c33 100644 (file)
@@ -788,7 +788,7 @@ static int ext4_mb_init_cache(struct page *page, char *incore)
        int first_block;
        struct super_block *sb;
        struct buffer_head *bhs;
-       struct buffer_head **bh;
+       struct buffer_head **bh = NULL;
        struct inode *inode;
        char *data;
        char *bitmap;
@@ -2375,7 +2375,7 @@ static int ext4_groupinfo_create_slab(size_t size)
        return 0;
 }
 
-int ext4_mb_init(struct super_block *sb, int needs_recovery)
+int ext4_mb_init(struct super_block *sb)
 {
        struct ext4_sb_info *sbi = EXT4_SB(sb);
        unsigned i, j;
@@ -2517,6 +2517,9 @@ int ext4_mb_release(struct super_block *sb)
        struct ext4_sb_info *sbi = EXT4_SB(sb);
        struct kmem_cache *cachep = get_groupinfo_cache(sb->s_blocksize_bits);
 
+       if (sbi->s_proc)
+               remove_proc_entry("mb_groups", sbi->s_proc);
+
        if (sbi->s_group_info) {
                for (i = 0; i < ngroups; i++) {
                        grinfo = ext4_get_group_info(sb, i);
@@ -2564,8 +2567,6 @@ int ext4_mb_release(struct super_block *sb)
        }
 
        free_percpu(sbi->s_locality_groups);
-       if (sbi->s_proc)
-               remove_proc_entry("mb_groups", sbi->s_proc);
 
        return 0;
 }
@@ -2797,7 +2798,9 @@ ext4_mb_mark_diskspace_used(struct ext4_allocation_context *ac,
        }
        len = ext4_free_group_clusters(sb, gdp) - ac->ac_b_ex.fe_len;
        ext4_free_group_clusters_set(sb, gdp, len);
-       gdp->bg_checksum = ext4_group_desc_csum(sbi, ac->ac_b_ex.fe_group, gdp);
+       ext4_block_bitmap_csum_set(sb, ac->ac_b_ex.fe_group, gdp, bitmap_bh,
+                                  EXT4_BLOCKS_PER_GROUP(sb) / 8);
+       ext4_group_desc_csum_set(sb, ac->ac_b_ex.fe_group, gdp);
 
        ext4_unlock_group(sb, ac->ac_b_ex.fe_group);
        percpu_counter_sub(&sbi->s_freeclusters_counter, ac->ac_b_ex.fe_len);
@@ -3071,13 +3074,9 @@ static void ext4_mb_collect_stats(struct ext4_allocation_context *ac)
 static void ext4_discard_allocated_blocks(struct ext4_allocation_context *ac)
 {
        struct ext4_prealloc_space *pa = ac->ac_pa;
-       int len;
-
-       if (pa && pa->pa_type == MB_INODE_PA) {
-               len = ac->ac_b_ex.fe_len;
-               pa->pa_free += len;
-       }
 
+       if (pa && pa->pa_type == MB_INODE_PA)
+               pa->pa_free += ac->ac_b_ex.fe_len;
 }
 
 /*
@@ -4636,6 +4635,7 @@ do_more:
                 */
                new_entry = kmem_cache_alloc(ext4_free_data_cachep, GFP_NOFS);
                if (!new_entry) {
+                       ext4_mb_unload_buddy(&e4b);
                        err = -ENOMEM;
                        goto error_return;
                }
@@ -4659,7 +4659,9 @@ do_more:
 
        ret = ext4_free_group_clusters(sb, gdp) + count_clusters;
        ext4_free_group_clusters_set(sb, gdp, ret);
-       gdp->bg_checksum = ext4_group_desc_csum(sbi, block_group, gdp);
+       ext4_block_bitmap_csum_set(sb, block_group, gdp, bitmap_bh,
+                                  EXT4_BLOCKS_PER_GROUP(sb) / 8);
+       ext4_group_desc_csum_set(sb, block_group, gdp);
        ext4_unlock_group(sb, block_group);
        percpu_counter_add(&sbi->s_freeclusters_counter, count_clusters);
 
@@ -4803,7 +4805,9 @@ int ext4_group_add_blocks(handle_t *handle, struct super_block *sb,
        mb_free_blocks(NULL, &e4b, bit, count);
        blk_free_count = blocks_freed + ext4_free_group_clusters(sb, desc);
        ext4_free_group_clusters_set(sb, desc, blk_free_count);
-       desc->bg_checksum = ext4_group_desc_csum(sbi, block_group, desc);
+       ext4_block_bitmap_csum_set(sb, block_group, desc, bitmap_bh,
+                                  EXT4_BLOCKS_PER_GROUP(sb) / 8);
+       ext4_group_desc_csum_set(sb, block_group, desc);
        ext4_unlock_group(sb, block_group);
        percpu_counter_add(&sbi->s_freeclusters_counter,
                           EXT4_B2C(sbi, blocks_freed));
index ed6548d89165e1d9c31118aca21d3e89a3772ab2..f99a1311e84765296b0a0a04534e0be0536915bc 100644 (file)
@@ -6,12 +6,45 @@
 
 #include "ext4.h"
 
+/* Checksumming functions */
+static __u32 ext4_mmp_csum(struct super_block *sb, struct mmp_struct *mmp)
+{
+       struct ext4_sb_info *sbi = EXT4_SB(sb);
+       int offset = offsetof(struct mmp_struct, mmp_checksum);
+       __u32 csum;
+
+       csum = ext4_chksum(sbi, sbi->s_csum_seed, (char *)mmp, offset);
+
+       return cpu_to_le32(csum);
+}
+
+int ext4_mmp_csum_verify(struct super_block *sb, struct mmp_struct *mmp)
+{
+       if (!EXT4_HAS_RO_COMPAT_FEATURE(sb,
+                                      EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+               return 1;
+
+       return mmp->mmp_checksum == ext4_mmp_csum(sb, mmp);
+}
+
+void ext4_mmp_csum_set(struct super_block *sb, struct mmp_struct *mmp)
+{
+       if (!EXT4_HAS_RO_COMPAT_FEATURE(sb,
+                                      EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+               return;
+
+       mmp->mmp_checksum = ext4_mmp_csum(sb, mmp);
+}
+
 /*
  * Write the MMP block using WRITE_SYNC to try to get the block on-disk
  * faster.
  */
-static int write_mmp_block(struct buffer_head *bh)
+static int write_mmp_block(struct super_block *sb, struct buffer_head *bh)
 {
+       struct mmp_struct *mmp = (struct mmp_struct *)(bh->b_data);
+
+       ext4_mmp_csum_set(sb, mmp);
        mark_buffer_dirty(bh);
        lock_buffer(bh);
        bh->b_end_io = end_buffer_write_sync;
@@ -59,7 +92,8 @@ static int read_mmp_block(struct super_block *sb, struct buffer_head **bh,
        }
 
        mmp = (struct mmp_struct *)((*bh)->b_data);
-       if (le32_to_cpu(mmp->mmp_magic) != EXT4_MMP_MAGIC)
+       if (le32_to_cpu(mmp->mmp_magic) != EXT4_MMP_MAGIC ||
+           !ext4_mmp_csum_verify(sb, mmp))
                return -EINVAL;
 
        return 0;
@@ -120,7 +154,7 @@ static int kmmpd(void *data)
                mmp->mmp_time = cpu_to_le64(get_seconds());
                last_update_time = jiffies;
 
-               retval = write_mmp_block(bh);
+               retval = write_mmp_block(sb, bh);
                /*
                 * Don't spew too many error messages. Print one every
                 * (s_mmp_update_interval * 60) seconds.
@@ -200,7 +234,7 @@ static int kmmpd(void *data)
        mmp->mmp_seq = cpu_to_le32(EXT4_MMP_SEQ_CLEAN);
        mmp->mmp_time = cpu_to_le64(get_seconds());
 
-       retval = write_mmp_block(bh);
+       retval = write_mmp_block(sb, bh);
 
 failed:
        kfree(data);
@@ -299,7 +333,7 @@ skip:
        seq = mmp_new_seq();
        mmp->mmp_seq = cpu_to_le32(seq);
 
-       retval = write_mmp_block(bh);
+       retval = write_mmp_block(sb, bh);
        if (retval)
                goto failed;
 
index e2a3f4b0ff78d6f81fbf2228f12f201e6ab1a024..5845cd97bf8b094b0fc01082279e8d65ee73f241 100644 (file)
@@ -145,6 +145,14 @@ struct dx_map_entry
        u16 size;
 };
 
+/*
+ * This goes at the end of each htree block.
+ */
+struct dx_tail {
+       u32 dt_reserved;
+       __le32 dt_checksum;     /* crc32c(uuid+inum+dirblock) */
+};
+
 static inline ext4_lblk_t dx_get_block(struct dx_entry *entry);
 static void dx_set_block(struct dx_entry *entry, ext4_lblk_t value);
 static inline unsigned dx_get_hash(struct dx_entry *entry);
@@ -180,6 +188,230 @@ static struct buffer_head * ext4_dx_find_entry(struct inode *dir,
 static int ext4_dx_add_entry(handle_t *handle, struct dentry *dentry,
                             struct inode *inode);
 
+/* checksumming functions */
+#define EXT4_DIRENT_TAIL(block, blocksize) \
+       ((struct ext4_dir_entry_tail *)(((void *)(block)) + \
+                                       ((blocksize) - \
+                                        sizeof(struct ext4_dir_entry_tail))))
+
+static void initialize_dirent_tail(struct ext4_dir_entry_tail *t,
+                                  unsigned int blocksize)
+{
+       memset(t, 0, sizeof(struct ext4_dir_entry_tail));
+       t->det_rec_len = ext4_rec_len_to_disk(
+                       sizeof(struct ext4_dir_entry_tail), blocksize);
+       t->det_reserved_ft = EXT4_FT_DIR_CSUM;
+}
+
+/* Walk through a dirent block to find a checksum "dirent" at the tail */
+static struct ext4_dir_entry_tail *get_dirent_tail(struct inode *inode,
+                                                  struct ext4_dir_entry *de)
+{
+       struct ext4_dir_entry_tail *t;
+
+#ifdef PARANOID
+       struct ext4_dir_entry *d, *top;
+
+       d = de;
+       top = (struct ext4_dir_entry *)(((void *)de) +
+               (EXT4_BLOCK_SIZE(inode->i_sb) -
+               sizeof(struct ext4_dir_entry_tail)));
+       while (d < top && d->rec_len)
+               d = (struct ext4_dir_entry *)(((void *)d) +
+                   le16_to_cpu(d->rec_len));
+
+       if (d != top)
+               return NULL;
+
+       t = (struct ext4_dir_entry_tail *)d;
+#else
+       t = EXT4_DIRENT_TAIL(de, EXT4_BLOCK_SIZE(inode->i_sb));
+#endif
+
+       if (t->det_reserved_zero1 ||
+           le16_to_cpu(t->det_rec_len) != sizeof(struct ext4_dir_entry_tail) ||
+           t->det_reserved_zero2 ||
+           t->det_reserved_ft != EXT4_FT_DIR_CSUM)
+               return NULL;
+
+       return t;
+}
+
+static __le32 ext4_dirent_csum(struct inode *inode,
+                              struct ext4_dir_entry *dirent, int size)
+{
+       struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
+       struct ext4_inode_info *ei = EXT4_I(inode);
+       __u32 csum;
+
+       csum = ext4_chksum(sbi, ei->i_csum_seed, (__u8 *)dirent, size);
+       return cpu_to_le32(csum);
+}
+
+int ext4_dirent_csum_verify(struct inode *inode, struct ext4_dir_entry *dirent)
+{
+       struct ext4_dir_entry_tail *t;
+
+       if (!EXT4_HAS_RO_COMPAT_FEATURE(inode->i_sb,
+                                       EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+               return 1;
+
+       t = get_dirent_tail(inode, dirent);
+       if (!t) {
+               EXT4_ERROR_INODE(inode, "metadata_csum set but no space in dir "
+                                "leaf for checksum.  Please run e2fsck -D.");
+               return 0;
+       }
+
+       if (t->det_checksum != ext4_dirent_csum(inode, dirent,
+                                               (void *)t - (void *)dirent))
+               return 0;
+
+       return 1;
+}
+
+static void ext4_dirent_csum_set(struct inode *inode,
+                                struct ext4_dir_entry *dirent)
+{
+       struct ext4_dir_entry_tail *t;
+
+       if (!EXT4_HAS_RO_COMPAT_FEATURE(inode->i_sb,
+                                       EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+               return;
+
+       t = get_dirent_tail(inode, dirent);
+       if (!t) {
+               EXT4_ERROR_INODE(inode, "metadata_csum set but no space in dir "
+                                "leaf for checksum.  Please run e2fsck -D.");
+               return;
+       }
+
+       t->det_checksum = ext4_dirent_csum(inode, dirent,
+                                          (void *)t - (void *)dirent);
+}
+
+static inline int ext4_handle_dirty_dirent_node(handle_t *handle,
+                                               struct inode *inode,
+                                               struct buffer_head *bh)
+{
+       ext4_dirent_csum_set(inode, (struct ext4_dir_entry *)bh->b_data);
+       return ext4_handle_dirty_metadata(handle, inode, bh);
+}
+
+static struct dx_countlimit *get_dx_countlimit(struct inode *inode,
+                                              struct ext4_dir_entry *dirent,
+                                              int *offset)
+{
+       struct ext4_dir_entry *dp;
+       struct dx_root_info *root;
+       int count_offset;
+
+       if (le16_to_cpu(dirent->rec_len) == EXT4_BLOCK_SIZE(inode->i_sb))
+               count_offset = 8;
+       else if (le16_to_cpu(dirent->rec_len) == 12) {
+               dp = (struct ext4_dir_entry *)(((void *)dirent) + 12);
+               if (le16_to_cpu(dp->rec_len) !=
+                   EXT4_BLOCK_SIZE(inode->i_sb) - 12)
+                       return NULL;
+               root = (struct dx_root_info *)(((void *)dp + 12));
+               if (root->reserved_zero ||
+                   root->info_length != sizeof(struct dx_root_info))
+                       return NULL;
+               count_offset = 32;
+       } else
+               return NULL;
+
+       if (offset)
+               *offset = count_offset;
+       return (struct dx_countlimit *)(((void *)dirent) + count_offset);
+}
+
+static __le32 ext4_dx_csum(struct inode *inode, struct ext4_dir_entry *dirent,
+                          int count_offset, int count, struct dx_tail *t)
+{
+       struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
+       struct ext4_inode_info *ei = EXT4_I(inode);
+       __u32 csum, old_csum;
+       int size;
+
+       size = count_offset + (count * sizeof(struct dx_entry));
+       old_csum = t->dt_checksum;
+       t->dt_checksum = 0;
+       csum = ext4_chksum(sbi, ei->i_csum_seed, (__u8 *)dirent, size);
+       csum = ext4_chksum(sbi, csum, (__u8 *)t, sizeof(struct dx_tail));
+       t->dt_checksum = old_csum;
+
+       return cpu_to_le32(csum);
+}
+
+static int ext4_dx_csum_verify(struct inode *inode,
+                              struct ext4_dir_entry *dirent)
+{
+       struct dx_countlimit *c;
+       struct dx_tail *t;
+       int count_offset, limit, count;
+
+       if (!EXT4_HAS_RO_COMPAT_FEATURE(inode->i_sb,
+                                       EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+               return 1;
+
+       c = get_dx_countlimit(inode, dirent, &count_offset);
+       if (!c) {
+               EXT4_ERROR_INODE(inode, "dir seems corrupt?  Run e2fsck -D.");
+               return 1;
+       }
+       limit = le16_to_cpu(c->limit);
+       count = le16_to_cpu(c->count);
+       if (count_offset + (limit * sizeof(struct dx_entry)) >
+           EXT4_BLOCK_SIZE(inode->i_sb) - sizeof(struct dx_tail)) {
+               EXT4_ERROR_INODE(inode, "metadata_csum set but no space for "
+                                "tree checksum found.  Run e2fsck -D.");
+               return 1;
+       }
+       t = (struct dx_tail *)(((struct dx_entry *)c) + limit);
+
+       if (t->dt_checksum != ext4_dx_csum(inode, dirent, count_offset,
+                                           count, t))
+               return 0;
+       return 1;
+}
+
+static void ext4_dx_csum_set(struct inode *inode, struct ext4_dir_entry *dirent)
+{
+       struct dx_countlimit *c;
+       struct dx_tail *t;
+       int count_offset, limit, count;
+
+       if (!EXT4_HAS_RO_COMPAT_FEATURE(inode->i_sb,
+                                       EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+               return;
+
+       c = get_dx_countlimit(inode, dirent, &count_offset);
+       if (!c) {
+               EXT4_ERROR_INODE(inode, "dir seems corrupt?  Run e2fsck -D.");
+               return;
+       }
+       limit = le16_to_cpu(c->limit);
+       count = le16_to_cpu(c->count);
+       if (count_offset + (limit * sizeof(struct dx_entry)) >
+           EXT4_BLOCK_SIZE(inode->i_sb) - sizeof(struct dx_tail)) {
+               EXT4_ERROR_INODE(inode, "metadata_csum set but no space for "
+                                "tree checksum.  Run e2fsck -D.");
+               return;
+       }
+       t = (struct dx_tail *)(((struct dx_entry *)c) + limit);
+
+       t->dt_checksum = ext4_dx_csum(inode, dirent, count_offset, count, t);
+}
+
+static inline int ext4_handle_dirty_dx_node(handle_t *handle,
+                                           struct inode *inode,
+                                           struct buffer_head *bh)
+{
+       ext4_dx_csum_set(inode, (struct ext4_dir_entry *)bh->b_data);
+       return ext4_handle_dirty_metadata(handle, inode, bh);
+}
+
 /*
  * p is at least 6 bytes before the end of page
  */
@@ -239,12 +471,20 @@ static inline unsigned dx_root_limit(struct inode *dir, unsigned infosize)
 {
        unsigned entry_space = dir->i_sb->s_blocksize - EXT4_DIR_REC_LEN(1) -
                EXT4_DIR_REC_LEN(2) - infosize;
+
+       if (EXT4_HAS_RO_COMPAT_FEATURE(dir->i_sb,
+                                      EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+               entry_space -= sizeof(struct dx_tail);
        return entry_space / sizeof(struct dx_entry);
 }
 
 static inline unsigned dx_node_limit(struct inode *dir)
 {
        unsigned entry_space = dir->i_sb->s_blocksize - EXT4_DIR_REC_LEN(0);
+
+       if (EXT4_HAS_RO_COMPAT_FEATURE(dir->i_sb,
+                                      EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+               entry_space -= sizeof(struct dx_tail);
        return entry_space / sizeof(struct dx_entry);
 }
 
@@ -390,6 +630,15 @@ dx_probe(const struct qstr *d_name, struct inode *dir,
                goto fail;
        }
 
+       if (!buffer_verified(bh) &&
+           !ext4_dx_csum_verify(dir, (struct ext4_dir_entry *)bh->b_data)) {
+               ext4_warning(dir->i_sb, "Root failed checksum");
+               brelse(bh);
+               *err = ERR_BAD_DX_DIR;
+               goto fail;
+       }
+       set_buffer_verified(bh);
+
        entries = (struct dx_entry *) (((char *)&root->info) +
                                       root->info.info_length);
 
@@ -450,6 +699,17 @@ dx_probe(const struct qstr *d_name, struct inode *dir,
                if (!(bh = ext4_bread (NULL,dir, dx_get_block(at), 0, err)))
                        goto fail2;
                at = entries = ((struct dx_node *) bh->b_data)->entries;
+
+               if (!buffer_verified(bh) &&
+                   !ext4_dx_csum_verify(dir,
+                                        (struct ext4_dir_entry *)bh->b_data)) {
+                       ext4_warning(dir->i_sb, "Node failed checksum");
+                       brelse(bh);
+                       *err = ERR_BAD_DX_DIR;
+                       goto fail;
+               }
+               set_buffer_verified(bh);
+
                if (dx_get_limit(entries) != dx_node_limit (dir)) {
                        ext4_warning(dir->i_sb,
                                     "dx entry: limit != node limit");
@@ -549,6 +809,15 @@ static int ext4_htree_next_block(struct inode *dir, __u32 hash,
                if (!(bh = ext4_bread(NULL, dir, dx_get_block(p->at),
                                      0, &err)))
                        return err; /* Failure */
+
+               if (!buffer_verified(bh) &&
+                   !ext4_dx_csum_verify(dir,
+                                        (struct ext4_dir_entry *)bh->b_data)) {
+                       ext4_warning(dir->i_sb, "Node failed checksum");
+                       return -EIO;
+               }
+               set_buffer_verified(bh);
+
                p++;
                brelse(p->bh);
                p->bh = bh;
@@ -577,6 +846,11 @@ static int htree_dirblock_to_tree(struct file *dir_file,
        if (!(bh = ext4_bread (NULL, dir, block, 0, &err)))
                return err;
 
+       if (!buffer_verified(bh) &&
+           !ext4_dirent_csum_verify(dir, (struct ext4_dir_entry *)bh->b_data))
+               return -EIO;
+       set_buffer_verified(bh);
+
        de = (struct ext4_dir_entry_2 *) bh->b_data;
        top = (struct ext4_dir_entry_2 *) ((char *) de +
                                           dir->i_sb->s_blocksize -
@@ -936,6 +1210,15 @@ restart:
                        brelse(bh);
                        goto next;
                }
+               if (!buffer_verified(bh) &&
+                   !ext4_dirent_csum_verify(dir,
+                               (struct ext4_dir_entry *)bh->b_data)) {
+                       EXT4_ERROR_INODE(dir, "checksumming directory "
+                                        "block %lu", (unsigned long)block);
+                       brelse(bh);
+                       goto next;
+               }
+               set_buffer_verified(bh);
                i = search_dirblock(bh, dir, d_name,
                            block << EXT4_BLOCK_SIZE_BITS(sb), res_dir);
                if (i == 1) {
@@ -987,6 +1270,16 @@ static struct buffer_head * ext4_dx_find_entry(struct inode *dir, const struct q
                if (!(bh = ext4_bread(NULL, dir, block, 0, err)))
                        goto errout;
 
+               if (!buffer_verified(bh) &&
+                   !ext4_dirent_csum_verify(dir,
+                               (struct ext4_dir_entry *)bh->b_data)) {
+                       EXT4_ERROR_INODE(dir, "checksumming directory "
+                                        "block %lu", (unsigned long)block);
+                       brelse(bh);
+                       *err = -EIO;
+                       goto errout;
+               }
+               set_buffer_verified(bh);
                retval = search_dirblock(bh, dir, d_name,
                                         block << EXT4_BLOCK_SIZE_BITS(sb),
                                         res_dir);
@@ -1037,6 +1330,12 @@ static struct dentry *ext4_lookup(struct inode *dir, struct dentry *dentry, stru
                        EXT4_ERROR_INODE(dir, "bad inode number: %u", ino);
                        return ERR_PTR(-EIO);
                }
+               if (unlikely(ino == dir->i_ino)) {
+                       EXT4_ERROR_INODE(dir, "'%.*s' linked to parent dir",
+                                        dentry->d_name.len,
+                                        dentry->d_name.name);
+                       return ERR_PTR(-EIO);
+               }
                inode = ext4_iget(dir->i_sb, ino);
                if (inode == ERR_PTR(-ESTALE)) {
                        EXT4_ERROR_INODE(dir,
@@ -1156,8 +1455,14 @@ static struct ext4_dir_entry_2 *do_split(handle_t *handle, struct inode *dir,
        char *data1 = (*bh)->b_data, *data2;
        unsigned split, move, size;
        struct ext4_dir_entry_2 *de = NULL, *de2;
+       struct ext4_dir_entry_tail *t;
+       int     csum_size = 0;
        int     err = 0, i;
 
+       if (EXT4_HAS_RO_COMPAT_FEATURE(dir->i_sb,
+                                      EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+               csum_size = sizeof(struct ext4_dir_entry_tail);
+
        bh2 = ext4_append (handle, dir, &newblock, &err);
        if (!(bh2)) {
                brelse(*bh);
@@ -1204,10 +1509,20 @@ static struct ext4_dir_entry_2 *do_split(handle_t *handle, struct inode *dir,
        /* Fancy dance to stay within two buffers */
        de2 = dx_move_dirents(data1, data2, map + split, count - split, blocksize);
        de = dx_pack_dirents(data1, blocksize);
-       de->rec_len = ext4_rec_len_to_disk(data1 + blocksize - (char *) de,
+       de->rec_len = ext4_rec_len_to_disk(data1 + (blocksize - csum_size) -
+                                          (char *) de,
                                           blocksize);
-       de2->rec_len = ext4_rec_len_to_disk(data2 + blocksize - (char *) de2,
+       de2->rec_len = ext4_rec_len_to_disk(data2 + (blocksize - csum_size) -
+                                           (char *) de2,
                                            blocksize);
+       if (csum_size) {
+               t = EXT4_DIRENT_TAIL(data2, blocksize);
+               initialize_dirent_tail(t, blocksize);
+
+               t = EXT4_DIRENT_TAIL(data1, blocksize);
+               initialize_dirent_tail(t, blocksize);
+       }
+
        dxtrace(dx_show_leaf (hinfo, (struct ext4_dir_entry_2 *) data1, blocksize, 1));
        dxtrace(dx_show_leaf (hinfo, (struct ext4_dir_entry_2 *) data2, blocksize, 1));
 
@@ -1218,10 +1533,10 @@ static struct ext4_dir_entry_2 *do_split(handle_t *handle, struct inode *dir,
                de = de2;
        }
        dx_insert_block(frame, hash2 + continued, newblock);
-       err = ext4_handle_dirty_metadata(handle, dir, bh2);
+       err = ext4_handle_dirty_dirent_node(handle, dir, bh2);
        if (err)
                goto journal_error;
-       err = ext4_handle_dirty_metadata(handle, dir, frame->bh);
+       err = ext4_handle_dirty_dx_node(handle, dir, frame->bh);
        if (err)
                goto journal_error;
        brelse(bh2);
@@ -1258,11 +1573,16 @@ static int add_dirent_to_buf(handle_t *handle, struct dentry *dentry,
        unsigned short  reclen;
        int             nlen, rlen, err;
        char            *top;
+       int             csum_size = 0;
+
+       if (EXT4_HAS_RO_COMPAT_FEATURE(inode->i_sb,
+                                      EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+               csum_size = sizeof(struct ext4_dir_entry_tail);
 
        reclen = EXT4_DIR_REC_LEN(namelen);
        if (!de) {
                de = (struct ext4_dir_entry_2 *)bh->b_data;
-               top = bh->b_data + blocksize - reclen;
+               top = bh->b_data + (blocksize - csum_size) - reclen;
                while ((char *) de <= top) {
                        if (ext4_check_dir_entry(dir, NULL, de, bh, offset))
                                return -EIO;
@@ -1295,11 +1615,8 @@ static int add_dirent_to_buf(handle_t *handle, struct dentry *dentry,
                de = de1;
        }
        de->file_type = EXT4_FT_UNKNOWN;
-       if (inode) {
-               de->inode = cpu_to_le32(inode->i_ino);
-               ext4_set_de_type(dir->i_sb, de, inode->i_mode);
-       } else
-               de->inode = 0;
+       de->inode = cpu_to_le32(inode->i_ino);
+       ext4_set_de_type(dir->i_sb, de, inode->i_mode);
        de->name_len = namelen;
        memcpy(de->name, name, namelen);
        /*
@@ -1318,7 +1635,7 @@ static int add_dirent_to_buf(handle_t *handle, struct dentry *dentry,
        dir->i_version++;
        ext4_mark_inode_dirty(handle, dir);
        BUFFER_TRACE(bh, "call ext4_handle_dirty_metadata");
-       err = ext4_handle_dirty_metadata(handle, dir, bh);
+       err = ext4_handle_dirty_dirent_node(handle, dir, bh);
        if (err)
                ext4_std_error(dir->i_sb, err);
        return 0;
@@ -1339,6 +1656,7 @@ static int make_indexed_dir(handle_t *handle, struct dentry *dentry,
        struct dx_frame frames[2], *frame;
        struct dx_entry *entries;
        struct ext4_dir_entry_2 *de, *de2;
+       struct ext4_dir_entry_tail *t;
        char            *data1, *top;
        unsigned        len;
        int             retval;
@@ -1346,6 +1664,11 @@ static int make_indexed_dir(handle_t *handle, struct dentry *dentry,
        struct dx_hash_info hinfo;
        ext4_lblk_t  block;
        struct fake_dirent *fde;
+       int             csum_size = 0;
+
+       if (EXT4_HAS_RO_COMPAT_FEATURE(inode->i_sb,
+                                      EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+               csum_size = sizeof(struct ext4_dir_entry_tail);
 
        blocksize =  dir->i_sb->s_blocksize;
        dxtrace(printk(KERN_DEBUG "Creating index: inode %lu\n", dir->i_ino));
@@ -1366,7 +1689,7 @@ static int make_indexed_dir(handle_t *handle, struct dentry *dentry,
                brelse(bh);
                return -EIO;
        }
-       len = ((char *) root) + blocksize - (char *) de;
+       len = ((char *) root) + (blocksize - csum_size) - (char *) de;
 
        /* Allocate new block for the 0th block's dirents */
        bh2 = ext4_append(handle, dir, &block, &retval);
@@ -1382,8 +1705,15 @@ static int make_indexed_dir(handle_t *handle, struct dentry *dentry,
        top = data1 + len;
        while ((char *)(de2 = ext4_next_entry(de, blocksize)) < top)
                de = de2;
-       de->rec_len = ext4_rec_len_to_disk(data1 + blocksize - (char *) de,
+       de->rec_len = ext4_rec_len_to_disk(data1 + (blocksize - csum_size) -
+                                          (char *) de,
                                           blocksize);
+
+       if (csum_size) {
+               t = EXT4_DIRENT_TAIL(data1, blocksize);
+               initialize_dirent_tail(t, blocksize);
+       }
+
        /* Initialize the root; the dot dirents already exist */
        de = (struct ext4_dir_entry_2 *) (&root->dotdot);
        de->rec_len = ext4_rec_len_to_disk(blocksize - EXT4_DIR_REC_LEN(2),
@@ -1408,8 +1738,8 @@ static int make_indexed_dir(handle_t *handle, struct dentry *dentry,
        frame->bh = bh;
        bh = bh2;
 
-       ext4_handle_dirty_metadata(handle, dir, frame->bh);
-       ext4_handle_dirty_metadata(handle, dir, bh);
+       ext4_handle_dirty_dx_node(handle, dir, frame->bh);
+       ext4_handle_dirty_dirent_node(handle, dir, bh);
 
        de = do_split(handle,dir, &bh, frame, &hinfo, &retval);
        if (!de) {
@@ -1445,11 +1775,17 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry,
        struct inode *dir = dentry->d_parent->d_inode;
        struct buffer_head *bh;
        struct ext4_dir_entry_2 *de;
+       struct ext4_dir_entry_tail *t;
        struct super_block *sb;
        int     retval;
        int     dx_fallback=0;
        unsigned blocksize;
        ext4_lblk_t block, blocks;
+       int     csum_size = 0;
+
+       if (EXT4_HAS_RO_COMPAT_FEATURE(inode->i_sb,
+                                      EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+               csum_size = sizeof(struct ext4_dir_entry_tail);
 
        sb = dir->i_sb;
        blocksize = sb->s_blocksize;
@@ -1468,6 +1804,11 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry,
                bh = ext4_bread(handle, dir, block, 0, &retval);
                if(!bh)
                        return retval;
+               if (!buffer_verified(bh) &&
+                   !ext4_dirent_csum_verify(dir,
+                               (struct ext4_dir_entry *)bh->b_data))
+                       return -EIO;
+               set_buffer_verified(bh);
                retval = add_dirent_to_buf(handle, dentry, inode, NULL, bh);
                if (retval != -ENOSPC) {
                        brelse(bh);
@@ -1484,7 +1825,13 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry,
                return retval;
        de = (struct ext4_dir_entry_2 *) bh->b_data;
        de->inode = 0;
-       de->rec_len = ext4_rec_len_to_disk(blocksize, blocksize);
+       de->rec_len = ext4_rec_len_to_disk(blocksize - csum_size, blocksize);
+
+       if (csum_size) {
+               t = EXT4_DIRENT_TAIL(bh->b_data, blocksize);
+               initialize_dirent_tail(t, blocksize);
+       }
+
        retval = add_dirent_to_buf(handle, dentry, inode, de, bh);
        brelse(bh);
        if (retval == 0)
@@ -1516,6 +1863,11 @@ static int ext4_dx_add_entry(handle_t *handle, struct dentry *dentry,
        if (!(bh = ext4_bread(handle,dir, dx_get_block(frame->at), 0, &err)))
                goto cleanup;
 
+       if (!buffer_verified(bh) &&
+           !ext4_dirent_csum_verify(dir, (struct ext4_dir_entry *)bh->b_data))
+               goto journal_error;
+       set_buffer_verified(bh);
+
        BUFFER_TRACE(bh, "get_write_access");
        err = ext4_journal_get_write_access(handle, bh);
        if (err)
@@ -1583,7 +1935,7 @@ static int ext4_dx_add_entry(handle_t *handle, struct dentry *dentry,
                        dxtrace(dx_show_index("node", frames[1].entries));
                        dxtrace(dx_show_index("node",
                               ((struct dx_node *) bh2->b_data)->entries));
-                       err = ext4_handle_dirty_metadata(handle, dir, bh2);
+                       err = ext4_handle_dirty_dx_node(handle, dir, bh2);
                        if (err)
                                goto journal_error;
                        brelse (bh2);
@@ -1609,7 +1961,7 @@ static int ext4_dx_add_entry(handle_t *handle, struct dentry *dentry,
                        if (err)
                                goto journal_error;
                }
-               err = ext4_handle_dirty_metadata(handle, dir, frames[0].bh);
+               err = ext4_handle_dirty_dx_node(handle, dir, frames[0].bh);
                if (err) {
                        ext4_std_error(inode->i_sb, err);
                        goto cleanup;
@@ -1641,12 +1993,17 @@ static int ext4_delete_entry(handle_t *handle,
 {
        struct ext4_dir_entry_2 *de, *pde;
        unsigned int blocksize = dir->i_sb->s_blocksize;
+       int csum_size = 0;
        int i, err;
 
+       if (EXT4_HAS_RO_COMPAT_FEATURE(dir->i_sb,
+                                      EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+               csum_size = sizeof(struct ext4_dir_entry_tail);
+
        i = 0;
        pde = NULL;
        de = (struct ext4_dir_entry_2 *) bh->b_data;
-       while (i < bh->b_size) {
+       while (i < bh->b_size - csum_size) {
                if (ext4_check_dir_entry(dir, NULL, de, bh, i))
                        return -EIO;
                if (de == de_del)  {
@@ -1667,7 +2024,7 @@ static int ext4_delete_entry(handle_t *handle,
                                de->inode = 0;
                        dir->i_version++;
                        BUFFER_TRACE(bh, "call ext4_handle_dirty_metadata");
-                       err = ext4_handle_dirty_metadata(handle, dir, bh);
+                       err = ext4_handle_dirty_dirent_node(handle, dir, bh);
                        if (unlikely(err)) {
                                ext4_std_error(dir->i_sb, err);
                                return err;
@@ -1809,9 +2166,15 @@ static int ext4_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
        struct inode *inode;
        struct buffer_head *dir_block = NULL;
        struct ext4_dir_entry_2 *de;
+       struct ext4_dir_entry_tail *t;
        unsigned int blocksize = dir->i_sb->s_blocksize;
+       int csum_size = 0;
        int err, retries = 0;
 
+       if (EXT4_HAS_RO_COMPAT_FEATURE(dir->i_sb,
+                                      EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+               csum_size = sizeof(struct ext4_dir_entry_tail);
+
        if (EXT4_DIR_LINK_MAX(dir))
                return -EMLINK;
 
@@ -1852,16 +2215,24 @@ retry:
        ext4_set_de_type(dir->i_sb, de, S_IFDIR);
        de = ext4_next_entry(de, blocksize);
        de->inode = cpu_to_le32(dir->i_ino);
-       de->rec_len = ext4_rec_len_to_disk(blocksize - EXT4_DIR_REC_LEN(1),
+       de->rec_len = ext4_rec_len_to_disk(blocksize -
+                                          (csum_size + EXT4_DIR_REC_LEN(1)),
                                           blocksize);
        de->name_len = 2;
        strcpy(de->name, "..");
        ext4_set_de_type(dir->i_sb, de, S_IFDIR);
        set_nlink(inode, 2);
+
+       if (csum_size) {
+               t = EXT4_DIRENT_TAIL(dir_block->b_data, blocksize);
+               initialize_dirent_tail(t, blocksize);
+       }
+
        BUFFER_TRACE(dir_block, "call ext4_handle_dirty_metadata");
-       err = ext4_handle_dirty_metadata(handle, inode, dir_block);
+       err = ext4_handle_dirty_dirent_node(handle, inode, dir_block);
        if (err)
                goto out_clear_inode;
+       set_buffer_verified(dir_block);
        err = ext4_mark_inode_dirty(handle, inode);
        if (!err)
                err = ext4_add_entry(handle, dentry, inode);
@@ -1911,6 +2282,14 @@ static int empty_dir(struct inode *inode)
                                     inode->i_ino);
                return 1;
        }
+       if (!buffer_verified(bh) &&
+           !ext4_dirent_csum_verify(inode,
+                       (struct ext4_dir_entry *)bh->b_data)) {
+               EXT4_ERROR_INODE(inode, "checksum error reading directory "
+                                "lblock 0");
+               return -EIO;
+       }
+       set_buffer_verified(bh);
        de = (struct ext4_dir_entry_2 *) bh->b_data;
        de1 = ext4_next_entry(de, sb->s_blocksize);
        if (le32_to_cpu(de->inode) != inode->i_ino ||
@@ -1942,6 +2321,14 @@ static int empty_dir(struct inode *inode)
                                offset += sb->s_blocksize;
                                continue;
                        }
+                       if (!buffer_verified(bh) &&
+                           !ext4_dirent_csum_verify(inode,
+                                       (struct ext4_dir_entry *)bh->b_data)) {
+                               EXT4_ERROR_INODE(inode, "checksum error "
+                                                "reading directory lblock 0");
+                               return -EIO;
+                       }
+                       set_buffer_verified(bh);
                        de = (struct ext4_dir_entry_2 *) bh->b_data;
                }
                if (ext4_check_dir_entry(inode, NULL, de, bh, offset)) {
@@ -2010,7 +2397,7 @@ int ext4_orphan_add(handle_t *handle, struct inode *inode)
        /* Insert this inode at the head of the on-disk orphan list... */
        NEXT_ORPHAN(inode) = le32_to_cpu(EXT4_SB(sb)->s_es->s_last_orphan);
        EXT4_SB(sb)->s_es->s_last_orphan = cpu_to_le32(inode->i_ino);
-       err = ext4_handle_dirty_metadata(handle, NULL, EXT4_SB(sb)->s_sbh);
+       err = ext4_handle_dirty_super_now(handle, sb);
        rc = ext4_mark_iloc_dirty(handle, inode, &iloc);
        if (!err)
                err = rc;
@@ -2083,7 +2470,7 @@ int ext4_orphan_del(handle_t *handle, struct inode *inode)
                if (err)
                        goto out_brelse;
                sbi->s_es->s_last_orphan = cpu_to_le32(ino_next);
-               err = ext4_handle_dirty_metadata(handle, NULL, sbi->s_sbh);
+               err = ext4_handle_dirty_super_now(handle, inode->i_sb);
        } else {
                struct ext4_iloc iloc2;
                struct inode *i_prev =
@@ -2442,6 +2829,11 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
                dir_bh = ext4_bread(handle, old_inode, 0, 0, &retval);
                if (!dir_bh)
                        goto end_rename;
+               if (!buffer_verified(dir_bh) &&
+                   !ext4_dirent_csum_verify(old_inode,
+                               (struct ext4_dir_entry *)dir_bh->b_data))
+                       goto end_rename;
+               set_buffer_verified(dir_bh);
                if (le32_to_cpu(PARENT_INO(dir_bh->b_data,
                                old_dir->i_sb->s_blocksize)) != old_dir->i_ino)
                        goto end_rename;
@@ -2472,7 +2864,7 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
                                        ext4_current_time(new_dir);
                ext4_mark_inode_dirty(handle, new_dir);
                BUFFER_TRACE(new_bh, "call ext4_handle_dirty_metadata");
-               retval = ext4_handle_dirty_metadata(handle, new_dir, new_bh);
+               retval = ext4_handle_dirty_dirent_node(handle, new_dir, new_bh);
                if (unlikely(retval)) {
                        ext4_std_error(new_dir->i_sb, retval);
                        goto end_rename;
@@ -2526,7 +2918,8 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
                PARENT_INO(dir_bh->b_data, new_dir->i_sb->s_blocksize) =
                                                cpu_to_le32(new_dir->i_ino);
                BUFFER_TRACE(dir_bh, "call ext4_handle_dirty_metadata");
-               retval = ext4_handle_dirty_metadata(handle, old_inode, dir_bh);
+               retval = ext4_handle_dirty_dirent_node(handle, old_inode,
+                                                      dir_bh);
                if (retval) {
                        ext4_std_error(old_dir->i_sb, retval);
                        goto end_rename;
index 59fa0be272516adf6cbbc94384106690bf710c65..7ea6cbb44121952bf0d4f81f914950ab284dba6b 100644 (file)
@@ -161,6 +161,8 @@ static struct ext4_new_flex_group_data *alloc_flex_gd(unsigned long flexbg_size)
        if (flex_gd == NULL)
                goto out3;
 
+       if (flexbg_size >= UINT_MAX / sizeof(struct ext4_new_flex_group_data))
+               goto out2;
        flex_gd->count = flexbg_size;
 
        flex_gd->groups = kmalloc(sizeof(struct ext4_new_group_data) *
@@ -796,7 +798,7 @@ static int add_new_gdb(handle_t *handle, struct inode *inode,
        ext4_kvfree(o_group_desc);
 
        le16_add_cpu(&es->s_reserved_gdt_blocks, -1);
-       err = ext4_handle_dirty_metadata(handle, NULL, EXT4_SB(sb)->s_sbh);
+       err = ext4_handle_dirty_super_now(handle, sb);
        if (err)
                ext4_std_error(sb, err);
 
@@ -968,6 +970,8 @@ static void update_backups(struct super_block *sb,
                goto exit_err;
        }
 
+       ext4_superblock_csum_set(sb, (struct ext4_super_block *)data);
+
        while ((group = ext4_list_backups(sb, &three, &five, &seven)) < last) {
                struct buffer_head *bh;
 
@@ -1067,6 +1071,54 @@ static int ext4_add_new_descs(handle_t *handle, struct super_block *sb,
        return err;
 }
 
+static struct buffer_head *ext4_get_bitmap(struct super_block *sb, __u64 block)
+{
+       struct buffer_head *bh = sb_getblk(sb, block);
+       if (!bh)
+               return NULL;
+
+       if (bitmap_uptodate(bh))
+               return bh;
+
+       lock_buffer(bh);
+       if (bh_submit_read(bh) < 0) {
+               unlock_buffer(bh);
+               brelse(bh);
+               return NULL;
+       }
+       unlock_buffer(bh);
+
+       return bh;
+}
+
+static int ext4_set_bitmap_checksums(struct super_block *sb,
+                                    ext4_group_t group,
+                                    struct ext4_group_desc *gdp,
+                                    struct ext4_new_group_data *group_data)
+{
+       struct buffer_head *bh;
+
+       if (!EXT4_HAS_RO_COMPAT_FEATURE(sb,
+                                       EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+               return 0;
+
+       bh = ext4_get_bitmap(sb, group_data->inode_bitmap);
+       if (!bh)
+               return -EIO;
+       ext4_inode_bitmap_csum_set(sb, group, gdp, bh,
+                                  EXT4_INODES_PER_GROUP(sb) / 8);
+       brelse(bh);
+
+       bh = ext4_get_bitmap(sb, group_data->block_bitmap);
+       if (!bh)
+               return -EIO;
+       ext4_block_bitmap_csum_set(sb, group, gdp, bh,
+                                  EXT4_BLOCKS_PER_GROUP(sb) / 8);
+       brelse(bh);
+
+       return 0;
+}
+
 /*
  * ext4_setup_new_descs() will set up the group descriptor descriptors of a flex bg
  */
@@ -1093,18 +1145,24 @@ static int ext4_setup_new_descs(handle_t *handle, struct super_block *sb,
                 */
                gdb_bh = sbi->s_group_desc[gdb_num];
                /* Update group descriptor block for new group */
-               gdp = (struct ext4_group_desc *)((char *)gdb_bh->b_data +
+               gdp = (struct ext4_group_desc *)(gdb_bh->b_data +
                                                 gdb_off * EXT4_DESC_SIZE(sb));
 
                memset(gdp, 0, EXT4_DESC_SIZE(sb));
                ext4_block_bitmap_set(sb, gdp, group_data->block_bitmap);
                ext4_inode_bitmap_set(sb, gdp, group_data->inode_bitmap);
+               err = ext4_set_bitmap_checksums(sb, group, gdp, group_data);
+               if (err) {
+                       ext4_std_error(sb, err);
+                       break;
+               }
+
                ext4_inode_table_set(sb, gdp, group_data->inode_table);
                ext4_free_group_clusters_set(sb, gdp,
                                             EXT4_B2C(sbi, group_data->free_blocks_count));
                ext4_free_inodes_set(sb, gdp, EXT4_INODES_PER_GROUP(sb));
                gdp->bg_flags = cpu_to_le16(*bg_flags);
-               gdp->bg_checksum = ext4_group_desc_csum(sbi, group, gdp);
+               ext4_group_desc_csum_set(sb, group, gdp);
 
                err = ext4_handle_dirty_metadata(handle, NULL, gdb_bh);
                if (unlikely(err)) {
@@ -1343,17 +1401,14 @@ static int ext4_setup_next_flex_gd(struct super_block *sb,
                           (1 + ext4_bg_num_gdb(sb, group + i) +
                            le16_to_cpu(es->s_reserved_gdt_blocks)) : 0;
                group_data[i].free_blocks_count = blocks_per_group - overhead;
-               if (EXT4_HAS_RO_COMPAT_FEATURE(sb,
-                                              EXT4_FEATURE_RO_COMPAT_GDT_CSUM))
+               if (ext4_has_group_desc_csum(sb))
                        flex_gd->bg_flags[i] = EXT4_BG_BLOCK_UNINIT |
                                               EXT4_BG_INODE_UNINIT;
                else
                        flex_gd->bg_flags[i] = EXT4_BG_INODE_ZEROED;
        }
 
-       if (last_group == n_group &&
-           EXT4_HAS_RO_COMPAT_FEATURE(sb,
-                                      EXT4_FEATURE_RO_COMPAT_GDT_CSUM))
+       if (last_group == n_group && ext4_has_group_desc_csum(sb))
                /* We need to initialize block bitmap of last group. */
                flex_gd->bg_flags[i - 1] &= ~EXT4_BG_BLOCK_UNINIT;
 
index 35b5954489eeb88c6c5a29fd76fced5c3472e6f5..eb7aa3e4ef05caf136f24e0565a28e6d1e0a1539 100644 (file)
@@ -112,6 +112,48 @@ static struct file_system_type ext3_fs_type = {
 #define IS_EXT3_SB(sb) (0)
 #endif
 
+static int ext4_verify_csum_type(struct super_block *sb,
+                                struct ext4_super_block *es)
+{
+       if (!EXT4_HAS_RO_COMPAT_FEATURE(sb,
+                                       EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+               return 1;
+
+       return es->s_checksum_type == EXT4_CRC32C_CHKSUM;
+}
+
+static __le32 ext4_superblock_csum(struct super_block *sb,
+                                  struct ext4_super_block *es)
+{
+       struct ext4_sb_info *sbi = EXT4_SB(sb);
+       int offset = offsetof(struct ext4_super_block, s_checksum);
+       __u32 csum;
+
+       csum = ext4_chksum(sbi, ~0, (char *)es, offset);
+
+       return cpu_to_le32(csum);
+}
+
+int ext4_superblock_csum_verify(struct super_block *sb,
+                               struct ext4_super_block *es)
+{
+       if (!EXT4_HAS_RO_COMPAT_FEATURE(sb,
+                                      EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+               return 1;
+
+       return es->s_checksum == ext4_superblock_csum(sb, es);
+}
+
+void ext4_superblock_csum_set(struct super_block *sb,
+                             struct ext4_super_block *es)
+{
+       if (!EXT4_HAS_RO_COMPAT_FEATURE(sb,
+               EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+               return;
+
+       es->s_checksum = ext4_superblock_csum(sb, es);
+}
+
 void *ext4_kvmalloc(size_t size, gfp_t flags)
 {
        void *ret;
@@ -497,6 +539,7 @@ void __ext4_error(struct super_block *sb, const char *function,
        printk(KERN_CRIT "EXT4-fs error (device %s): %s:%d: comm %s: %pV\n",
               sb->s_id, function, line, current->comm, &vaf);
        va_end(args);
+       save_error_info(sb, function, line);
 
        ext4_handle_error(sb);
 }
@@ -905,6 +948,8 @@ static void ext4_put_super(struct super_block *sb)
        unlock_super(sb);
        kobject_put(&sbi->s_kobj);
        wait_for_completion(&sbi->s_kobj_unregister);
+       if (sbi->s_chksum_driver)
+               crypto_free_shash(sbi->s_chksum_driver);
        kfree(sbi->s_blockgroup_lock);
        kfree(sbi);
 }
@@ -1922,43 +1967,69 @@ failed:
        return 0;
 }
 
-__le16 ext4_group_desc_csum(struct ext4_sb_info *sbi, __u32 block_group,
-                           struct ext4_group_desc *gdp)
+static __le16 ext4_group_desc_csum(struct ext4_sb_info *sbi, __u32 block_group,
+                                  struct ext4_group_desc *gdp)
 {
+       int offset;
        __u16 crc = 0;
+       __le32 le_group = cpu_to_le32(block_group);
 
-       if (sbi->s_es->s_feature_ro_compat &
-           cpu_to_le32(EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) {
-               int offset = offsetof(struct ext4_group_desc, bg_checksum);
-               __le32 le_group = cpu_to_le32(block_group);
-
-               crc = crc16(~0, sbi->s_es->s_uuid, sizeof(sbi->s_es->s_uuid));
-               crc = crc16(crc, (__u8 *)&le_group, sizeof(le_group));
-               crc = crc16(crc, (__u8 *)gdp, offset);
-               offset += sizeof(gdp->bg_checksum); /* skip checksum */
-               /* for checksum of struct ext4_group_desc do the rest...*/
-               if ((sbi->s_es->s_feature_incompat &
-                    cpu_to_le32(EXT4_FEATURE_INCOMPAT_64BIT)) &&
-                   offset < le16_to_cpu(sbi->s_es->s_desc_size))
-                       crc = crc16(crc, (__u8 *)gdp + offset,
-                                   le16_to_cpu(sbi->s_es->s_desc_size) -
-                                       offset);
+       if ((sbi->s_es->s_feature_ro_compat &
+            cpu_to_le32(EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))) {
+               /* Use new metadata_csum algorithm */
+               __u16 old_csum;
+               __u32 csum32;
+
+               old_csum = gdp->bg_checksum;
+               gdp->bg_checksum = 0;
+               csum32 = ext4_chksum(sbi, sbi->s_csum_seed, (__u8 *)&le_group,
+                                    sizeof(le_group));
+               csum32 = ext4_chksum(sbi, csum32, (__u8 *)gdp,
+                                    sbi->s_desc_size);
+               gdp->bg_checksum = old_csum;
+
+               crc = csum32 & 0xFFFF;
+               goto out;
        }
 
+       /* old crc16 code */
+       offset = offsetof(struct ext4_group_desc, bg_checksum);
+
+       crc = crc16(~0, sbi->s_es->s_uuid, sizeof(sbi->s_es->s_uuid));
+       crc = crc16(crc, (__u8 *)&le_group, sizeof(le_group));
+       crc = crc16(crc, (__u8 *)gdp, offset);
+       offset += sizeof(gdp->bg_checksum); /* skip checksum */
+       /* for checksum of struct ext4_group_desc do the rest...*/
+       if ((sbi->s_es->s_feature_incompat &
+            cpu_to_le32(EXT4_FEATURE_INCOMPAT_64BIT)) &&
+           offset < le16_to_cpu(sbi->s_es->s_desc_size))
+               crc = crc16(crc, (__u8 *)gdp + offset,
+                           le16_to_cpu(sbi->s_es->s_desc_size) -
+                               offset);
+
+out:
        return cpu_to_le16(crc);
 }
 
-int ext4_group_desc_csum_verify(struct ext4_sb_info *sbi, __u32 block_group,
+int ext4_group_desc_csum_verify(struct super_block *sb, __u32 block_group,
                                struct ext4_group_desc *gdp)
 {
-       if ((sbi->s_es->s_feature_ro_compat &
-            cpu_to_le32(EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) &&
-           (gdp->bg_checksum != ext4_group_desc_csum(sbi, block_group, gdp)))
+       if (ext4_has_group_desc_csum(sb) &&
+           (gdp->bg_checksum != ext4_group_desc_csum(EXT4_SB(sb),
+                                                     block_group, gdp)))
                return 0;
 
        return 1;
 }
 
+void ext4_group_desc_csum_set(struct super_block *sb, __u32 block_group,
+                             struct ext4_group_desc *gdp)
+{
+       if (!ext4_has_group_desc_csum(sb))
+               return;
+       gdp->bg_checksum = ext4_group_desc_csum(EXT4_SB(sb), block_group, gdp);
+}
+
 /* Called at mount-time, super-block is locked */
 static int ext4_check_descriptors(struct super_block *sb,
                                  ext4_group_t *first_not_zeroed)
@@ -2013,7 +2084,7 @@ static int ext4_check_descriptors(struct super_block *sb,
                        return 0;
                }
                ext4_lock_group(sb, i);
-               if (!ext4_group_desc_csum_verify(sbi, i, gdp)) {
+               if (!ext4_group_desc_csum_verify(sb, i, gdp)) {
                        ext4_msg(sb, KERN_ERR, "ext4_check_descriptors: "
                                 "Checksum for group %u failed (%u!=%u)",
                                 i, le16_to_cpu(ext4_group_desc_csum(sbi, i,
@@ -2417,6 +2488,23 @@ static ssize_t sbi_ui_store(struct ext4_attr *a,
        return count;
 }
 
+static ssize_t trigger_test_error(struct ext4_attr *a,
+                                 struct ext4_sb_info *sbi,
+                                 const char *buf, size_t count)
+{
+       int len = count;
+
+       if (!capable(CAP_SYS_ADMIN))
+               return -EPERM;
+
+       if (len && buf[len-1] == '\n')
+               len--;
+
+       if (len)
+               ext4_error(sbi->s_sb, "%.*s", len, buf);
+       return count;
+}
+
 #define EXT4_ATTR_OFFSET(_name,_mode,_show,_store,_elname) \
 static struct ext4_attr ext4_attr_##_name = {                  \
        .attr = {.name = __stringify(_name), .mode = _mode },   \
@@ -2447,6 +2535,7 @@ EXT4_RW_ATTR_SBI_UI(mb_order2_req, s_mb_order2_reqs);
 EXT4_RW_ATTR_SBI_UI(mb_stream_req, s_mb_stream_request);
 EXT4_RW_ATTR_SBI_UI(mb_group_prealloc, s_mb_group_prealloc);
 EXT4_RW_ATTR_SBI_UI(max_writeback_mb_bump, s_max_writeback_mb_bump);
+EXT4_ATTR(trigger_fs_error, 0200, NULL, trigger_test_error);
 
 static struct attribute *ext4_attrs[] = {
        ATTR_LIST(delayed_allocation_blocks),
@@ -2461,6 +2550,7 @@ static struct attribute *ext4_attrs[] = {
        ATTR_LIST(mb_stream_req),
        ATTR_LIST(mb_group_prealloc),
        ATTR_LIST(max_writeback_mb_bump),
+       ATTR_LIST(trigger_fs_error),
        NULL,
 };
 
@@ -2957,6 +3047,44 @@ static void ext4_destroy_lazyinit_thread(void)
        kthread_stop(ext4_lazyinit_task);
 }
 
+static int set_journal_csum_feature_set(struct super_block *sb)
+{
+       int ret = 1;
+       int compat, incompat;
+       struct ext4_sb_info *sbi = EXT4_SB(sb);
+
+       if (EXT4_HAS_RO_COMPAT_FEATURE(sb,
+                                      EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) {
+               /* journal checksum v2 */
+               compat = 0;
+               incompat = JBD2_FEATURE_INCOMPAT_CSUM_V2;
+       } else {
+               /* journal checksum v1 */
+               compat = JBD2_FEATURE_COMPAT_CHECKSUM;
+               incompat = 0;
+       }
+
+       if (test_opt(sb, JOURNAL_ASYNC_COMMIT)) {
+               ret = jbd2_journal_set_features(sbi->s_journal,
+                               compat, 0,
+                               JBD2_FEATURE_INCOMPAT_ASYNC_COMMIT |
+                               incompat);
+       } else if (test_opt(sb, JOURNAL_CHECKSUM)) {
+               ret = jbd2_journal_set_features(sbi->s_journal,
+                               compat, 0,
+                               incompat);
+               jbd2_journal_clear_features(sbi->s_journal, 0, 0,
+                               JBD2_FEATURE_INCOMPAT_ASYNC_COMMIT);
+       } else {
+               jbd2_journal_clear_features(sbi->s_journal,
+                               JBD2_FEATURE_COMPAT_CHECKSUM, 0,
+                               JBD2_FEATURE_INCOMPAT_ASYNC_COMMIT |
+                               JBD2_FEATURE_INCOMPAT_CSUM_V2);
+       }
+
+       return ret;
+}
+
 static int ext4_fill_super(struct super_block *sb, void *data, int silent)
 {
        char *orig_data = kstrdup(data, GFP_KERNEL);
@@ -2993,6 +3121,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
                goto out_free_orig;
        }
        sb->s_fs_info = sbi;
+       sbi->s_sb = sb;
        sbi->s_mount_opt = 0;
        sbi->s_resuid = make_kuid(&init_user_ns, EXT4_DEF_RESUID);
        sbi->s_resgid = make_kgid(&init_user_ns, EXT4_DEF_RESGID);
@@ -3032,13 +3161,54 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
         * Note: s_es must be initialized as soon as possible because
         *       some ext4 macro-instructions depend on its value
         */
-       es = (struct ext4_super_block *) (((char *)bh->b_data) + offset);
+       es = (struct ext4_super_block *) (bh->b_data + offset);
        sbi->s_es = es;
        sb->s_magic = le16_to_cpu(es->s_magic);
        if (sb->s_magic != EXT4_SUPER_MAGIC)
                goto cantfind_ext4;
        sbi->s_kbytes_written = le64_to_cpu(es->s_kbytes_written);
 
+       /* Warn if metadata_csum and gdt_csum are both set. */
+       if (EXT4_HAS_RO_COMPAT_FEATURE(sb,
+                                      EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) &&
+           EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_GDT_CSUM))
+               ext4_warning(sb, KERN_INFO "metadata_csum and uninit_bg are "
+                            "redundant flags; please run fsck.");
+
+       /* Check for a known checksum algorithm */
+       if (!ext4_verify_csum_type(sb, es)) {
+               ext4_msg(sb, KERN_ERR, "VFS: Found ext4 filesystem with "
+                        "unknown checksum algorithm.");
+               silent = 1;
+               goto cantfind_ext4;
+       }
+
+       /* Load the checksum driver */
+       if (EXT4_HAS_RO_COMPAT_FEATURE(sb,
+                                      EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) {
+               sbi->s_chksum_driver = crypto_alloc_shash("crc32c", 0, 0);
+               if (IS_ERR(sbi->s_chksum_driver)) {
+                       ext4_msg(sb, KERN_ERR, "Cannot load crc32c driver.");
+                       ret = PTR_ERR(sbi->s_chksum_driver);
+                       sbi->s_chksum_driver = NULL;
+                       goto failed_mount;
+               }
+       }
+
+       /* Check superblock checksum */
+       if (!ext4_superblock_csum_verify(sb, es)) {
+               ext4_msg(sb, KERN_ERR, "VFS: Found ext4 filesystem with "
+                        "invalid superblock checksum.  Run e2fsck?");
+               silent = 1;
+               goto cantfind_ext4;
+       }
+
+       /* Precompute checksum seed for all metadata */
+       if (EXT4_HAS_RO_COMPAT_FEATURE(sb,
+                       EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+               sbi->s_csum_seed = ext4_chksum(sbi, ~0, es->s_uuid,
+                                              sizeof(es->s_uuid));
+
        /* Set defaults before we parse the mount options */
        def_mount_opts = le32_to_cpu(es->s_default_mount_opts);
        set_opt(sb, INIT_INODE_TABLE);
@@ -3200,7 +3370,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
                               "Can't read superblock on 2nd try");
                        goto failed_mount;
                }
-               es = (struct ext4_super_block *)(((char *)bh->b_data) + offset);
+               es = (struct ext4_super_block *)(bh->b_data + offset);
                sbi->s_es = es;
                if (es->s_magic != cpu_to_le16(EXT4_SUPER_MAGIC)) {
                        ext4_msg(sb, KERN_ERR,
@@ -3392,6 +3562,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
                                          GFP_KERNEL);
        if (sbi->s_group_desc == NULL) {
                ext4_msg(sb, KERN_ERR, "not enough memory");
+               ret = -ENOMEM;
                goto failed_mount;
        }
 
@@ -3449,6 +3620,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
        }
        if (err) {
                ext4_msg(sb, KERN_ERR, "insufficient memory");
+               ret = err;
                goto failed_mount3;
        }
 
@@ -3506,26 +3678,17 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
                goto no_journal;
        }
 
-       if (ext4_blocks_count(es) > 0xffffffffULL &&
+       if (EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_64BIT) &&
            !jbd2_journal_set_features(EXT4_SB(sb)->s_journal, 0, 0,
                                       JBD2_FEATURE_INCOMPAT_64BIT)) {
                ext4_msg(sb, KERN_ERR, "Failed to set 64-bit journal feature");
                goto failed_mount_wq;
        }
 
-       if (test_opt(sb, JOURNAL_ASYNC_COMMIT)) {
-               jbd2_journal_set_features(sbi->s_journal,
-                               JBD2_FEATURE_COMPAT_CHECKSUM, 0,
-                               JBD2_FEATURE_INCOMPAT_ASYNC_COMMIT);
-       } else if (test_opt(sb, JOURNAL_CHECKSUM)) {
-               jbd2_journal_set_features(sbi->s_journal,
-                               JBD2_FEATURE_COMPAT_CHECKSUM, 0, 0);
-               jbd2_journal_clear_features(sbi->s_journal, 0, 0,
-                               JBD2_FEATURE_INCOMPAT_ASYNC_COMMIT);
-       } else {
-               jbd2_journal_clear_features(sbi->s_journal,
-                               JBD2_FEATURE_COMPAT_CHECKSUM, 0,
-                               JBD2_FEATURE_INCOMPAT_ASYNC_COMMIT);
+       if (!set_journal_csum_feature_set(sb)) {
+               ext4_msg(sb, KERN_ERR, "Failed to set journal checksum "
+                        "feature set");
+               goto failed_mount_wq;
        }
 
        /* We have now updated the journal if required, so we can
@@ -3606,7 +3769,8 @@ no_journal:
                goto failed_mount4;
        }
 
-       ext4_setup_super(sb, es, sb->s_flags & MS_RDONLY);
+       if (ext4_setup_super(sb, es, sb->s_flags & MS_RDONLY))
+               sb->s_flags |= MS_RDONLY;
 
        /* determine the minimum size of new large inodes, if present */
        if (sbi->s_inode_size > EXT4_GOOD_OLD_INODE_SIZE) {
@@ -3641,7 +3805,7 @@ no_journal:
        }
 
        ext4_ext_init(sb);
-       err = ext4_mb_init(sb, needs_recovery);
+       err = ext4_mb_init(sb);
        if (err) {
                ext4_msg(sb, KERN_ERR, "failed to initialize mballoc (%d)",
                         err);
@@ -3724,6 +3888,8 @@ failed_mount2:
                brelse(sbi->s_group_desc[i]);
        ext4_kvfree(sbi->s_group_desc);
 failed_mount:
+       if (sbi->s_chksum_driver)
+               crypto_free_shash(sbi->s_chksum_driver);
        if (sbi->s_proc) {
                remove_proc_entry("options", sbi->s_proc);
                remove_proc_entry(sb->s_id, ext4_proc_root);
@@ -3847,7 +4013,7 @@ static journal_t *ext4_get_dev_journal(struct super_block *sb,
                goto out_bdev;
        }
 
-       es = (struct ext4_super_block *) (((char *)bh->b_data) + offset);
+       es = (struct ext4_super_block *) (bh->b_data + offset);
        if ((le16_to_cpu(es->s_magic) != EXT4_SUPER_MAGIC) ||
            !(le32_to_cpu(es->s_feature_incompat) &
              EXT4_FEATURE_INCOMPAT_JOURNAL_DEV)) {
@@ -4039,6 +4205,7 @@ static int ext4_commit_super(struct super_block *sb, int sync)
                                &EXT4_SB(sb)->s_freeinodes_counter));
        sb->s_dirt = 0;
        BUFFER_TRACE(sbh, "marking dirty");
+       ext4_superblock_csum_set(sb, es);
        mark_buffer_dirty(sbh);
        if (sync) {
                error = sync_dirty_buffer(sbh);
@@ -4333,7 +4500,7 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
                                struct ext4_group_desc *gdp =
                                        ext4_get_group_desc(sb, g, NULL);
 
-                               if (!ext4_group_desc_csum_verify(sbi, g, gdp)) {
+                               if (!ext4_group_desc_csum_verify(sb, g, gdp)) {
                                        ext4_msg(sb, KERN_ERR,
               "ext4_remount: Checksum for group %u failed (%u!=%u)",
                g, le16_to_cpu(ext4_group_desc_csum(sbi, g, gdp)),
index e88748e55c0f246e90ca21c2094303719f83df07..e56c9ed7d6e30d523b7f8e4b638f9190427cf50d 100644 (file)
@@ -122,6 +122,58 @@ const struct xattr_handler *ext4_xattr_handlers[] = {
        NULL
 };
 
+static __le32 ext4_xattr_block_csum(struct inode *inode,
+                                   sector_t block_nr,
+                                   struct ext4_xattr_header *hdr)
+{
+       struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
+       struct ext4_inode_info *ei = EXT4_I(inode);
+       __u32 csum, old;
+
+       old = hdr->h_checksum;
+       hdr->h_checksum = 0;
+       if (le32_to_cpu(hdr->h_refcount) != 1) {
+               block_nr = cpu_to_le64(block_nr);
+               csum = ext4_chksum(sbi, sbi->s_csum_seed, (__u8 *)&block_nr,
+                                  sizeof(block_nr));
+       } else
+               csum = ei->i_csum_seed;
+       csum = ext4_chksum(sbi, csum, (__u8 *)hdr,
+                          EXT4_BLOCK_SIZE(inode->i_sb));
+       hdr->h_checksum = old;
+       return cpu_to_le32(csum);
+}
+
+static int ext4_xattr_block_csum_verify(struct inode *inode,
+                                       sector_t block_nr,
+                                       struct ext4_xattr_header *hdr)
+{
+       if (EXT4_HAS_RO_COMPAT_FEATURE(inode->i_sb,
+               EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) &&
+           (hdr->h_checksum != ext4_xattr_block_csum(inode, block_nr, hdr)))
+               return 0;
+       return 1;
+}
+
+static void ext4_xattr_block_csum_set(struct inode *inode,
+                                     sector_t block_nr,
+                                     struct ext4_xattr_header *hdr)
+{
+       if (!EXT4_HAS_RO_COMPAT_FEATURE(inode->i_sb,
+               EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+               return;
+
+       hdr->h_checksum = ext4_xattr_block_csum(inode, block_nr, hdr);
+}
+
+static inline int ext4_handle_dirty_xattr_block(handle_t *handle,
+                                               struct inode *inode,
+                                               struct buffer_head *bh)
+{
+       ext4_xattr_block_csum_set(inode, bh->b_blocknr, BHDR(bh));
+       return ext4_handle_dirty_metadata(handle, inode, bh);
+}
+
 static inline const struct xattr_handler *
 ext4_xattr_handler(int name_index)
 {
@@ -156,12 +208,22 @@ ext4_xattr_check_names(struct ext4_xattr_entry *entry, void *end)
 }
 
 static inline int
-ext4_xattr_check_block(struct buffer_head *bh)
+ext4_xattr_check_block(struct inode *inode, struct buffer_head *bh)
 {
+       int error;
+
+       if (buffer_verified(bh))
+               return 0;
+
        if (BHDR(bh)->h_magic != cpu_to_le32(EXT4_XATTR_MAGIC) ||
            BHDR(bh)->h_blocks != cpu_to_le32(1))
                return -EIO;
-       return ext4_xattr_check_names(BFIRST(bh), bh->b_data + bh->b_size);
+       if (!ext4_xattr_block_csum_verify(inode, bh->b_blocknr, BHDR(bh)))
+               return -EIO;
+       error = ext4_xattr_check_names(BFIRST(bh), bh->b_data + bh->b_size);
+       if (!error)
+               set_buffer_verified(bh);
+       return error;
 }
 
 static inline int
@@ -224,7 +286,7 @@ ext4_xattr_block_get(struct inode *inode, int name_index, const char *name,
                goto cleanup;
        ea_bdebug(bh, "b_count=%d, refcount=%d",
                atomic_read(&(bh->b_count)), le32_to_cpu(BHDR(bh)->h_refcount));
-       if (ext4_xattr_check_block(bh)) {
+       if (ext4_xattr_check_block(inode, bh)) {
 bad_block:
                EXT4_ERROR_INODE(inode, "bad block %llu",
                                 EXT4_I(inode)->i_file_acl);
@@ -369,7 +431,7 @@ ext4_xattr_block_list(struct dentry *dentry, char *buffer, size_t buffer_size)
                goto cleanup;
        ea_bdebug(bh, "b_count=%d, refcount=%d",
                atomic_read(&(bh->b_count)), le32_to_cpu(BHDR(bh)->h_refcount));
-       if (ext4_xattr_check_block(bh)) {
+       if (ext4_xattr_check_block(inode, bh)) {
                EXT4_ERROR_INODE(inode, "bad block %llu",
                                 EXT4_I(inode)->i_file_acl);
                error = -EIO;
@@ -492,7 +554,7 @@ ext4_xattr_release_block(handle_t *handle, struct inode *inode,
                if (ce)
                        mb_cache_entry_release(ce);
                unlock_buffer(bh);
-               error = ext4_handle_dirty_metadata(handle, inode, bh);
+               error = ext4_handle_dirty_xattr_block(handle, inode, bh);
                if (IS_SYNC(inode))
                        ext4_handle_sync(handle);
                dquot_free_block(inode, 1);
@@ -662,7 +724,7 @@ ext4_xattr_block_find(struct inode *inode, struct ext4_xattr_info *i,
                ea_bdebug(bs->bh, "b_count=%d, refcount=%d",
                        atomic_read(&(bs->bh->b_count)),
                        le32_to_cpu(BHDR(bs->bh)->h_refcount));
-               if (ext4_xattr_check_block(bs->bh)) {
+               if (ext4_xattr_check_block(inode, bs->bh)) {
                        EXT4_ERROR_INODE(inode, "bad block %llu",
                                         EXT4_I(inode)->i_file_acl);
                        error = -EIO;
@@ -725,9 +787,9 @@ ext4_xattr_block_set(handle_t *handle, struct inode *inode,
                        if (error == -EIO)
                                goto bad_block;
                        if (!error)
-                               error = ext4_handle_dirty_metadata(handle,
-                                                                  inode,
-                                                                  bs->bh);
+                               error = ext4_handle_dirty_xattr_block(handle,
+                                                                     inode,
+                                                                     bs->bh);
                        if (error)
                                goto cleanup;
                        goto inserted;
@@ -796,9 +858,9 @@ inserted:
                                ea_bdebug(new_bh, "reusing; refcount now=%d",
                                        le32_to_cpu(BHDR(new_bh)->h_refcount));
                                unlock_buffer(new_bh);
-                               error = ext4_handle_dirty_metadata(handle,
-                                                                  inode,
-                                                                  new_bh);
+                               error = ext4_handle_dirty_xattr_block(handle,
+                                                                     inode,
+                                                                     new_bh);
                                if (error)
                                        goto cleanup_dquot;
                        }
@@ -855,8 +917,8 @@ getblk_failed:
                        set_buffer_uptodate(new_bh);
                        unlock_buffer(new_bh);
                        ext4_xattr_cache_insert(new_bh);
-                       error = ext4_handle_dirty_metadata(handle,
-                                                          inode, new_bh);
+                       error = ext4_handle_dirty_xattr_block(handle,
+                                                             inode, new_bh);
                        if (error)
                                goto cleanup;
                }
@@ -1193,7 +1255,7 @@ retry:
                error = -EIO;
                if (!bh)
                        goto cleanup;
-               if (ext4_xattr_check_block(bh)) {
+               if (ext4_xattr_check_block(inode, bh)) {
                        EXT4_ERROR_INODE(inode, "bad block %llu",
                                         EXT4_I(inode)->i_file_acl);
                        error = -EIO;
index 25b7387ff183f880cdb9ccaf2529ca8c0f218a7b..91f31ca7d9af9df24a965c64bb0271c43a4d4b09 100644 (file)
@@ -27,7 +27,9 @@ struct ext4_xattr_header {
        __le32  h_refcount;     /* reference count */
        __le32  h_blocks;       /* number of disk blocks used */
        __le32  h_hash;         /* hash value of all attributes */
-       __u32   h_reserved[4];  /* zero right now */
+       __le32  h_checksum;     /* crc32c(uuid+id+xattrblock) */
+                               /* id = inum if refcount=1, blknum otherwise */
+       __u32   h_reserved[3];  /* zero right now */
 };
 
 struct ext4_xattr_ibody_header {
index aca191bd5f8fa66bcef77c549b647fce81e28b0f..6eaa28c98ad1e9038dd939f2960d31034d8663a3 100644 (file)
@@ -98,8 +98,8 @@ next:
 
        *bh = sb_bread(sb, phys);
        if (*bh == NULL) {
-               fat_msg(sb, KERN_ERR, "Directory bread(block %llu) failed",
-                      (llu)phys);
+               fat_msg_ratelimit(sb, KERN_ERR,
+                       "Directory bread(block %llu) failed", (llu)phys);
                /* skip this block */
                *pos = (iblock + 1) << sb->s_blocksize_bits;
                goto next;
index 66994f316e18d11f052c85b97922cc1407347a70..fc35c5c69136e805b41ffa6102dc1878d68f7a3f 100644 (file)
@@ -82,6 +82,7 @@ struct msdos_sb_info {
        int fatent_shift;
        struct fatent_operations *fatent_ops;
        struct inode *fat_inode;
+       struct inode *fsinfo_inode;
 
        struct ratelimit_state ratelimit;
 
@@ -334,6 +335,11 @@ void __fat_fs_error(struct super_block *sb, int report, const char *fmt, ...);
        __fat_fs_error(sb, __ratelimit(&MSDOS_SB(sb)->ratelimit), fmt , ## args)
 __printf(3, 4) __cold
 void fat_msg(struct super_block *sb, const char *level, const char *fmt, ...);
+#define fat_msg_ratelimit(sb, level, fmt, args...)     \
+       do {    \
+                       if (__ratelimit(&MSDOS_SB(sb)->ratelimit))      \
+                               fat_msg(sb, level, fmt, ## args);       \
+        } while (0)
 extern int fat_clusters_flush(struct super_block *sb);
 extern int fat_chain_add(struct inode *inode, int new_dclus, int nr_cluster);
 extern void fat_time_fat2unix(struct msdos_sb_info *sbi, struct timespec *ts,
index 2e81ac0df7e2eae4b30ad9b2e1db360798f92b2c..31f08ab62c562d1926a75183c802793642cd390c 100644 (file)
@@ -308,6 +308,16 @@ void fat_ent_access_init(struct super_block *sb)
        }
 }
 
+static void mark_fsinfo_dirty(struct super_block *sb)
+{
+       struct msdos_sb_info *sbi = MSDOS_SB(sb);
+
+       if (sb->s_flags & MS_RDONLY || sbi->fat_bits != 32)
+               return;
+
+       __mark_inode_dirty(sbi->fsinfo_inode, I_DIRTY_SYNC);
+}
+
 static inline int fat_ent_update_ptr(struct super_block *sb,
                                     struct fat_entry *fatent,
                                     int offset, sector_t blocknr)
@@ -498,7 +508,6 @@ int fat_alloc_clusters(struct inode *inode, int *cluster, int nr_cluster)
                                sbi->prev_free = entry;
                                if (sbi->free_clusters != -1)
                                        sbi->free_clusters--;
-                               sb->s_dirt = 1;
 
                                cluster[idx_clus] = entry;
                                idx_clus++;
@@ -520,11 +529,11 @@ int fat_alloc_clusters(struct inode *inode, int *cluster, int nr_cluster)
        /* Couldn't allocate the free entries */
        sbi->free_clusters = 0;
        sbi->free_clus_valid = 1;
-       sb->s_dirt = 1;
        err = -ENOSPC;
 
 out:
        unlock_fat(sbi);
+       mark_fsinfo_dirty(sb);
        fatent_brelse(&fatent);
        if (!err) {
                if (inode_needs_sync(inode))
@@ -549,7 +558,7 @@ int fat_free_clusters(struct inode *inode, int cluster)
        struct fat_entry fatent;
        struct buffer_head *bhs[MAX_BUF_PER_PAGE];
        int i, err, nr_bhs;
-       int first_cl = cluster;
+       int first_cl = cluster, dirty_fsinfo = 0;
 
        nr_bhs = 0;
        fatent_init(&fatent);
@@ -587,7 +596,7 @@ int fat_free_clusters(struct inode *inode, int cluster)
                ops->ent_put(&fatent, FAT_ENT_FREE);
                if (sbi->free_clusters != -1) {
                        sbi->free_clusters++;
-                       sb->s_dirt = 1;
+                       dirty_fsinfo = 1;
                }
 
                if (nr_bhs + fatent.nr_bhs > MAX_BUF_PER_PAGE) {
@@ -617,6 +626,8 @@ error:
        for (i = 0; i < nr_bhs; i++)
                brelse(bhs[i]);
        unlock_fat(sbi);
+       if (dirty_fsinfo)
+               mark_fsinfo_dirty(sb);
 
        return err;
 }
@@ -677,7 +688,7 @@ int fat_count_free_clusters(struct super_block *sb)
        }
        sbi->free_clusters = free;
        sbi->free_clus_valid = 1;
-       sb->s_dirt = 1;
+       mark_fsinfo_dirty(sb);
        fatent_brelse(&fatent);
 out:
        unlock_fat(sbi);
index b3d290c1b51392ac949bdf8e00514e29e29bd2e9..a3d81ebf6d864a8c2189147e5771c435473b2e42 100644 (file)
@@ -459,37 +459,11 @@ static void fat_evict_inode(struct inode *inode)
        fat_detach(inode);
 }
 
-static void fat_write_super(struct super_block *sb)
-{
-       lock_super(sb);
-       sb->s_dirt = 0;
-
-       if (!(sb->s_flags & MS_RDONLY))
-               fat_clusters_flush(sb);
-       unlock_super(sb);
-}
-
-static int fat_sync_fs(struct super_block *sb, int wait)
-{
-       int err = 0;
-
-       if (sb->s_dirt) {
-               lock_super(sb);
-               sb->s_dirt = 0;
-               err = fat_clusters_flush(sb);
-               unlock_super(sb);
-       }
-
-       return err;
-}
-
 static void fat_put_super(struct super_block *sb)
 {
        struct msdos_sb_info *sbi = MSDOS_SB(sb);
 
-       if (sb->s_dirt)
-               fat_write_super(sb);
-
+       iput(sbi->fsinfo_inode);
        iput(sbi->fat_inode);
 
        unload_nls(sbi->nls_disk);
@@ -661,7 +635,18 @@ retry:
 
 static int fat_write_inode(struct inode *inode, struct writeback_control *wbc)
 {
-       return __fat_write_inode(inode, wbc->sync_mode == WB_SYNC_ALL);
+       int err;
+
+       if (inode->i_ino == MSDOS_FSINFO_INO) {
+               struct super_block *sb = inode->i_sb;
+
+               lock_super(sb);
+               err = fat_clusters_flush(sb);
+               unlock_super(sb);
+       } else
+               err = __fat_write_inode(inode, wbc->sync_mode == WB_SYNC_ALL);
+
+       return err;
 }
 
 int fat_sync_inode(struct inode *inode)
@@ -678,8 +663,6 @@ static const struct super_operations fat_sops = {
        .write_inode    = fat_write_inode,
        .evict_inode    = fat_evict_inode,
        .put_super      = fat_put_super,
-       .write_super    = fat_write_super,
-       .sync_fs        = fat_sync_fs,
        .statfs         = fat_statfs,
        .remount_fs     = fat_remount,
 
@@ -752,10 +735,9 @@ static struct dentry *fat_fh_to_dentry(struct super_block *sb,
 }
 
 static int
-fat_encode_fh(struct dentry *de, __u32 *fh, int *lenp, int connectable)
+fat_encode_fh(struct inode *inode, __u32 *fh, int *lenp, struct inode *parent)
 {
        int len = *lenp;
-       struct inode *inode =  de->d_inode;
        u32 ipos_h, ipos_m, ipos_l;
 
        if (len < 5) {
@@ -771,9 +753,9 @@ fat_encode_fh(struct dentry *de, __u32 *fh, int *lenp, int connectable)
        fh[1] = inode->i_generation;
        fh[2] = ipos_h;
        fh[3] = ipos_m | MSDOS_I(inode)->i_logstart;
-       spin_lock(&de->d_lock);
-       fh[4] = ipos_l | MSDOS_I(de->d_parent->d_inode)->i_logstart;
-       spin_unlock(&de->d_lock);
+       fh[4] = ipos_l;
+       if (parent)
+               fh[4] |= MSDOS_I(parent)->i_logstart;
        return 3;
 }
 
@@ -1244,6 +1226,7 @@ int fat_fill_super(struct super_block *sb, void *data, int silent, int isvfat,
                   void (*setup)(struct super_block *))
 {
        struct inode *root_inode = NULL, *fat_inode = NULL;
+       struct inode *fsinfo_inode = NULL;
        struct buffer_head *bh;
        struct fat_boot_sector *b;
        struct msdos_sb_info *sbi;
@@ -1490,6 +1473,14 @@ int fat_fill_super(struct super_block *sb, void *data, int silent, int isvfat,
                goto out_fail;
        MSDOS_I(fat_inode)->i_pos = 0;
        sbi->fat_inode = fat_inode;
+
+       fsinfo_inode = new_inode(sb);
+       if (!fsinfo_inode)
+               goto out_fail;
+       fsinfo_inode->i_ino = MSDOS_FSINFO_INO;
+       sbi->fsinfo_inode = fsinfo_inode;
+       insert_inode_hash(fsinfo_inode);
+
        root_inode = new_inode(sb);
        if (!root_inode)
                goto out_fail;
@@ -1516,6 +1507,8 @@ out_invalid:
                fat_msg(sb, KERN_INFO, "Can't find a valid FAT filesystem");
 
 out_fail:
+       if (fsinfo_inode)
+               iput(fsinfo_inode);
        if (fat_inode)
                iput(fat_inode);
        unload_nls(sbi->nls_io);
index d078b75572a75eb9117092ee5bb752c84e1b38b8..81b70e665bf000412f73aa300890a53823db36f0 100644 (file)
@@ -442,28 +442,24 @@ static int check_fcntl_cmd(unsigned cmd)
 SYSCALL_DEFINE3(fcntl, unsigned int, fd, unsigned int, cmd, unsigned long, arg)
 {      
        struct file *filp;
+       int fput_needed;
        long err = -EBADF;
 
-       filp = fget_raw(fd);
+       filp = fget_raw_light(fd, &fput_needed);
        if (!filp)
                goto out;
 
        if (unlikely(filp->f_mode & FMODE_PATH)) {
-               if (!check_fcntl_cmd(cmd)) {
-                       fput(filp);
-                       goto out;
-               }
+               if (!check_fcntl_cmd(cmd))
+                       goto out1;
        }
 
        err = security_file_fcntl(filp, cmd, arg);
-       if (err) {
-               fput(filp);
-               return err;
-       }
+       if (!err)
+               err = do_fcntl(fd, cmd, arg, filp);
 
-       err = do_fcntl(fd, cmd, arg, filp);
-
-       fput(filp);
+out1:
+       fput_light(filp, fput_needed);
 out:
        return err;
 }
@@ -473,26 +469,21 @@ SYSCALL_DEFINE3(fcntl64, unsigned int, fd, unsigned int, cmd,
                unsigned long, arg)
 {      
        struct file * filp;
-       long err;
+       long err = -EBADF;
+       int fput_needed;
 
-       err = -EBADF;
-       filp = fget_raw(fd);
+       filp = fget_raw_light(fd, &fput_needed);
        if (!filp)
                goto out;
 
        if (unlikely(filp->f_mode & FMODE_PATH)) {
-               if (!check_fcntl_cmd(cmd)) {
-                       fput(filp);
-                       goto out;
-               }
+               if (!check_fcntl_cmd(cmd))
+                       goto out1;
        }
 
        err = security_file_fcntl(filp, cmd, arg);
-       if (err) {
-               fput(filp);
-               return err;
-       }
-       err = -EBADF;
+       if (err)
+               goto out1;
        
        switch (cmd) {
                case F_GETLK64:
@@ -507,7 +498,8 @@ SYSCALL_DEFINE3(fcntl64, unsigned int, fd, unsigned int, cmd,
                        err = do_fcntl(fd, cmd, arg, filp);
                        break;
        }
-       fput(filp);
+out1:
+       fput_light(filp, fput_needed);
 out:
        return err;
 }
index 70f2a0fd6aec62b28724d46e356dc0ff871f88b8..a305d9e2d1b2aac05dcd456bdd23885652272439 100644 (file)
@@ -34,7 +34,6 @@ struct files_stat_struct files_stat = {
        .max_files = NR_FILE
 };
 
-DECLARE_LGLOCK(files_lglock);
 DEFINE_LGLOCK(files_lglock);
 
 /* SLAB cache for file structures */
@@ -421,9 +420,9 @@ static inline void __file_sb_list_add(struct file *file, struct super_block *sb)
  */
 void file_sb_list_add(struct file *file, struct super_block *sb)
 {
-       lg_local_lock(files_lglock);
+       lg_local_lock(&files_lglock);
        __file_sb_list_add(file, sb);
-       lg_local_unlock(files_lglock);
+       lg_local_unlock(&files_lglock);
 }
 
 /**
@@ -436,9 +435,9 @@ void file_sb_list_add(struct file *file, struct super_block *sb)
 void file_sb_list_del(struct file *file)
 {
        if (!list_empty(&file->f_u.fu_list)) {
-               lg_local_lock_cpu(files_lglock, file_list_cpu(file));
+               lg_local_lock_cpu(&files_lglock, file_list_cpu(file));
                list_del_init(&file->f_u.fu_list);
-               lg_local_unlock_cpu(files_lglock, file_list_cpu(file));
+               lg_local_unlock_cpu(&files_lglock, file_list_cpu(file));
        }
 }
 
@@ -485,7 +484,7 @@ void mark_files_ro(struct super_block *sb)
        struct file *f;
 
 retry:
-       lg_global_lock(files_lglock);
+       lg_global_lock(&files_lglock);
        do_file_list_for_each_entry(sb, f) {
                struct vfsmount *mnt;
                if (!S_ISREG(f->f_path.dentry->d_inode->i_mode))
@@ -502,12 +501,12 @@ retry:
                file_release_write(f);
                mnt = mntget(f->f_path.mnt);
                /* This can sleep, so we can't hold the spinlock. */
-               lg_global_unlock(files_lglock);
+               lg_global_unlock(&files_lglock);
                mnt_drop_write(mnt);
                mntput(mnt);
                goto retry;
        } while_file_list_for_each_entry;
-       lg_global_unlock(files_lglock);
+       lg_global_unlock(&files_lglock);
 }
 
 void __init files_init(unsigned long mempages)
@@ -525,6 +524,6 @@ void __init files_init(unsigned long mempages)
        n = (mempages * (PAGE_SIZE / 1024)) / 10;
        files_stat.max_files = max_t(unsigned long, n, NR_FILE);
        files_defer_init();
-       lg_lock_init(files_lglock);
+       lg_lock_init(&files_lglock, "files_lglock");
        percpu_counter_init(&nr_files, 0);
 } 
index 504e61b7fd7515f8aafe7e3b9edd2c9fa42fd91d..9562109d3a879b3dab50ee27d989f3ae89c8b833 100644 (file)
@@ -962,7 +962,9 @@ static ssize_t fuse_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
        if (err)
                goto out;
 
-       file_update_time(file);
+       err = file_update_time(file);
+       if (err)
+               goto out;
 
        if (file->f_flags & O_DIRECT) {
                written = generic_file_direct_write(iocb, iov, &nr_segs,
index 56f6dcf307684287bad491b8711fa3a4ef4f0633..42678a33b7bb6297ced300f7fbb61696d37628c9 100644 (file)
@@ -627,12 +627,10 @@ static struct dentry *fuse_get_dentry(struct super_block *sb,
        return ERR_PTR(err);
 }
 
-static int fuse_encode_fh(struct dentry *dentry, u32 *fh, int *max_len,
-                          int connectable)
+static int fuse_encode_fh(struct inode *inode, u32 *fh, int *max_len,
+                          struct inode *parent)
 {
-       struct inode *inode = dentry->d_inode;
-       bool encode_parent = connectable && !S_ISDIR(inode->i_mode);
-       int len = encode_parent ? 6 : 3;
+       int len = parent ? 6 : 3;
        u64 nodeid;
        u32 generation;
 
@@ -648,14 +646,9 @@ static int fuse_encode_fh(struct dentry *dentry, u32 *fh, int *max_len,
        fh[1] = (u32)(nodeid & 0xffffffff);
        fh[2] = generation;
 
-       if (encode_parent) {
-               struct inode *parent;
-
-               spin_lock(&dentry->d_lock);
-               parent = dentry->d_parent->d_inode;
+       if (parent) {
                nodeid = get_fuse_inode(parent)->nodeid;
                generation = parent->i_generation;
-               spin_unlock(&dentry->d_lock);
 
                fh[3] = (u32)(nodeid >> 32);
                fh[4] = (u32)(nodeid & 0xffffffff);
@@ -663,7 +656,7 @@ static int fuse_encode_fh(struct dentry *dentry, u32 *fh, int *max_len,
        }
 
        *max_len = len;
-       return encode_parent ? 0x82 : 0x81;
+       return parent ? 0x82 : 0x81;
 }
 
 static struct dentry *fuse_fh_to_dentry(struct super_block *sb,
index 70ba891654f8ce3582c456e208feda6d56e90a1a..e8ed6d4a6181132ff47960dc118cd6fb60c1b81c 100644 (file)
 #define GFS2_LARGE_FH_SIZE 8
 #define GFS2_OLD_FH_SIZE 10
 
-static int gfs2_encode_fh(struct dentry *dentry, __u32 *p, int *len,
-                         int connectable)
+static int gfs2_encode_fh(struct inode *inode, __u32 *p, int *len,
+                         struct inode *parent)
 {
        __be32 *fh = (__force __be32 *)p;
-       struct inode *inode = dentry->d_inode;
        struct super_block *sb = inode->i_sb;
        struct gfs2_inode *ip = GFS2_I(inode);
 
-       if (connectable && (*len < GFS2_LARGE_FH_SIZE)) {
+       if (parent && (*len < GFS2_LARGE_FH_SIZE)) {
                *len = GFS2_LARGE_FH_SIZE;
                return 255;
        } else if (*len < GFS2_SMALL_FH_SIZE) {
@@ -50,14 +49,10 @@ static int gfs2_encode_fh(struct dentry *dentry, __u32 *p, int *len,
        fh[3] = cpu_to_be32(ip->i_no_addr & 0xFFFFFFFF);
        *len = GFS2_SMALL_FH_SIZE;
 
-       if (!connectable || inode == sb->s_root->d_inode)
+       if (!parent || inode == sb->s_root->d_inode)
                return *len;
 
-       spin_lock(&dentry->d_lock);
-       inode = dentry->d_parent->d_inode;
-       ip = GFS2_I(inode);
-       igrab(inode);
-       spin_unlock(&dentry->d_lock);
+       ip = GFS2_I(parent);
 
        fh[4] = cpu_to_be32(ip->i_no_formal_ino >> 32);
        fh[5] = cpu_to_be32(ip->i_no_formal_ino & 0xFFFFFFFF);
@@ -65,8 +60,6 @@ static int gfs2_encode_fh(struct dentry *dentry, __u32 *p, int *len,
        fh[7] = cpu_to_be32(ip->i_no_addr & 0xFFFFFFFF);
        *len = GFS2_LARGE_FH_SIZE;
 
-       iput(inode);
-
        return *len;
 }
 
index 7a5eb2c718c854206d6db419abe4ce7bc61d12c7..cdb84a8380682b5f341138cb6f75e2754434e073 100644 (file)
@@ -16,9 +16,9 @@
 static int chk_if_allocated(struct super_block *s, secno sec, char *msg)
 {
        struct quad_buffer_head qbh;
-       u32 *bmp;
+       __le32 *bmp;
        if (!(bmp = hpfs_map_bitmap(s, sec >> 14, &qbh, "chk"))) goto fail;
-       if ((cpu_to_le32(bmp[(sec & 0x3fff) >> 5]) >> (sec & 0x1f)) & 1) {
+       if ((le32_to_cpu(bmp[(sec & 0x3fff) >> 5]) >> (sec & 0x1f)) & 1) {
                hpfs_error(s, "sector '%s' - %08x not allocated in bitmap", msg, sec);
                goto fail1;
        }
@@ -62,7 +62,7 @@ int hpfs_chk_sectors(struct super_block *s, secno start, int len, char *msg)
 static secno alloc_in_bmp(struct super_block *s, secno near, unsigned n, unsigned forward)
 {
        struct quad_buffer_head qbh;
-       unsigned *bmp;
+       __le32 *bmp;
        unsigned bs = near & ~0x3fff;
        unsigned nr = (near & 0x3fff) & ~(n - 1);
        /*unsigned mnr;*/
@@ -236,7 +236,7 @@ static secno alloc_in_dirband(struct super_block *s, secno near)
 int hpfs_alloc_if_possible(struct super_block *s, secno sec)
 {
        struct quad_buffer_head qbh;
-       u32 *bmp;
+       __le32 *bmp;
        if (!(bmp = hpfs_map_bitmap(s, sec >> 14, &qbh, "aip"))) goto end;
        if (le32_to_cpu(bmp[(sec & 0x3fff) >> 5]) & (1 << (sec & 0x1f))) {
                bmp[(sec & 0x3fff) >> 5] &= cpu_to_le32(~(1 << (sec & 0x1f)));
@@ -254,7 +254,7 @@ int hpfs_alloc_if_possible(struct super_block *s, secno sec)
 void hpfs_free_sectors(struct super_block *s, secno sec, unsigned n)
 {
        struct quad_buffer_head qbh;
-       u32 *bmp;
+       __le32 *bmp;
        struct hpfs_sb_info *sbi = hpfs_sb(s);
        /*printk("2 - ");*/
        if (!n) return;
@@ -299,7 +299,7 @@ int hpfs_check_free_dnodes(struct super_block *s, int n)
        int n_bmps = (hpfs_sb(s)->sb_fs_size + 0x4000 - 1) >> 14;
        int b = hpfs_sb(s)->sb_c_bitmap & 0x0fffffff;
        int i, j;
-       u32 *bmp;
+       __le32 *bmp;
        struct quad_buffer_head qbh;
        if ((bmp = hpfs_map_dnode_bitmap(s, &qbh))) {
                for (j = 0; j < 512; j++) {
@@ -351,7 +351,7 @@ void hpfs_free_dnode(struct super_block *s, dnode_secno dno)
                hpfs_free_sectors(s, dno, 4);
        } else {
                struct quad_buffer_head qbh;
-               u32 *bmp;
+               __le32 *bmp;
                unsigned ssec = (dno - hpfs_sb(s)->sb_dirband_start) / 4;
                if (!(bmp = hpfs_map_dnode_bitmap(s, &qbh))) {
                        return;
index 08b503e8ed29ec610a098cb9658e1a2ecaa1779c..4bae4a4a60b1936eba70d17d18e7d4a016ed54b9 100644 (file)
@@ -20,7 +20,7 @@ secno hpfs_bplus_lookup(struct super_block *s, struct inode *inode,
        int c1, c2 = 0;
        go_down:
        if (hpfs_sb(s)->sb_chk) if (hpfs_stop_cycles(s, a, &c1, &c2, "hpfs_bplus_lookup")) return -1;
-       if (btree->internal) {
+       if (bp_internal(btree)) {
                for (i = 0; i < btree->n_used_nodes; i++)
                        if (le32_to_cpu(btree->u.internal[i].file_secno) > sec) {
                                a = le32_to_cpu(btree->u.internal[i].down);
@@ -82,7 +82,7 @@ secno hpfs_add_sector_to_btree(struct super_block *s, secno node, int fnod, unsi
                brelse(bh);
                return -1;
        }
-       if (btree->internal) {
+       if (bp_internal(btree)) {
                a = le32_to_cpu(btree->u.internal[n].down);
                btree->u.internal[n].file_secno = cpu_to_le32(-1);
                mark_buffer_dirty(bh);
@@ -129,12 +129,12 @@ secno hpfs_add_sector_to_btree(struct super_block *s, secno node, int fnod, unsi
                }
                if (a == node && fnod) {
                        anode->up = cpu_to_le32(node);
-                       anode->btree.fnode_parent = 1;
+                       anode->btree.flags |= BP_fnode_parent;
                        anode->btree.n_used_nodes = btree->n_used_nodes;
                        anode->btree.first_free = btree->first_free;
                        anode->btree.n_free_nodes = 40 - anode->btree.n_used_nodes;
                        memcpy(&anode->u, &btree->u, btree->n_used_nodes * 12);
-                       btree->internal = 1;
+                       btree->flags |= BP_internal;
                        btree->n_free_nodes = 11;
                        btree->n_used_nodes = 1;
                        btree->first_free = cpu_to_le16((char *)&(btree->u.internal[1]) - (char *)btree);
@@ -184,7 +184,10 @@ secno hpfs_add_sector_to_btree(struct super_block *s, secno node, int fnod, unsi
                        hpfs_free_sectors(s, ra, 1);
                        if ((anode = hpfs_map_anode(s, na, &bh))) {
                                anode->up = cpu_to_le32(up);
-                               anode->btree.fnode_parent = up == node && fnod;
+                               if (up == node && fnod)
+                                       anode->btree.flags |= BP_fnode_parent;
+                               else
+                                       anode->btree.flags &= ~BP_fnode_parent;
                                mark_buffer_dirty(bh);
                                brelse(bh);
                        }
@@ -198,7 +201,7 @@ secno hpfs_add_sector_to_btree(struct super_block *s, secno node, int fnod, unsi
                if ((new_anode = hpfs_alloc_anode(s, a, &na, &bh))) {
                        anode = new_anode;
                        /*anode->up = cpu_to_le32(up != -1 ? up : ra);*/
-                       anode->btree.internal = 1;
+                       anode->btree.flags |= BP_internal;
                        anode->btree.n_used_nodes = 1;
                        anode->btree.n_free_nodes = 59;
                        anode->btree.first_free = cpu_to_le16(16);
@@ -215,7 +218,8 @@ secno hpfs_add_sector_to_btree(struct super_block *s, secno node, int fnod, unsi
        }
        if ((anode = hpfs_map_anode(s, na, &bh))) {
                anode->up = cpu_to_le32(node);
-               if (fnod) anode->btree.fnode_parent = 1;
+               if (fnod)
+                       anode->btree.flags |= BP_fnode_parent;
                mark_buffer_dirty(bh);
                brelse(bh);
        }
@@ -234,18 +238,19 @@ secno hpfs_add_sector_to_btree(struct super_block *s, secno node, int fnod, unsi
        }
        ranode->up = cpu_to_le32(node);
        memcpy(&ranode->btree, btree, le16_to_cpu(btree->first_free));
-       if (fnod) ranode->btree.fnode_parent = 1;
-       ranode->btree.n_free_nodes = (ranode->btree.internal ? 60 : 40) - ranode->btree.n_used_nodes;
-       if (ranode->btree.internal) for (n = 0; n < ranode->btree.n_used_nodes; n++) {
+       if (fnod)
+               ranode->btree.flags |= BP_fnode_parent;
+       ranode->btree.n_free_nodes = (bp_internal(&ranode->btree) ? 60 : 40) - ranode->btree.n_used_nodes;
+       if (bp_internal(&ranode->btree)) for (n = 0; n < ranode->btree.n_used_nodes; n++) {
                struct anode *unode;
                if ((unode = hpfs_map_anode(s, le32_to_cpu(ranode->u.internal[n].down), &bh1))) {
                        unode->up = cpu_to_le32(ra);
-                       unode->btree.fnode_parent = 0;
+                       unode->btree.flags &= ~BP_fnode_parent;
                        mark_buffer_dirty(bh1);
                        brelse(bh1);
                }
        }
-       btree->internal = 1;
+       btree->flags |= BP_internal;
        btree->n_free_nodes = fnod ? 10 : 58;
        btree->n_used_nodes = 2;
        btree->first_free = cpu_to_le16((char *)&btree->u.internal[2] - (char *)btree);
@@ -278,7 +283,7 @@ void hpfs_remove_btree(struct super_block *s, struct bplus_header *btree)
        int d1, d2;
        go_down:
        d2 = 0;
-       while (btree1->internal) {
+       while (bp_internal(btree1)) {
                ano = le32_to_cpu(btree1->u.internal[pos].down);
                if (level) brelse(bh);
                if (hpfs_sb(s)->sb_chk)
@@ -412,13 +417,13 @@ void hpfs_truncate_btree(struct super_block *s, secno f, int fno, unsigned secs)
                        btree->n_free_nodes = 8;
                        btree->n_used_nodes = 0;
                        btree->first_free = cpu_to_le16(8);
-                       btree->internal = 0;
+                       btree->flags &= ~BP_internal;
                        mark_buffer_dirty(bh);
                } else hpfs_free_sectors(s, f, 1);
                brelse(bh);
                return;
        }
-       while (btree->internal) {
+       while (bp_internal(btree)) {
                nodes = btree->n_used_nodes + btree->n_free_nodes;
                for (i = 0; i < btree->n_used_nodes; i++)
                        if (le32_to_cpu(btree->u.internal[i].file_secno) >= secs) goto f;
@@ -479,13 +484,13 @@ void hpfs_remove_fnode(struct super_block *s, fnode_secno fno)
        struct extended_attribute *ea;
        struct extended_attribute *ea_end;
        if (!(fnode = hpfs_map_fnode(s, fno, &bh))) return;
-       if (!fnode->dirflag) hpfs_remove_btree(s, &fnode->btree);
+       if (!fnode_is_dir(fnode)) hpfs_remove_btree(s, &fnode->btree);
        else hpfs_remove_dtree(s, le32_to_cpu(fnode->u.external[0].disk_secno));
        ea_end = fnode_end_ea(fnode);
        for (ea = fnode_ea(fnode); ea < ea_end; ea = next_ea(ea))
-               if (ea->indirect)
-                       hpfs_ea_remove(s, ea_sec(ea), ea->anode, ea_len(ea));
-       hpfs_ea_ext_remove(s, le32_to_cpu(fnode->ea_secno), fnode->ea_anode, le32_to_cpu(fnode->ea_size_l));
+               if (ea_indirect(ea))
+                       hpfs_ea_remove(s, ea_sec(ea), ea_in_anode(ea), ea_len(ea));
+       hpfs_ea_ext_remove(s, le32_to_cpu(fnode->ea_secno), fnode_in_anode(fnode), le32_to_cpu(fnode->ea_size_l));
        brelse(bh);
        hpfs_free_sectors(s, fno, 1);
 }
index 9ecde27d1e297ed3d42a742352f469f1bf2dfb9a..f49d1498aa2e98d3a1bc46fff59d67ca45f04a4e 100644 (file)
@@ -156,7 +156,6 @@ void hpfs_brelse4(struct quad_buffer_head *qbh)
 
 void hpfs_mark_4buffers_dirty(struct quad_buffer_head *qbh)
 {
-       PRINTK(("hpfs_mark_4buffers_dirty\n"));
        memcpy(qbh->bh[0]->b_data, qbh->data, 512);
        memcpy(qbh->bh[1]->b_data, qbh->data + 512, 512);
        memcpy(qbh->bh[2]->b_data, qbh->data + 2 * 512, 512);
index 2fa0089a02a8ec2934cda55cbbae18e50c34a4ea..b8472f803f4e54ea5039b85ac36cfdf33a48925b 100644 (file)
@@ -87,7 +87,7 @@ static int hpfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
                        ret = -EIOERROR;
                        goto out;
                }
-               if (!fno->dirflag) {
+               if (!fnode_is_dir(fno)) {
                        e = 1;
                        hpfs_error(inode->i_sb, "not a directory, fnode %08lx",
                                        (unsigned long)inode->i_ino);
index 1e0e2ac30fd3be93f8e5b7a97618f19a52220ec4..3228c524ebe56f948d8896cec23ca6b1284f6303 100644 (file)
@@ -153,7 +153,7 @@ static void set_last_pointer(struct super_block *s, struct dnode *d, dnode_secno
                }
                de->length = cpu_to_le16(36);
                de->down = 1;
-               *(dnode_secno *)((char *)de + 32) = cpu_to_le32(ptr);
+               *(__le32 *)((char *)de + 32) = cpu_to_le32(ptr);
        }
 }
 
@@ -177,7 +177,7 @@ struct hpfs_dirent *hpfs_add_de(struct super_block *s, struct dnode *d,
        memmove((char *)de + d_size, de, (char *)de_end - (char *)de);
        memset(de, 0, d_size);
        if (down_ptr) {
-               *(dnode_secno *)((char *)de + d_size - 4) = cpu_to_le32(down_ptr);
+               *(__le32 *)((char *)de + d_size - 4) = cpu_to_le32(down_ptr);
                de->down = 1;
        }
        de->length = cpu_to_le16(d_size);
@@ -656,7 +656,7 @@ static void delete_empty_dnode(struct inode *i, dnode_secno dno)
                                del->down = 0;
                                d1->first_free = cpu_to_le32(le32_to_cpu(d1->first_free) - 4);
                        } else if (down)
-                               *(dnode_secno *) ((void *) del + le16_to_cpu(del->length) - 4) = cpu_to_le32(down);
+                               *(__le32 *) ((void *) del + le16_to_cpu(del->length) - 4) = cpu_to_le32(down);
                } else goto endm;
                if (!(de_cp = kmalloc(le16_to_cpu(de_prev->length), GFP_NOFS))) {
                        printk("HPFS: out of memory for dtree balancing\n");
@@ -672,7 +672,7 @@ static void delete_empty_dnode(struct inode *i, dnode_secno dno)
                        de_prev->down = 1;
                        dnode->first_free = cpu_to_le32(le32_to_cpu(dnode->first_free) + 4);
                }
-               *(dnode_secno *) ((void *) de_prev + le16_to_cpu(de_prev->length) - 4) = cpu_to_le32(ndown);
+               *(__le32 *) ((void *) de_prev + le16_to_cpu(de_prev->length) - 4) = cpu_to_le32(ndown);
                hpfs_mark_4buffers_dirty(&qbh);
                hpfs_brelse4(&qbh);
                for_all_poss(i, hpfs_pos_subst, ((loff_t)up << 4) | (p - 1), 4);
@@ -1015,7 +1015,7 @@ struct hpfs_dirent *map_fnode_dirent(struct super_block *s, fnode_secno fno,
                kfree(name2);
                return NULL;
        }       
-       if (!upf->dirflag) {
+       if (!fnode_is_dir(upf)) {
                brelse(bh);
                hpfs_error(s, "fnode %08x has non-directory parent %08x", fno, le32_to_cpu(f->up));
                kfree(name2);
index d8b84d113c891bbcfd8416d3f35153983b0549a7..bcaafcd2666ac275d02c2f054023cc537ebd7644 100644 (file)
@@ -23,15 +23,15 @@ void hpfs_ea_ext_remove(struct super_block *s, secno a, int ano, unsigned len)
                        return;
                }
                if (hpfs_ea_read(s, a, ano, pos, 4, ex)) return;
-               if (ea->indirect) {
+               if (ea_indirect(ea)) {
                        if (ea_valuelen(ea) != 8) {
-                               hpfs_error(s, "ea->indirect set while ea->valuelen!=8, %s %08x, pos %08x",
+                               hpfs_error(s, "ea_indirect(ea) set while ea->valuelen!=8, %s %08x, pos %08x",
                                        ano ? "anode" : "sectors", a, pos);
                                return;
                        }
                        if (hpfs_ea_read(s, a, ano, pos + 4, ea->namelen + 9, ex+4))
                                return;
-                       hpfs_ea_remove(s, ea_sec(ea), ea->anode, ea_len(ea));
+                       hpfs_ea_remove(s, ea_sec(ea), ea_in_anode(ea), ea_len(ea));
                }
                pos += ea->namelen + ea_valuelen(ea) + 5;
        }
@@ -81,7 +81,7 @@ int hpfs_read_ea(struct super_block *s, struct fnode *fnode, char *key,
        struct extended_attribute *ea_end = fnode_end_ea(fnode);
        for (ea = fnode_ea(fnode); ea < ea_end; ea = next_ea(ea))
                if (!strcmp(ea->name, key)) {
-                       if (ea->indirect)
+                       if (ea_indirect(ea))
                                goto indirect;
                        if (ea_valuelen(ea) >= size)
                                return -EINVAL;
@@ -91,7 +91,7 @@ int hpfs_read_ea(struct super_block *s, struct fnode *fnode, char *key,
                }
        a = le32_to_cpu(fnode->ea_secno);
        len = le32_to_cpu(fnode->ea_size_l);
-       ano = fnode->ea_anode;
+       ano = fnode_in_anode(fnode);
        pos = 0;
        while (pos < len) {
                ea = (struct extended_attribute *)ex;
@@ -101,10 +101,10 @@ int hpfs_read_ea(struct super_block *s, struct fnode *fnode, char *key,
                        return -EIO;
                }
                if (hpfs_ea_read(s, a, ano, pos, 4, ex)) return -EIO;
-               if (hpfs_ea_read(s, a, ano, pos + 4, ea->namelen + 1 + (ea->indirect ? 8 : 0), ex + 4))
+               if (hpfs_ea_read(s, a, ano, pos + 4, ea->namelen + 1 + (ea_indirect(ea) ? 8 : 0), ex + 4))
                        return -EIO;
                if (!strcmp(ea->name, key)) {
-                       if (ea->indirect)
+                       if (ea_indirect(ea))
                                goto indirect;
                        if (ea_valuelen(ea) >= size)
                                return -EINVAL;
@@ -119,7 +119,7 @@ int hpfs_read_ea(struct super_block *s, struct fnode *fnode, char *key,
 indirect:
        if (ea_len(ea) >= size)
                return -EINVAL;
-       if (hpfs_ea_read(s, ea_sec(ea), ea->anode, 0, ea_len(ea), buf))
+       if (hpfs_ea_read(s, ea_sec(ea), ea_in_anode(ea), 0, ea_len(ea), buf))
                return -EIO;
        buf[ea_len(ea)] = 0;
        return 0;
@@ -136,8 +136,8 @@ char *hpfs_get_ea(struct super_block *s, struct fnode *fnode, char *key, int *si
        struct extended_attribute *ea_end = fnode_end_ea(fnode);
        for (ea = fnode_ea(fnode); ea < ea_end; ea = next_ea(ea))
                if (!strcmp(ea->name, key)) {
-                       if (ea->indirect)
-                               return get_indirect_ea(s, ea->anode, ea_sec(ea), *size = ea_len(ea));
+                       if (ea_indirect(ea))
+                               return get_indirect_ea(s, ea_in_anode(ea), ea_sec(ea), *size = ea_len(ea));
                        if (!(ret = kmalloc((*size = ea_valuelen(ea)) + 1, GFP_NOFS))) {
                                printk("HPFS: out of memory for EA\n");
                                return NULL;
@@ -148,7 +148,7 @@ char *hpfs_get_ea(struct super_block *s, struct fnode *fnode, char *key, int *si
                }
        a = le32_to_cpu(fnode->ea_secno);
        len = le32_to_cpu(fnode->ea_size_l);
-       ano = fnode->ea_anode;
+       ano = fnode_in_anode(fnode);
        pos = 0;
        while (pos < len) {
                char ex[4 + 255 + 1 + 8];
@@ -159,11 +159,11 @@ char *hpfs_get_ea(struct super_block *s, struct fnode *fnode, char *key, int *si
                        return NULL;
                }
                if (hpfs_ea_read(s, a, ano, pos, 4, ex)) return NULL;
-               if (hpfs_ea_read(s, a, ano, pos + 4, ea->namelen + 1 + (ea->indirect ? 8 : 0), ex + 4))
+               if (hpfs_ea_read(s, a, ano, pos + 4, ea->namelen + 1 + (ea_indirect(ea) ? 8 : 0), ex + 4))
                        return NULL;
                if (!strcmp(ea->name, key)) {
-                       if (ea->indirect)
-                               return get_indirect_ea(s, ea->anode, ea_sec(ea), *size = ea_len(ea));
+                       if (ea_indirect(ea))
+                               return get_indirect_ea(s, ea_in_anode(ea), ea_sec(ea), *size = ea_len(ea));
                        if (!(ret = kmalloc((*size = ea_valuelen(ea)) + 1, GFP_NOFS))) {
                                printk("HPFS: out of memory for EA\n");
                                return NULL;
@@ -199,9 +199,9 @@ void hpfs_set_ea(struct inode *inode, struct fnode *fnode, const char *key,
        struct extended_attribute *ea_end = fnode_end_ea(fnode);
        for (ea = fnode_ea(fnode); ea < ea_end; ea = next_ea(ea))
                if (!strcmp(ea->name, key)) {
-                       if (ea->indirect) {
+                       if (ea_indirect(ea)) {
                                if (ea_len(ea) == size)
-                                       set_indirect_ea(s, ea->anode, ea_sec(ea), data, size);
+                                       set_indirect_ea(s, ea_in_anode(ea), ea_sec(ea), data, size);
                        } else if (ea_valuelen(ea) == size) {
                                memcpy(ea_data(ea), data, size);
                        }
@@ -209,7 +209,7 @@ void hpfs_set_ea(struct inode *inode, struct fnode *fnode, const char *key,
                }
        a = le32_to_cpu(fnode->ea_secno);
        len = le32_to_cpu(fnode->ea_size_l);
-       ano = fnode->ea_anode;
+       ano = fnode_in_anode(fnode);
        pos = 0;
        while (pos < len) {
                char ex[4 + 255 + 1 + 8];
@@ -220,12 +220,12 @@ void hpfs_set_ea(struct inode *inode, struct fnode *fnode, const char *key,
                        return;
                }
                if (hpfs_ea_read(s, a, ano, pos, 4, ex)) return;
-               if (hpfs_ea_read(s, a, ano, pos + 4, ea->namelen + 1 + (ea->indirect ? 8 : 0), ex + 4))
+               if (hpfs_ea_read(s, a, ano, pos + 4, ea->namelen + 1 + (ea_indirect(ea) ? 8 : 0), ex + 4))
                        return;
                if (!strcmp(ea->name, key)) {
-                       if (ea->indirect) {
+                       if (ea_indirect(ea)) {
                                if (ea_len(ea) == size)
-                                       set_indirect_ea(s, ea->anode, ea_sec(ea), data, size);
+                                       set_indirect_ea(s, ea_in_anode(ea), ea_sec(ea), data, size);
                        }
                        else {
                                if (ea_valuelen(ea) == size)
@@ -246,7 +246,7 @@ void hpfs_set_ea(struct inode *inode, struct fnode *fnode, const char *key,
        if (le16_to_cpu(fnode->ea_offs) < 0xc4 || le16_to_cpu(fnode->ea_offs) + le16_to_cpu(fnode->acl_size_s) + le16_to_cpu(fnode->ea_size_s) > 0x200) {
                hpfs_error(s, "fnode %08lx: ea_offs == %03x, ea_size_s == %03x",
                        (unsigned long)inode->i_ino,
-                       le32_to_cpu(fnode->ea_offs), le16_to_cpu(fnode->ea_size_s));
+                       le16_to_cpu(fnode->ea_offs), le16_to_cpu(fnode->ea_size_s));
                return;
        }
        if ((le16_to_cpu(fnode->ea_size_s) || !le32_to_cpu(fnode->ea_size_l)) &&
@@ -276,7 +276,7 @@ void hpfs_set_ea(struct inode *inode, struct fnode *fnode, const char *key,
                fnode->ea_size_l = cpu_to_le32(le16_to_cpu(fnode->ea_size_s));
                fnode->ea_size_s = cpu_to_le16(0);
                fnode->ea_secno = cpu_to_le32(n);
-               fnode->ea_anode = cpu_to_le32(0);
+               fnode->flags &= ~FNODE_anode;
                mark_buffer_dirty(bh);
                brelse(bh);
        }
@@ -288,9 +288,9 @@ void hpfs_set_ea(struct inode *inode, struct fnode *fnode, const char *key,
                        secno q = hpfs_alloc_sector(s, fno, 1, 0);
                        if (!q) goto bail;
                        fnode->ea_secno = cpu_to_le32(q);
-                       fnode->ea_anode = 0;
+                       fnode->flags &= ~FNODE_anode;
                        len++;
-               } else if (!fnode->ea_anode) {
+               } else if (!fnode_in_anode(fnode)) {
                        if (hpfs_alloc_if_possible(s, le32_to_cpu(fnode->ea_secno) + len)) {
                                len++;
                        } else {
@@ -310,7 +310,7 @@ void hpfs_set_ea(struct inode *inode, struct fnode *fnode, const char *key,
                                anode->u.external[0].length = cpu_to_le32(len);
                                mark_buffer_dirty(bh);
                                brelse(bh);
-                               fnode->ea_anode = 1;
+                               fnode->flags |= FNODE_anode;
                                fnode->ea_secno = cpu_to_le32(a_s);*/
                                secno new_sec;
                                int i;
@@ -338,7 +338,7 @@ void hpfs_set_ea(struct inode *inode, struct fnode *fnode, const char *key,
                                len = (pos + 511) >> 9;
                        }
                }
-               if (fnode->ea_anode) {
+               if (fnode_in_anode(fnode)) {
                        if (hpfs_add_sector_to_btree(s, le32_to_cpu(fnode->ea_secno),
                                                     0, len) != -1) {
                                len++;
@@ -351,16 +351,16 @@ void hpfs_set_ea(struct inode *inode, struct fnode *fnode, const char *key,
        h[1] = strlen(key);
        h[2] = size & 0xff;
        h[3] = size >> 8;
-       if (hpfs_ea_write(s, le32_to_cpu(fnode->ea_secno), fnode->ea_anode, le32_to_cpu(fnode->ea_size_l), 4, h)) goto bail;
-       if (hpfs_ea_write(s, le32_to_cpu(fnode->ea_secno), fnode->ea_anode, le32_to_cpu(fnode->ea_size_l) + 4, h[1] + 1, key)) goto bail;
-       if (hpfs_ea_write(s, le32_to_cpu(fnode->ea_secno), fnode->ea_anode, le32_to_cpu(fnode->ea_size_l) + 5 + h[1], size, data)) goto bail;
+       if (hpfs_ea_write(s, le32_to_cpu(fnode->ea_secno), fnode_in_anode(fnode), le32_to_cpu(fnode->ea_size_l), 4, h)) goto bail;
+       if (hpfs_ea_write(s, le32_to_cpu(fnode->ea_secno), fnode_in_anode(fnode), le32_to_cpu(fnode->ea_size_l) + 4, h[1] + 1, key)) goto bail;
+       if (hpfs_ea_write(s, le32_to_cpu(fnode->ea_secno), fnode_in_anode(fnode), le32_to_cpu(fnode->ea_size_l) + 5 + h[1], size, data)) goto bail;
        fnode->ea_size_l = cpu_to_le32(pos);
        ret:
        hpfs_i(inode)->i_ea_size += 5 + strlen(key) + size;
        return;
        bail:
        if (le32_to_cpu(fnode->ea_secno))
-               if (fnode->ea_anode) hpfs_truncate_btree(s, le32_to_cpu(fnode->ea_secno), 1, (le32_to_cpu(fnode->ea_size_l) + 511) >> 9);
+               if (fnode_in_anode(fnode)) hpfs_truncate_btree(s, le32_to_cpu(fnode->ea_secno), 1, (le32_to_cpu(fnode->ea_size_l) + 511) >> 9);
                else hpfs_free_sectors(s, le32_to_cpu(fnode->ea_secno) + ((le32_to_cpu(fnode->ea_size_l) + 511) >> 9), len - ((le32_to_cpu(fnode->ea_size_l) + 511) >> 9));
        else fnode->ea_secno = fnode->ea_size_l = cpu_to_le32(0);
 }
index 8b0650aae32812bac9abbb581439b592d64949d2..cce025aff1b19b86f824cd47bfb6c9457d583068 100644 (file)
@@ -51,11 +51,11 @@ struct hpfs_boot_block
   u8 n_rootdir_entries[2];
   u8 n_sectors_s[2];
   u8 media_byte;
-  u16 sectors_per_fat;
-  u16 sectors_per_track;
-  u16 heads_per_cyl;
-  u32 n_hidden_sectors;
-  u32 n_sectors_l;             /* size of partition */
+  __le16 sectors_per_fat;
+  __le16 sectors_per_track;
+  __le16 heads_per_cyl;
+  __le32 n_hidden_sectors;
+  __le32 n_sectors_l;          /* size of partition */
   u8 drive_number;
   u8 mbz;
   u8 sig_28h;                  /* 28h */
@@ -63,7 +63,7 @@ struct hpfs_boot_block
   u8 vol_label[11];
   u8 sig_hpfs[8];              /* "HPFS    " */
   u8 pad[448];
-  u16 magic;                   /* aa55 */
+  __le16 magic;                        /* aa55 */
 };
 
 
@@ -75,28 +75,28 @@ struct hpfs_boot_block
 
 struct hpfs_super_block
 {
-  u32 magic;                           /* f995 e849 */
-  u32 magic1;                          /* fa53 e9c5, more magic? */
+  __le32 magic;                                /* f995 e849 */
+  __le32 magic1;                       /* fa53 e9c5, more magic? */
   u8 version;                          /* version of a filesystem  usually 2 */
   u8 funcversion;                      /* functional version - oldest version
                                           of filesystem that can understand
                                           this disk */
-  u16 zero;                            /* 0 */
-  fnode_secno root;                    /* fnode of root directory */
-  secno n_sectors;                     /* size of filesystem */
-  u32 n_badblocks;                     /* number of bad blocks */
-  secno bitmaps;                       /* pointers to free space bit maps */
-  u32 zero1;                           /* 0 */
-  secno badblocks;                     /* bad block list */
-  u32 zero3;                           /* 0 */
-  time32_t last_chkdsk;                        /* date last checked, 0 if never */
-  time32_t last_optimize;              /* date last optimized, 0 if never */
-  secno n_dir_band;                    /* number of sectors in dir band */
-  secno dir_band_start;                        /* first sector in dir band */
-  secno dir_band_end;                  /* last sector in dir band */
-  secno dir_band_bitmap;               /* free space map, 1 dnode per bit */
+  __le16 zero;                         /* 0 */
+  __le32 root;                         /* fnode of root directory */
+  __le32 n_sectors;                    /* size of filesystem */
+  __le32 n_badblocks;                  /* number of bad blocks */
+  __le32 bitmaps;                      /* pointers to free space bit maps */
+  __le32 zero1;                                /* 0 */
+  __le32 badblocks;                    /* bad block list */
+  __le32 zero3;                                /* 0 */
+  __le32 last_chkdsk;                  /* date last checked, 0 if never */
+  __le32 last_optimize;                        /* date last optimized, 0 if never */
+  __le32 n_dir_band;                   /* number of sectors in dir band */
+  __le32 dir_band_start;                       /* first sector in dir band */
+  __le32 dir_band_end;                 /* last sector in dir band */
+  __le32 dir_band_bitmap;              /* free space map, 1 dnode per bit */
   u8 volume_name[32];                  /* not used */
-  secno user_id_table;                 /* 8 preallocated sectors - user id */
+  __le32 user_id_table;                        /* 8 preallocated sectors - user id */
   u32 zero6[103];                      /* 0 */
 };
 
@@ -109,8 +109,8 @@ struct hpfs_super_block
 
 struct hpfs_spare_block
 {
-  u32 magic;                           /* f991 1849 */
-  u32 magic1;                          /* fa52 29c5, more magic? */
+  __le32 magic;                                /* f991 1849 */
+  __le32 magic1;                               /* fa52 29c5, more magic? */
 
 #ifdef __LITTLE_ENDIAN
   u8 dirty: 1;                         /* 0 clean, 1 "improperly stopped" */
@@ -153,21 +153,21 @@ struct hpfs_spare_block
   u8 mm_contlgulty;
   u8 unused;
 
-  secno hotfix_map;                    /* info about remapped bad sectors */
-  u32 n_spares_used;                   /* number of hotfixes */
-  u32 n_spares;                                /* number of spares in hotfix map */
-  u32 n_dnode_spares_free;             /* spare dnodes unused */
-  u32 n_dnode_spares;                  /* length of spare_dnodes[] list,
+  __le32 hotfix_map;                   /* info about remapped bad sectors */
+  __le32 n_spares_used;                        /* number of hotfixes */
+  __le32 n_spares;                     /* number of spares in hotfix map */
+  __le32 n_dnode_spares_free;          /* spare dnodes unused */
+  __le32 n_dnode_spares;               /* length of spare_dnodes[] list,
                                           follows in this block*/
-  secno code_page_dir;                 /* code page directory block */
-  u32 n_code_pages;                    /* number of code pages */
-  u32 super_crc;                       /* on HPFS386 and LAN Server this is
+  __le32 code_page_dir;                        /* code page directory block */
+  __le32 n_code_pages;                 /* number of code pages */
+  __le32 super_crc;                    /* on HPFS386 and LAN Server this is
                                           checksum of superblock, on normal
                                           OS/2 unused */
-  u32 spare_crc;                       /* on HPFS386 checksum of spareblock */
-  u32 zero1[15];                       /* unused */
-  dnode_secno spare_dnodes[100];       /* emergency free dnode list */
-  u32 zero2[1];                                /* room for more? */
+  __le32 spare_crc;                    /* on HPFS386 checksum of spareblock */
+  __le32 zero1[15];                    /* unused */
+  __le32 spare_dnodes[100];            /* emergency free dnode list */
+  __le32 zero2[1];                     /* room for more? */
 };
 
 /* The bad block list is 4 sectors long.  The first word must be zero,
@@ -202,18 +202,18 @@ struct hpfs_spare_block
 
 struct code_page_directory
 {
-  u32 magic;                           /* 4945 21f7 */
-  u32 n_code_pages;                    /* number of pointers following */
-  u32 zero1[2];
+  __le32 magic;                                /* 4945 21f7 */
+  __le32 n_code_pages;                 /* number of pointers following */
+  __le32 zero1[2];
   struct {
-    u16 ix;                            /* index */
-    u16 code_page_number;              /* code page number */
-    u32 bounds;                                /* matches corresponding word
+    __le16 ix;                         /* index */
+    __le16 code_page_number;           /* code page number */
+    __le32 bounds;                     /* matches corresponding word
                                           in data block */
-    secno code_page_data;              /* sector number of a code_page_data
+    __le32 code_page_data;             /* sector number of a code_page_data
                                           containing c.p. array */
-    u16 index;                         /* index in c.p. array in that sector*/
-    u16 unknown;                       /* some unknown value; usually 0;
+    __le16 index;                      /* index in c.p. array in that sector*/
+    __le16 unknown;                    /* some unknown value; usually 0;
                                           2 in Japanese version */
   } array[31];                         /* unknown length */
 };
@@ -224,19 +224,19 @@ struct code_page_directory
 
 struct code_page_data
 {
-  u32 magic;                           /* 8945 21f7 */
-  u32 n_used;                          /* # elements used in c_p_data[] */
-  u32 bounds[3];                       /* looks a bit like
+  __le32 magic;                                /* 8945 21f7 */
+  __le32 n_used;                       /* # elements used in c_p_data[] */
+  __le32 bounds[3];                    /* looks a bit like
                                             (beg1,end1), (beg2,end2)
                                           one byte each */
-  u16 offs[3];                         /* offsets from start of sector
+  __le16 offs[3];                      /* offsets from start of sector
                                           to start of c_p_data[ix] */
   struct {
-    u16 ix;                            /* index */
-    u16 code_page_number;              /* code page number */
-    u16 unknown;                       /* the same as in cp directory */
+    __le16 ix;                         /* index */
+    __le16 code_page_number;           /* code page number */
+    __le16 unknown;                    /* the same as in cp directory */
     u8 map[128];                       /* upcase table for chars 80..ff */
-    u16 zero2;
+    __le16 zero2;
   } code_page[3];
   u8 incognita[78];
 };
@@ -278,8 +278,8 @@ struct code_page_data
 #define DNODE_MAGIC   0x77e40aae
 
 struct dnode {
-  u32 magic;                           /* 77e4 0aae */
-  u32 first_free;                      /* offset from start of dnode to
+  __le32 magic;                                /* 77e4 0aae */
+  __le32 first_free;                   /* offset from start of dnode to
                                           first free dir entry */
 #ifdef __LITTLE_ENDIAN
   u8 root_dnode: 1;                    /* Is it root dnode? */
@@ -293,14 +293,14 @@ struct dnode {
   u8 root_dnode: 1;                    /* Is it root dnode? */
 #endif
   u8 increment_me2[3];
-  secno up;                            /* (root dnode) directory's fnode
+  __le32 up;                           /* (root dnode) directory's fnode
                                           (nonroot) parent dnode */
-  dnode_secno self;                    /* pointer to this dnode */
+  __le32 self;                 /* pointer to this dnode */
   u8 dirent[2028];                     /* one or more dirents */
 };
 
 struct hpfs_dirent {
-  u16 length;                          /* offset to next dirent */
+  __le16 length;                       /* offset to next dirent */
 
 #ifdef __LITTLE_ENDIAN
   u8 first: 1;                         /* set on phony ^A^A (".") entry */
@@ -346,12 +346,12 @@ struct hpfs_dirent {
   u8 read_only: 1;                     /* dos attrib */
 #endif
 
-  fnode_secno fnode;                   /* fnode giving allocation info */
-  time32_t write_date;                 /* mtime */
-  u32 file_size;                       /* file length, bytes */
-  time32_t read_date;                  /* atime */
-  time32_t creation_date;                      /* ctime */
-  u32 ea_size;                         /* total EA length, bytes */
+  __le32 fnode;                                /* fnode giving allocation info */
+  __le32 write_date;                   /* mtime */
+  __le32 file_size;                    /* file length, bytes */
+  __le32 read_date;                    /* atime */
+  __le32 creation_date;                        /* ctime */
+  __le32 ea_size;                      /* total EA length, bytes */
   u8 no_of_acls;                       /* number of ACL's (low 3 bits) */
   u8 ix;                               /* code page index (of filename), see
                                           struct code_page_data */
@@ -375,50 +375,36 @@ struct hpfs_dirent {
 
 struct bplus_leaf_node
 {
-  u32 file_secno;                      /* first file sector in extent */
-  u32 length;                          /* length, sectors */
-  secno disk_secno;                    /* first corresponding disk sector */
+  __le32 file_secno;                   /* first file sector in extent */
+  __le32 length;                       /* length, sectors */
+  __le32 disk_secno;                   /* first corresponding disk sector */
 };
 
 struct bplus_internal_node
 {
-  u32 file_secno;                      /* subtree maps sectors < this  */
-  anode_secno down;                    /* pointer to subtree */
+  __le32 file_secno;                   /* subtree maps sectors < this  */
+  __le32 down;                         /* pointer to subtree */
 };
 
+enum {
+       BP_hbff = 1,
+       BP_fnode_parent = 0x20,
+       BP_binary_search = 0x40,
+       BP_internal = 0x80
+};
 struct bplus_header
 {
-#ifdef __LITTLE_ENDIAN
-  u8 hbff: 1;                  /* high bit of first free entry offset */
-  u8 flag1234: 4;
-  u8 fnode_parent: 1;                  /* ? we're pointed to by an fnode,
-                                          the data btree or some ea or the
-                                          main ea bootage pointer ea_secno */
-                                       /* also can get set in fnodes, which
-                                          may be a chkdsk glitch or may mean
-                                          this bit is irrelevant in fnodes,
-                                          or this interpretation is all wet */
-  u8 binary_search: 1;                 /* suggest binary search (unused) */
-  u8 internal: 1;                      /* 1 -> (internal) tree of anodes
-                                          0 -> (leaf) list of extents */
-#else
-  u8 internal: 1;                      /* 1 -> (internal) tree of anodes
-                                          0 -> (leaf) list of extents */
-  u8 binary_search: 1;                 /* suggest binary search (unused) */
-  u8 fnode_parent: 1;                  /* ? we're pointed to by an fnode,
+  u8 flags;                            /* bit 0 - high bit of first free entry offset
+                                          bit 5 - we're pointed to by an fnode,
                                           the data btree or some ea or the
-                                          main ea bootage pointer ea_secno */
-                                       /* also can get set in fnodes, which
-                                          may be a chkdsk glitch or may mean
-                                          this bit is irrelevant in fnodes,
-                                          or this interpretation is all wet */
-  u8 flag1234: 4;
-  u8 hbff: 1;                  /* high bit of first free entry offset */
-#endif
+                                          main ea bootage pointer ea_secno
+                                          bit 6 - suggest binary search (unused)
+                                          bit 7 - 1 -> (internal) tree of anodes
+                                                  0 -> (leaf) list of extents */
   u8 fill[3];
   u8 n_free_nodes;                     /* free nodes in following array */
   u8 n_used_nodes;                     /* used nodes in following array */
-  u16 first_free;                      /* offset from start of header to
+  __le16 first_free;                   /* offset from start of header to
                                           first free node in array */
   union {
     struct bplus_internal_node internal[0]; /* (internal) 2-word entries giving
@@ -428,6 +414,16 @@ struct bplus_header
   } u;
 };
 
+static inline bool bp_internal(struct bplus_header *bp)
+{
+       return bp->flags & BP_internal;
+}
+
+static inline bool bp_fnode_parent(struct bplus_header *bp)
+{
+       return bp->flags & BP_fnode_parent;
+}
+
 /* fnode: root of allocation b+ tree, and EA's */
 
 /* Every file and every directory has one fnode, pointed to by the directory
@@ -436,62 +432,56 @@ struct bplus_header
 
 #define FNODE_MAGIC 0xf7e40aae
 
+enum {FNODE_anode = cpu_to_le16(2), FNODE_dir = cpu_to_le16(256)};
 struct fnode
 {
-  u32 magic;                           /* f7e4 0aae */
-  u32 zero1[2];                                /* read history */
+  __le32 magic;                                /* f7e4 0aae */
+  __le32 zero1[2];                     /* read history */
   u8 len, name[15];                    /* true length, truncated name */
-  fnode_secno up;                      /* pointer to file's directory fnode */
-  secno acl_size_l;
-  secno acl_secno;
-  u16 acl_size_s;
+  __le32 up;                           /* pointer to file's directory fnode */
+  __le32 acl_size_l;
+  __le32 acl_secno;
+  __le16 acl_size_s;
   u8 acl_anode;
   u8 zero2;                            /* history bit count */
-  u32 ea_size_l;                       /* length of disk-resident ea's */
-  secno ea_secno;                      /* first sector of disk-resident ea's*/
-  u16 ea_size_s;                       /* length of fnode-resident ea's */
-
-#ifdef __LITTLE_ENDIAN
-  u8 flag0: 1;
-  u8 ea_anode: 1;                      /* 1 -> ea_secno is an anode */
-  u8 flag234567: 6;
-#else
-  u8 flag234567: 6;
-  u8 ea_anode: 1;                      /* 1 -> ea_secno is an anode */
-  u8 flag0: 1;
-#endif
+  __le32 ea_size_l;                    /* length of disk-resident ea's */
+  __le32 ea_secno;                     /* first sector of disk-resident ea's*/
+  __le16 ea_size_s;                    /* length of fnode-resident ea's */
 
-#ifdef __LITTLE_ENDIAN
-  u8 dirflag: 1;                       /* 1 -> directory.  first & only extent
-                                          points to dnode. */
-  u8 flag9012345: 7;
-#else
-  u8 flag9012345: 7;
-  u8 dirflag: 1;                       /* 1 -> directory.  first & only extent
+  __le16 flags;                                /* bit 1 set -> ea_secno is an anode */
+                                       /* bit 8 set -> directory.  first & only extent
                                           points to dnode. */
-#endif
-
   struct bplus_header btree;           /* b+ tree, 8 extents or 12 subtrees */
   union {
     struct bplus_leaf_node external[8];
     struct bplus_internal_node internal[12];
   } u;
 
-  u32 file_size;                       /* file length, bytes */
-  u32 n_needea;                                /* number of EA's with NEEDEA set */
+  __le32 file_size;                    /* file length, bytes */
+  __le32 n_needea;                     /* number of EA's with NEEDEA set */
   u8 user_id[16];                      /* unused */
-  u16 ea_offs;                         /* offset from start of fnode
+  __le16 ea_offs;                      /* offset from start of fnode
                                           to first fnode-resident ea */
   u8 dasd_limit_treshhold;
   u8 dasd_limit_delta;
-  u32 dasd_limit;
-  u32 dasd_usage;
+  __le32 dasd_limit;
+  __le32 dasd_usage;
   u8 ea[316];                          /* zero or more EA's, packed together
                                           with no alignment padding.
                                           (Do not use this name, get here
                                           via fnode + ea_offs. I think.) */
 };
 
+static inline bool fnode_in_anode(struct fnode *p)
+{
+       return (p->flags & FNODE_anode) != 0;
+}
+
+static inline bool fnode_is_dir(struct fnode *p)
+{
+       return (p->flags & FNODE_dir) != 0;
+}
+
 
 /* anode: 99.44% pure allocation tree */
 
@@ -499,9 +489,9 @@ struct fnode
 
 struct anode
 {
-  u32 magic;                           /* 37e4 0aae */
-  anode_secno self;                    /* pointer to this anode */
-  secno up;                            /* parent anode or fnode */
+  __le32 magic;                                /* 37e4 0aae */
+  __le32 self;                         /* pointer to this anode */
+  __le32 up;                           /* parent anode or fnode */
 
   struct bplus_header btree;           /* b+tree, 40 extents or 60 subtrees */
   union {
@@ -509,7 +499,7 @@ struct anode
     struct bplus_internal_node internal[60];
   } u;
 
-  u32 fill[3];                         /* unused */
+  __le32 fill[3];                      /* unused */
 };
 
 
@@ -528,32 +518,23 @@ struct anode
    run, or in multiple runs.  Flags in the fnode tell whether the EA list
    is immediate, in a single run, or in multiple runs. */
 
+enum {EA_indirect = 1, EA_anode = 2, EA_needea = 128 };
 struct extended_attribute
 {
-#ifdef __LITTLE_ENDIAN
-  u8 indirect: 1;                      /* 1 -> value gives sector number
+  u8 flags;                            /* bit 0 set -> value gives sector number
                                           where real value starts */
-  u8 anode: 1;                         /* 1 -> sector is an anode
+                                       /* bit 1 set -> sector is an anode
                                           that points to fragmented value */
-  u8 flag23456: 5;
-  u8 needea: 1;                                /* required ea */
-#else
-  u8 needea: 1;                                /* required ea */
-  u8 flag23456: 5;
-  u8 anode: 1;                         /* 1 -> sector is an anode
-                                          that points to fragmented value */
-  u8 indirect: 1;                      /* 1 -> value gives sector number
-                                          where real value starts */
-#endif
+                                       /* bit 7 set -> required ea */
   u8 namelen;                          /* length of name, bytes */
   u8 valuelen_lo;                      /* length of value, bytes */
   u8 valuelen_hi;                      /* length of value, bytes */
-  u8 name[0];
+  u8 name[];
   /*
     u8 name[namelen];                  ascii attrib name
     u8 nul;                            terminating '\0', not counted
     u8 value[valuelen];                        value, arbitrary
-      if this.indirect, valuelen is 8 and the value is
+      if this.flags & 1, valuelen is 8 and the value is
         u32 length;                    real length of value, bytes
         secno secno;                   sector address where it starts
       if this.anode, the above sector number is the root of an anode tree
@@ -561,6 +542,16 @@ struct extended_attribute
   */
 };
 
+static inline bool ea_indirect(struct extended_attribute *ea)
+{
+       return ea->flags & EA_indirect;
+}
+
+static inline bool ea_in_anode(struct extended_attribute *ea)
+{
+       return ea->flags & EA_anode;
+}
+
 /*
    Local Variables:
    comment-column: 40
index de946170ebb1092937a1efd5f8dbd104f064d170..c07ef1f1ced60a0cf295772a218575d9c78e58d1 100644 (file)
 
 #define CHKCOND(x,y) if (!(x)) printk y
 
-#ifdef DBG
-#define PRINTK(x) printk x
-#else
-#undef PRINTK
-#define PRINTK(x)
-#endif
-
 struct hpfs_inode_info {
        loff_t mmu_private;
        ino_t i_parent_dir;     /* (directories) gives fnode of parent dir */
@@ -82,7 +75,7 @@ struct hpfs_sb_info {
        unsigned char *sb_cp_table;     /* code page tables: */
                                        /*      128 bytes uppercasing table & */
                                        /*      128 bytes lowercasing table */
-       unsigned *sb_bmp_dir;           /* main bitmap directory */
+       __le32 *sb_bmp_dir;             /* main bitmap directory */
        unsigned sb_c_bitmap;           /* current bitmap */
        unsigned sb_max_fwd_alloc;      /* max forwad allocation */
        int sb_timeshift;
@@ -100,7 +93,7 @@ struct quad_buffer_head {
 static inline dnode_secno de_down_pointer (struct hpfs_dirent *de)
 {
   CHKCOND(de->down,("HPFS: de_down_pointer: !de->down\n"));
-  return le32_to_cpu(*(dnode_secno *) ((void *) de + le16_to_cpu(de->length) - 4));
+  return le32_to_cpu(*(__le32 *) ((void *) de + le16_to_cpu(de->length) - 4));
 }
 
 /* The first dir entry in a dnode */
@@ -148,12 +141,12 @@ static inline struct extended_attribute *next_ea(struct extended_attribute *ea)
 
 static inline secno ea_sec(struct extended_attribute *ea)
 {
-       return le32_to_cpu(get_unaligned((secno *)((char *)ea + 9 + ea->namelen)));
+       return le32_to_cpu(get_unaligned((__le32 *)((char *)ea + 9 + ea->namelen)));
 }
 
 static inline secno ea_len(struct extended_attribute *ea)
 {
-       return le32_to_cpu(get_unaligned((secno *)((char *)ea + 5 + ea->namelen)));
+       return le32_to_cpu(get_unaligned((__le32 *)((char *)ea + 5 + ea->namelen)));
 }
 
 static inline char *ea_data(struct extended_attribute *ea)
@@ -178,7 +171,7 @@ static inline void copy_de(struct hpfs_dirent *dst, struct hpfs_dirent *src)
        dst->not_8x3 = n;
 }
 
-static inline unsigned tstbits(u32 *bmp, unsigned b, unsigned n)
+static inline unsigned tstbits(__le32 *bmp, unsigned b, unsigned n)
 {
        int i;
        if ((b >= 0x4000) || (b + n - 1 >= 0x4000)) return n;
@@ -275,10 +268,10 @@ void hpfs_evict_inode(struct inode *);
 
 /* map.c */
 
-unsigned *hpfs_map_dnode_bitmap(struct super_block *, struct quad_buffer_head *);
-unsigned *hpfs_map_bitmap(struct super_block *, unsigned, struct quad_buffer_head *, char *);
+__le32 *hpfs_map_dnode_bitmap(struct super_block *, struct quad_buffer_head *);
+__le32 *hpfs_map_bitmap(struct super_block *, unsigned, struct quad_buffer_head *, char *);
 unsigned char *hpfs_load_code_page(struct super_block *, secno);
-secno *hpfs_load_bitmap_directory(struct super_block *, secno bmp);
+__le32 *hpfs_load_bitmap_directory(struct super_block *, secno bmp);
 struct fnode *hpfs_map_fnode(struct super_block *s, ino_t, struct buffer_head **);
 struct anode *hpfs_map_anode(struct super_block *s, anode_secno, struct buffer_head **);
 struct dnode *hpfs_map_dnode(struct super_block *s, dnode_secno, struct quad_buffer_head *);
index b43066cbdc6a7cd201538cdc86e28fd9d22deac8..ed671e0ea78443b35bb6d1dd3eabc64bd7559c1f 100644 (file)
@@ -110,7 +110,7 @@ void hpfs_read_inode(struct inode *i)
                        }
                }
        }
-       if (fnode->dirflag) {
+       if (fnode_is_dir(fnode)) {
                int n_dnodes, n_subdirs;
                i->i_mode |= S_IFDIR;
                i->i_op = &hpfs_dir_iops;
index a790821366a7f045d068fe47df517dc479b0ecce..4acb19d78359d4bec83f90b854680dc3962905cf 100644 (file)
@@ -8,12 +8,12 @@
 
 #include "hpfs_fn.h"
 
-unsigned *hpfs_map_dnode_bitmap(struct super_block *s, struct quad_buffer_head *qbh)
+__le32 *hpfs_map_dnode_bitmap(struct super_block *s, struct quad_buffer_head *qbh)
 {
        return hpfs_map_4sectors(s, hpfs_sb(s)->sb_dmap, qbh, 0);
 }
 
-unsigned int *hpfs_map_bitmap(struct super_block *s, unsigned bmp_block,
+__le32 *hpfs_map_bitmap(struct super_block *s, unsigned bmp_block,
                         struct quad_buffer_head *qbh, char *id)
 {
        secno sec;
@@ -89,18 +89,18 @@ unsigned char *hpfs_load_code_page(struct super_block *s, secno cps)
        return cp_table;
 }
 
-secno *hpfs_load_bitmap_directory(struct super_block *s, secno bmp)
+__le32 *hpfs_load_bitmap_directory(struct super_block *s, secno bmp)
 {
        struct buffer_head *bh;
        int n = (hpfs_sb(s)->sb_fs_size + 0x200000 - 1) >> 21;
        int i;
-       secno *b;
+       __le32 *b;
        if (!(b = kmalloc(n * 512, GFP_KERNEL))) {
                printk("HPFS: can't allocate memory for bitmap directory\n");
                return NULL;
        }       
        for (i=0;i<n;i++) {
-               secno *d = hpfs_map_sector(s, bmp+i, &bh, n - i - 1);
+               __le32 *d = hpfs_map_sector(s, bmp+i, &bh, n - i - 1);
                if (!d) {
                        kfree(b);
                        return NULL;
@@ -130,16 +130,16 @@ struct fnode *hpfs_map_fnode(struct super_block *s, ino_t ino, struct buffer_hea
                                        (unsigned long)ino);
                                goto bail;
                        }
-                       if (!fnode->dirflag) {
+                       if (!fnode_is_dir(fnode)) {
                                if ((unsigned)fnode->btree.n_used_nodes + (unsigned)fnode->btree.n_free_nodes !=
-                                   (fnode->btree.internal ? 12 : 8)) {
+                                   (bp_internal(&fnode->btree) ? 12 : 8)) {
                                        hpfs_error(s,
                                           "bad number of nodes in fnode %08lx",
                                            (unsigned long)ino);
                                        goto bail;
                                }
                                if (le16_to_cpu(fnode->btree.first_free) !=
-                                   8 + fnode->btree.n_used_nodes * (fnode->btree.internal ? 8 : 12)) {
+                                   8 + fnode->btree.n_used_nodes * (bp_internal(&fnode->btree) ? 8 : 12)) {
                                        hpfs_error(s,
                                            "bad first_free pointer in fnode %08lx",
                                            (unsigned long)ino);
@@ -187,12 +187,12 @@ struct anode *hpfs_map_anode(struct super_block *s, anode_secno ano, struct buff
                                goto bail;
                        }
                        if ((unsigned)anode->btree.n_used_nodes + (unsigned)anode->btree.n_free_nodes !=
-                           (anode->btree.internal ? 60 : 40)) {
+                           (bp_internal(&anode->btree) ? 60 : 40)) {
                                hpfs_error(s, "bad number of nodes in anode %08x", ano);
                                goto bail;
                        }
                        if (le16_to_cpu(anode->btree.first_free) !=
-                           8 + anode->btree.n_used_nodes * (anode->btree.internal ? 8 : 12)) {
+                           8 + anode->btree.n_used_nodes * (bp_internal(&anode->btree) ? 8 : 12)) {
                                hpfs_error(s, "bad first_free pointer in anode %08x", ano);
                                goto bail;
                        }
index 30dd7b10b507a077877d58a2bb4d5ada18ee3101..9083ef8af58c162f7fd207f7ef37263b1f35de4f 100644 (file)
@@ -70,7 +70,7 @@ static int hpfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
        fnode->len = len;
        memcpy(fnode->name, name, len > 15 ? 15 : len);
        fnode->up = cpu_to_le32(dir->i_ino);
-       fnode->dirflag = 1;
+       fnode->flags |= FNODE_dir;
        fnode->btree.n_free_nodes = 7;
        fnode->btree.n_used_nodes = 1;
        fnode->btree.first_free = cpu_to_le16(0x14);
index 54f6eccb79d9ed8c67f7ada5a96867ad4c61b37c..706a12c083ea726a7a268d647ae266b02a3a2ca7 100644 (file)
@@ -572,7 +572,7 @@ static int hpfs_fill_super(struct super_block *s, void *options, int silent)
                mark_buffer_dirty(bh2);
        }
 
-       if (le32_to_cpu(spareblock->hotfixes_used) || le32_to_cpu(spareblock->n_spares_used)) {
+       if (spareblock->hotfixes_used || spareblock->n_spares_used) {
                if (errs >= 2) {
                        printk("HPFS: Hotfixes not supported here, try chkdsk\n");
                        mark_dirty(s, 0);
@@ -645,7 +645,7 @@ static int hpfs_fill_super(struct super_block *s, void *options, int silent)
                root->i_mtime.tv_nsec = 0;
                root->i_ctime.tv_sec = local_to_gmt(s, le32_to_cpu(de->creation_date));
                root->i_ctime.tv_nsec = 0;
-               hpfs_i(root)->i_ea_size = le16_to_cpu(de->ea_size);
+               hpfs_i(root)->i_ea_size = le32_to_cpu(de->ea_size);
                hpfs_i(root)->i_parent_dir = root->i_ino;
                if (root->i_size == -1)
                        root->i_size = 2048;
index 6bc8761cc3333524bf6c0408a4227f14a325bf7e..c99163b1b31036ef68974c0c5dbc192f8f73f4da 100644 (file)
@@ -1487,10 +1487,30 @@ static int relatime_need_update(struct vfsmount *mnt, struct inode *inode,
        return 0;
 }
 
+/*
+ * This does the actual work of updating an inodes time or version.  Must have
+ * had called mnt_want_write() before calling this.
+ */
+static int update_time(struct inode *inode, struct timespec *time, int flags)
+{
+       if (inode->i_op->update_time)
+               return inode->i_op->update_time(inode, time, flags);
+
+       if (flags & S_ATIME)
+               inode->i_atime = *time;
+       if (flags & S_VERSION)
+               inode_inc_iversion(inode);
+       if (flags & S_CTIME)
+               inode->i_ctime = *time;
+       if (flags & S_MTIME)
+               inode->i_mtime = *time;
+       mark_inode_dirty_sync(inode);
+       return 0;
+}
+
 /**
  *     touch_atime     -       update the access time
- *     @mnt: mount the inode is accessed on
- *     @dentry: dentry accessed
+ *     @path: the &struct path to update
  *
  *     Update the accessed time on an inode and mark it for writeback.
  *     This function automatically handles read only file systems and media,
@@ -1525,12 +1545,83 @@ void touch_atime(struct path *path)
        if (mnt_want_write(mnt))
                return;
 
-       inode->i_atime = now;
-       mark_inode_dirty_sync(inode);
+       /*
+        * File systems can error out when updating inodes if they need to
+        * allocate new space to modify an inode (such is the case for
+        * Btrfs), but since we touch atime while walking down the path we
+        * really don't care if we failed to update the atime of the file,
+        * so just ignore the return value.
+        */
+       update_time(inode, &now, S_ATIME);
        mnt_drop_write(mnt);
 }
 EXPORT_SYMBOL(touch_atime);
 
+/*
+ * The logic we want is
+ *
+ *     if suid or (sgid and xgrp)
+ *             remove privs
+ */
+int should_remove_suid(struct dentry *dentry)
+{
+       umode_t mode = dentry->d_inode->i_mode;
+       int kill = 0;
+
+       /* suid always must be killed */
+       if (unlikely(mode & S_ISUID))
+               kill = ATTR_KILL_SUID;
+
+       /*
+        * sgid without any exec bits is just a mandatory locking mark; leave
+        * it alone.  If some exec bits are set, it's a real sgid; kill it.
+        */
+       if (unlikely((mode & S_ISGID) && (mode & S_IXGRP)))
+               kill |= ATTR_KILL_SGID;
+
+       if (unlikely(kill && !capable(CAP_FSETID) && S_ISREG(mode)))
+               return kill;
+
+       return 0;
+}
+EXPORT_SYMBOL(should_remove_suid);
+
+static int __remove_suid(struct dentry *dentry, int kill)
+{
+       struct iattr newattrs;
+
+       newattrs.ia_valid = ATTR_FORCE | kill;
+       return notify_change(dentry, &newattrs);
+}
+
+int file_remove_suid(struct file *file)
+{
+       struct dentry *dentry = file->f_path.dentry;
+       struct inode *inode = dentry->d_inode;
+       int killsuid;
+       int killpriv;
+       int error = 0;
+
+       /* Fast path for nothing security related */
+       if (IS_NOSEC(inode))
+               return 0;
+
+       killsuid = should_remove_suid(dentry);
+       killpriv = security_inode_need_killpriv(dentry);
+
+       if (killpriv < 0)
+               return killpriv;
+       if (killpriv)
+               error = security_inode_killpriv(dentry);
+       if (!error && killsuid)
+               error = __remove_suid(dentry, killsuid);
+       if (!error && (inode->i_sb->s_flags & MS_NOSEC))
+               inode->i_flags |= S_NOSEC;
+
+       return error;
+}
+EXPORT_SYMBOL(file_remove_suid);
+
 /**
  *     file_update_time        -       update mtime and ctime time
  *     @file: file accessed
@@ -1540,18 +1631,20 @@ EXPORT_SYMBOL(touch_atime);
  *     usage in the file write path of filesystems, and filesystems may
  *     choose to explicitly ignore update via this function with the
  *     S_NOCMTIME inode flag, e.g. for network filesystem where these
- *     timestamps are handled by the server.
+ *     timestamps are handled by the server.  This can return an error for
+ *     file systems who need to allocate space in order to update an inode.
  */
 
-void file_update_time(struct file *file)
+int file_update_time(struct file *file)
 {
        struct inode *inode = file->f_path.dentry->d_inode;
        struct timespec now;
-       enum { S_MTIME = 1, S_CTIME = 2, S_VERSION = 4 } sync_it = 0;
+       int sync_it = 0;
+       int ret;
 
        /* First try to exhaust all avenues to not sync */
        if (IS_NOCMTIME(inode))
-               return;
+               return 0;
 
        now = current_fs_time(inode->i_sb);
        if (!timespec_equal(&inode->i_mtime, &now))
@@ -1564,21 +1657,16 @@ void file_update_time(struct file *file)
                sync_it |= S_VERSION;
 
        if (!sync_it)
-               return;
+               return 0;
 
        /* Finally allowed to write? Takes lock. */
        if (mnt_want_write_file(file))
-               return;
+               return 0;
 
-       /* Only change inode inside the lock region */
-       if (sync_it & S_VERSION)
-               inode_inc_iversion(inode);
-       if (sync_it & S_CTIME)
-               inode->i_ctime = now;
-       if (sync_it & S_MTIME)
-               inode->i_mtime = now;
-       mark_inode_dirty_sync(inode);
+       ret = update_time(inode, &now, sync_it);
        mnt_drop_write_file(file);
+
+       return ret;
 }
 EXPORT_SYMBOL(file_update_time);
 
@@ -1748,3 +1836,50 @@ bool inode_owner_or_capable(const struct inode *inode)
        return false;
 }
 EXPORT_SYMBOL(inode_owner_or_capable);
+
+/*
+ * Direct i/o helper functions
+ */
+static void __inode_dio_wait(struct inode *inode)
+{
+       wait_queue_head_t *wq = bit_waitqueue(&inode->i_state, __I_DIO_WAKEUP);
+       DEFINE_WAIT_BIT(q, &inode->i_state, __I_DIO_WAKEUP);
+
+       do {
+               prepare_to_wait(wq, &q.wait, TASK_UNINTERRUPTIBLE);
+               if (atomic_read(&inode->i_dio_count))
+                       schedule();
+       } while (atomic_read(&inode->i_dio_count));
+       finish_wait(wq, &q.wait);
+}
+
+/**
+ * inode_dio_wait - wait for outstanding DIO requests to finish
+ * @inode: inode to wait for
+ *
+ * Waits for all pending direct I/O requests to finish so that we can
+ * proceed with a truncate or equivalent operation.
+ *
+ * Must be called under a lock that serializes taking new references
+ * to i_dio_count, usually by inode->i_mutex.
+ */
+void inode_dio_wait(struct inode *inode)
+{
+       if (atomic_read(&inode->i_dio_count))
+               __inode_dio_wait(inode);
+}
+EXPORT_SYMBOL(inode_dio_wait);
+
+/*
+ * inode_dio_done - signal finish of a direct I/O requests
+ * @inode: inode the direct I/O happens on
+ *
+ * This is called once we've finished processing a direct I/O request,
+ * and is used to wake up callers waiting for direct I/O to be quiesced.
+ */
+void inode_dio_done(struct inode *inode)
+{
+       if (atomic_dec_and_test(&inode->i_dio_count))
+               wake_up_bit(&inode->i_state, __I_DIO_WAKEUP);
+}
+EXPORT_SYMBOL(inode_dio_done);
index 9962c59ba280b1c75d78adc55b8491733075a5e0..18bc216ea09d95ecff126ef96987ff786b5cbcb1 100644 (file)
@@ -56,7 +56,7 @@ extern int sb_prepare_remount_readonly(struct super_block *);
 
 extern void __init mnt_init(void);
 
-DECLARE_BRLOCK(vfsmount_lock);
+extern struct lglock vfsmount_lock;
 
 
 /*
@@ -100,6 +100,7 @@ extern struct file *do_file_open_root(struct dentry *, struct vfsmount *,
 
 extern long do_handle_open(int mountdirfd,
                           struct file_handle __user *ufh, int open_flag);
+extern int open_check_o_direct(struct file *f);
 
 /*
  * inode.c
index 5e6dbe8958fc7b8fa650ec23d4ebe67de06f13a3..e50170ca7c33f446acc16e29a0d0097828919c30 100644 (file)
@@ -50,7 +50,7 @@ int set_task_ioprio(struct task_struct *task, int ioprio)
 
        ioc = get_task_io_context(task, GFP_ATOMIC, NUMA_NO_NODE);
        if (ioc) {
-               ioc_ioprio_changed(ioc, ioprio);
+               ioc->ioprio = ioprio;
                put_io_context(ioc);
        }
 
index dd4687ff30d09900a14f113aec870007cfcfb7f0..aa4356d09eeeb03167bcf506a7fe8ad98efaba39 100644 (file)
@@ -107,12 +107,11 @@ static struct dentry *isofs_export_get_parent(struct dentry *child)
 }
 
 static int
-isofs_export_encode_fh(struct dentry *dentry,
+isofs_export_encode_fh(struct inode *inode,
                       __u32 *fh32,
                       int *max_len,
-                      int connectable)
+                      struct inode *parent)
 {
-       struct inode * inode = dentry->d_inode;
        struct iso_inode_info * ei = ISOFS_I(inode);
        int len = *max_len;
        int type = 1;
@@ -124,7 +123,7 @@ isofs_export_encode_fh(struct dentry *dentry,
         * offset of the inode and the upper 16 bits of fh32[1] to
         * hold the offset of the parent.
         */
-       if (connectable && (len < 5)) {
+       if (parent && (len < 5)) {
                *max_len = 5;
                return 255;
        } else if (len < 3) {
@@ -136,16 +135,12 @@ isofs_export_encode_fh(struct dentry *dentry,
        fh32[0] = ei->i_iget5_block;
        fh16[2] = (__u16)ei->i_iget5_offset;  /* fh16 [sic] */
        fh32[2] = inode->i_generation;
-       if (connectable && !S_ISDIR(inode->i_mode)) {
-               struct inode *parent;
+       if (parent) {
                struct iso_inode_info *eparent;
-               spin_lock(&dentry->d_lock);
-               parent = dentry->d_parent->d_inode;
                eparent = ISOFS_I(parent);
                fh32[3] = eparent->i_iget5_block;
                fh16[3] = (__u16)eparent->i_iget5_offset;  /* fh16 [sic] */
                fh32[4] = parent->i_generation;
-               spin_unlock(&dentry->d_lock);
                len = 5;
                type = 2;
        }
index f32f346f4b0a521a5b6bbaedc7b0a5a7750c4b1e..69a48c2944da682c8a133fe75183c086ef08813b 100644 (file)
@@ -1,6 +1,8 @@
 config JBD2
        tristate
        select CRC32
+       select CRYPTO
+       select CRYPTO_CRC32C
        help
          This is a generic journaling layer for block devices that support
          both 32-bit and 64-bit block numbers.  It is currently used by
index 840f70f507924a0ac4db70a9d729f715783b49be..216f4299f65e7e2f1e26859c8e1247cdf71c55df 100644 (file)
@@ -85,6 +85,24 @@ nope:
        __brelse(bh);
 }
 
+static void jbd2_commit_block_csum_set(journal_t *j,
+                                      struct journal_head *descriptor)
+{
+       struct commit_header *h;
+       __u32 csum;
+
+       if (!JBD2_HAS_INCOMPAT_FEATURE(j, JBD2_FEATURE_INCOMPAT_CSUM_V2))
+               return;
+
+       h = (struct commit_header *)(jh2bh(descriptor)->b_data);
+       h->h_chksum_type = 0;
+       h->h_chksum_size = 0;
+       h->h_chksum[0] = 0;
+       csum = jbd2_chksum(j, j->j_csum_seed, jh2bh(descriptor)->b_data,
+                          j->j_blocksize);
+       h->h_chksum[0] = cpu_to_be32(csum);
+}
+
 /*
  * Done it all: now submit the commit record.  We should have
  * cleaned up our previous buffers by now, so if we are in abort
@@ -128,6 +146,7 @@ static int journal_submit_commit_record(journal_t *journal,
                tmp->h_chksum_size      = JBD2_CRC32_CHKSUM_SIZE;
                tmp->h_chksum[0]        = cpu_to_be32(crc32_sum);
        }
+       jbd2_commit_block_csum_set(journal, descriptor);
 
        JBUFFER_TRACE(descriptor, "submit commit block");
        lock_buffer(bh);
@@ -301,6 +320,44 @@ static void write_tag_block(int tag_bytes, journal_block_tag_t *tag,
                tag->t_blocknr_high = cpu_to_be32((block >> 31) >> 1);
 }
 
+static void jbd2_descr_block_csum_set(journal_t *j,
+                                     struct journal_head *descriptor)
+{
+       struct jbd2_journal_block_tail *tail;
+       __u32 csum;
+
+       if (!JBD2_HAS_INCOMPAT_FEATURE(j, JBD2_FEATURE_INCOMPAT_CSUM_V2))
+               return;
+
+       tail = (struct jbd2_journal_block_tail *)
+                       (jh2bh(descriptor)->b_data + j->j_blocksize -
+                       sizeof(struct jbd2_journal_block_tail));
+       tail->t_checksum = 0;
+       csum = jbd2_chksum(j, j->j_csum_seed, jh2bh(descriptor)->b_data,
+                          j->j_blocksize);
+       tail->t_checksum = cpu_to_be32(csum);
+}
+
+static void jbd2_block_tag_csum_set(journal_t *j, journal_block_tag_t *tag,
+                                   struct buffer_head *bh, __u32 sequence)
+{
+       struct page *page = bh->b_page;
+       __u8 *addr;
+       __u32 csum;
+
+       if (!JBD2_HAS_INCOMPAT_FEATURE(j, JBD2_FEATURE_INCOMPAT_CSUM_V2))
+               return;
+
+       sequence = cpu_to_be32(sequence);
+       addr = kmap_atomic(page, KM_USER0);
+       csum = jbd2_chksum(j, j->j_csum_seed, (__u8 *)&sequence,
+                         sizeof(sequence));
+       csum = jbd2_chksum(j, csum, addr + offset_in_page(bh->b_data),
+                         bh->b_size);
+       kunmap_atomic(addr, KM_USER0);
+
+       tag->t_checksum = cpu_to_be32(csum);
+}
 /*
  * jbd2_journal_commit_transaction
  *
@@ -334,6 +391,10 @@ void jbd2_journal_commit_transaction(journal_t *journal)
        unsigned long first_block;
        tid_t first_tid;
        int update_tail;
+       int csum_size = 0;
+
+       if (JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_CSUM_V2))
+               csum_size = sizeof(struct jbd2_journal_block_tail);
 
        /*
         * First job: lock down the current transaction and wait for
@@ -627,7 +688,9 @@ void jbd2_journal_commit_transaction(journal_t *journal)
 
                tag = (journal_block_tag_t *) tagp;
                write_tag_block(tag_bytes, tag, jh2bh(jh)->b_blocknr);
-               tag->t_flags = cpu_to_be32(tag_flag);
+               tag->t_flags = cpu_to_be16(tag_flag);
+               jbd2_block_tag_csum_set(journal, tag, jh2bh(new_jh),
+                                       commit_transaction->t_tid);
                tagp += tag_bytes;
                space_left -= tag_bytes;
 
@@ -643,7 +706,7 @@ void jbd2_journal_commit_transaction(journal_t *journal)
 
                if (bufs == journal->j_wbufsize ||
                    commit_transaction->t_buffers == NULL ||
-                   space_left < tag_bytes + 16) {
+                   space_left < tag_bytes + 16 + csum_size) {
 
                        jbd_debug(4, "JBD2: Submit %d IOs\n", bufs);
 
@@ -651,8 +714,9 @@ void jbd2_journal_commit_transaction(journal_t *journal)
                            submitting the IOs.  "tag" still points to
                            the last tag we set up. */
 
-                       tag->t_flags |= cpu_to_be32(JBD2_FLAG_LAST_TAG);
+                       tag->t_flags |= cpu_to_be16(JBD2_FLAG_LAST_TAG);
 
+                       jbd2_descr_block_csum_set(journal, descriptor);
 start_journal_io:
                        for (i = 0; i < bufs; i++) {
                                struct buffer_head *bh = wbuf[i];
index 1afb701622b0b17748b4cd7f7d171df139bcc9cc..e9a3c4c85594e30aca1ed1f14d5667ba0595160a 100644 (file)
@@ -97,6 +97,43 @@ EXPORT_SYMBOL(jbd2_inode_cache);
 static void __journal_abort_soft (journal_t *journal, int errno);
 static int jbd2_journal_create_slab(size_t slab_size);
 
+/* Checksumming functions */
+int jbd2_verify_csum_type(journal_t *j, journal_superblock_t *sb)
+{
+       if (!JBD2_HAS_INCOMPAT_FEATURE(j, JBD2_FEATURE_INCOMPAT_CSUM_V2))
+               return 1;
+
+       return sb->s_checksum_type == JBD2_CRC32C_CHKSUM;
+}
+
+static __u32 jbd2_superblock_csum(journal_t *j, journal_superblock_t *sb)
+{
+       __u32 csum, old_csum;
+
+       old_csum = sb->s_checksum;
+       sb->s_checksum = 0;
+       csum = jbd2_chksum(j, ~0, (char *)sb, sizeof(journal_superblock_t));
+       sb->s_checksum = old_csum;
+
+       return cpu_to_be32(csum);
+}
+
+int jbd2_superblock_csum_verify(journal_t *j, journal_superblock_t *sb)
+{
+       if (!JBD2_HAS_INCOMPAT_FEATURE(j, JBD2_FEATURE_INCOMPAT_CSUM_V2))
+               return 1;
+
+       return sb->s_checksum == jbd2_superblock_csum(j, sb);
+}
+
+void jbd2_superblock_csum_set(journal_t *j, journal_superblock_t *sb)
+{
+       if (!JBD2_HAS_INCOMPAT_FEATURE(j, JBD2_FEATURE_INCOMPAT_CSUM_V2))
+               return;
+
+       sb->s_checksum = jbd2_superblock_csum(j, sb);
+}
+
 /*
  * Helper function used to manage commit timeouts
  */
@@ -1348,6 +1385,7 @@ static void jbd2_journal_update_sb_errno(journal_t *journal)
        jbd_debug(1, "JBD2: updating superblock error (errno %d)\n",
                  journal->j_errno);
        sb->s_errno    = cpu_to_be32(journal->j_errno);
+       jbd2_superblock_csum_set(journal, sb);
        read_unlock(&journal->j_state_lock);
 
        jbd2_write_superblock(journal, WRITE_SYNC);
@@ -1376,6 +1414,9 @@ static int journal_get_superblock(journal_t *journal)
                }
        }
 
+       if (buffer_verified(bh))
+               return 0;
+
        sb = journal->j_superblock;
 
        err = -EINVAL;
@@ -1413,6 +1454,43 @@ static int journal_get_superblock(journal_t *journal)
                goto out;
        }
 
+       if (JBD2_HAS_COMPAT_FEATURE(journal, JBD2_FEATURE_COMPAT_CHECKSUM) &&
+           JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_CSUM_V2)) {
+               /* Can't have checksum v1 and v2 on at the same time! */
+               printk(KERN_ERR "JBD: Can't enable checksumming v1 and v2 "
+                      "at the same time!\n");
+               goto out;
+       }
+
+       if (!jbd2_verify_csum_type(journal, sb)) {
+               printk(KERN_ERR "JBD: Unknown checksum type\n");
+               goto out;
+       }
+
+       /* Load the checksum driver */
+       if (JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_CSUM_V2)) {
+               journal->j_chksum_driver = crypto_alloc_shash("crc32c", 0, 0);
+               if (IS_ERR(journal->j_chksum_driver)) {
+                       printk(KERN_ERR "JBD: Cannot load crc32c driver.\n");
+                       err = PTR_ERR(journal->j_chksum_driver);
+                       journal->j_chksum_driver = NULL;
+                       goto out;
+               }
+       }
+
+       /* Check superblock checksum */
+       if (!jbd2_superblock_csum_verify(journal, sb)) {
+               printk(KERN_ERR "JBD: journal checksum error\n");
+               goto out;
+       }
+
+       /* Precompute checksum seed for all metadata */
+       if (JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_CSUM_V2))
+               journal->j_csum_seed = jbd2_chksum(journal, ~0, sb->s_uuid,
+                                                  sizeof(sb->s_uuid));
+
+       set_buffer_verified(bh);
+
        return 0;
 
 out:
@@ -1564,6 +1642,8 @@ int jbd2_journal_destroy(journal_t *journal)
                iput(journal->j_inode);
        if (journal->j_revoke)
                jbd2_journal_destroy_revoke(journal);
+       if (journal->j_chksum_driver)
+               crypto_free_shash(journal->j_chksum_driver);
        kfree(journal->j_wbuf);
        kfree(journal);
 
@@ -1653,6 +1733,10 @@ int jbd2_journal_check_available_features (journal_t *journal, unsigned long com
 int jbd2_journal_set_features (journal_t *journal, unsigned long compat,
                          unsigned long ro, unsigned long incompat)
 {
+#define INCOMPAT_FEATURE_ON(f) \
+               ((incompat & (f)) && !(sb->s_feature_incompat & cpu_to_be32(f)))
+#define COMPAT_FEATURE_ON(f) \
+               ((compat & (f)) && !(sb->s_feature_compat & cpu_to_be32(f)))
        journal_superblock_t *sb;
 
        if (jbd2_journal_check_used_features(journal, compat, ro, incompat))
@@ -1661,16 +1745,54 @@ int jbd2_journal_set_features (journal_t *journal, unsigned long compat,
        if (!jbd2_journal_check_available_features(journal, compat, ro, incompat))
                return 0;
 
+       /* Asking for checksumming v2 and v1?  Only give them v2. */
+       if (incompat & JBD2_FEATURE_INCOMPAT_CSUM_V2 &&
+           compat & JBD2_FEATURE_COMPAT_CHECKSUM)
+               compat &= ~JBD2_FEATURE_COMPAT_CHECKSUM;
+
        jbd_debug(1, "Setting new features 0x%lx/0x%lx/0x%lx\n",
                  compat, ro, incompat);
 
        sb = journal->j_superblock;
 
+       /* If enabling v2 checksums, update superblock */
+       if (INCOMPAT_FEATURE_ON(JBD2_FEATURE_INCOMPAT_CSUM_V2)) {
+               sb->s_checksum_type = JBD2_CRC32C_CHKSUM;
+               sb->s_feature_compat &=
+                       ~cpu_to_be32(JBD2_FEATURE_COMPAT_CHECKSUM);
+
+               /* Load the checksum driver */
+               if (journal->j_chksum_driver == NULL) {
+                       journal->j_chksum_driver = crypto_alloc_shash("crc32c",
+                                                                     0, 0);
+                       if (IS_ERR(journal->j_chksum_driver)) {
+                               printk(KERN_ERR "JBD: Cannot load crc32c "
+                                      "driver.\n");
+                               journal->j_chksum_driver = NULL;
+                               return 0;
+                       }
+               }
+
+               /* Precompute checksum seed for all metadata */
+               if (JBD2_HAS_INCOMPAT_FEATURE(journal,
+                                             JBD2_FEATURE_INCOMPAT_CSUM_V2))
+                       journal->j_csum_seed = jbd2_chksum(journal, ~0,
+                                                          sb->s_uuid,
+                                                          sizeof(sb->s_uuid));
+       }
+
+       /* If enabling v1 checksums, downgrade superblock */
+       if (COMPAT_FEATURE_ON(JBD2_FEATURE_COMPAT_CHECKSUM))
+               sb->s_feature_incompat &=
+                       ~cpu_to_be32(JBD2_FEATURE_INCOMPAT_CSUM_V2);
+
        sb->s_feature_compat    |= cpu_to_be32(compat);
        sb->s_feature_ro_compat |= cpu_to_be32(ro);
        sb->s_feature_incompat  |= cpu_to_be32(incompat);
 
        return 1;
+#undef COMPAT_FEATURE_ON
+#undef INCOMPAT_FEATURE_ON
 }
 
 /*
@@ -1975,10 +2097,16 @@ int jbd2_journal_blocks_per_page(struct inode *inode)
  */
 size_t journal_tag_bytes(journal_t *journal)
 {
+       journal_block_tag_t tag;
+       size_t x = 0;
+
+       if (JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_CSUM_V2))
+               x += sizeof(tag.t_checksum);
+
        if (JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_64BIT))
-               return JBD2_TAG_SIZE64;
+               return x + JBD2_TAG_SIZE64;
        else
-               return JBD2_TAG_SIZE32;
+               return x + JBD2_TAG_SIZE32;
 }
 
 /*
index c1a03354a22ff1b5a787251b422afcb5225ca2c9..0131e4362534c4d5b83273130ee292463ec49f07 100644 (file)
@@ -174,6 +174,25 @@ static int jread(struct buffer_head **bhp, journal_t *journal,
        return 0;
 }
 
+static int jbd2_descr_block_csum_verify(journal_t *j,
+                                       void *buf)
+{
+       struct jbd2_journal_block_tail *tail;
+       __u32 provided, calculated;
+
+       if (!JBD2_HAS_INCOMPAT_FEATURE(j, JBD2_FEATURE_INCOMPAT_CSUM_V2))
+               return 1;
+
+       tail = (struct jbd2_journal_block_tail *)(buf + j->j_blocksize -
+                       sizeof(struct jbd2_journal_block_tail));
+       provided = tail->t_checksum;
+       tail->t_checksum = 0;
+       calculated = jbd2_chksum(j, j->j_csum_seed, buf, j->j_blocksize);
+       tail->t_checksum = provided;
+
+       provided = be32_to_cpu(provided);
+       return provided == calculated;
+}
 
 /*
  * Count the number of in-use tags in a journal descriptor block.
@@ -186,6 +205,9 @@ static int count_tags(journal_t *journal, struct buffer_head *bh)
        int                     nr = 0, size = journal->j_blocksize;
        int                     tag_bytes = journal_tag_bytes(journal);
 
+       if (JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_CSUM_V2))
+               size -= sizeof(struct jbd2_journal_block_tail);
+
        tagp = &bh->b_data[sizeof(journal_header_t)];
 
        while ((tagp - bh->b_data + tag_bytes) <= size) {
@@ -193,10 +215,10 @@ static int count_tags(journal_t *journal, struct buffer_head *bh)
 
                nr++;
                tagp += tag_bytes;
-               if (!(tag->t_flags & cpu_to_be32(JBD2_FLAG_SAME_UUID)))
+               if (!(tag->t_flags & cpu_to_be16(JBD2_FLAG_SAME_UUID)))
                        tagp += 16;
 
-               if (tag->t_flags & cpu_to_be32(JBD2_FLAG_LAST_TAG))
+               if (tag->t_flags & cpu_to_be16(JBD2_FLAG_LAST_TAG))
                        break;
        }
 
@@ -353,6 +375,41 @@ static int calc_chksums(journal_t *journal, struct buffer_head *bh,
        return 0;
 }
 
+static int jbd2_commit_block_csum_verify(journal_t *j, void *buf)
+{
+       struct commit_header *h;
+       __u32 provided, calculated;
+
+       if (!JBD2_HAS_INCOMPAT_FEATURE(j, JBD2_FEATURE_INCOMPAT_CSUM_V2))
+               return 1;
+
+       h = buf;
+       provided = h->h_chksum[0];
+       h->h_chksum[0] = 0;
+       calculated = jbd2_chksum(j, j->j_csum_seed, buf, j->j_blocksize);
+       h->h_chksum[0] = provided;
+
+       provided = be32_to_cpu(provided);
+       return provided == calculated;
+}
+
+static int jbd2_block_tag_csum_verify(journal_t *j, journal_block_tag_t *tag,
+                                     void *buf, __u32 sequence)
+{
+       __u32 provided, calculated;
+
+       if (!JBD2_HAS_INCOMPAT_FEATURE(j, JBD2_FEATURE_INCOMPAT_CSUM_V2))
+               return 1;
+
+       sequence = cpu_to_be32(sequence);
+       calculated = jbd2_chksum(j, j->j_csum_seed, (__u8 *)&sequence,
+                                sizeof(sequence));
+       calculated = jbd2_chksum(j, calculated, buf, j->j_blocksize);
+       provided = be32_to_cpu(tag->t_checksum);
+
+       return provided == cpu_to_be32(calculated);
+}
+
 static int do_one_pass(journal_t *journal,
                        struct recovery_info *info, enum passtype pass)
 {
@@ -366,6 +423,7 @@ static int do_one_pass(journal_t *journal,
        int                     blocktype;
        int                     tag_bytes = journal_tag_bytes(journal);
        __u32                   crc32_sum = ~0; /* Transactional Checksums */
+       int                     descr_csum_size = 0;
 
        /*
         * First thing is to establish what we expect to find in the log
@@ -451,6 +509,18 @@ static int do_one_pass(journal_t *journal,
 
                switch(blocktype) {
                case JBD2_DESCRIPTOR_BLOCK:
+                       /* Verify checksum first */
+                       if (JBD2_HAS_INCOMPAT_FEATURE(journal,
+                                       JBD2_FEATURE_INCOMPAT_CSUM_V2))
+                               descr_csum_size =
+                                       sizeof(struct jbd2_journal_block_tail);
+                       if (descr_csum_size > 0 &&
+                           !jbd2_descr_block_csum_verify(journal,
+                                                         bh->b_data)) {
+                               err = -EIO;
+                               goto failed;
+                       }
+
                        /* If it is a valid descriptor block, replay it
                         * in pass REPLAY; if journal_checksums enabled, then
                         * calculate checksums in PASS_SCAN, otherwise,
@@ -481,11 +551,11 @@ static int do_one_pass(journal_t *journal,
 
                        tagp = &bh->b_data[sizeof(journal_header_t)];
                        while ((tagp - bh->b_data + tag_bytes)
-                              <= journal->j_blocksize) {
+                              <= journal->j_blocksize - descr_csum_size) {
                                unsigned long io_block;
 
                                tag = (journal_block_tag_t *) tagp;
-                               flags = be32_to_cpu(tag->t_flags);
+                               flags = be16_to_cpu(tag->t_flags);
 
                                io_block = next_log_block++;
                                wrap(journal, next_log_block);
@@ -516,6 +586,19 @@ static int do_one_pass(journal_t *journal,
                                                goto skip_write;
                                        }
 
+                                       /* Look for block corruption */
+                                       if (!jbd2_block_tag_csum_verify(
+                                               journal, tag, obh->b_data,
+                                               be32_to_cpu(tmp->h_sequence))) {
+                                               brelse(obh);
+                                               success = -EIO;
+                                               printk(KERN_ERR "JBD: Invalid "
+                                                      "checksum recovering "
+                                                      "block %llu in log\n",
+                                                      blocknr);
+                                               continue;
+                                       }
+
                                        /* Find a buffer for the new
                                         * data being restored */
                                        nbh = __getblk(journal->j_fs_dev,
@@ -650,6 +733,19 @@ static int do_one_pass(journal_t *journal,
                                }
                                crc32_sum = ~0;
                        }
+                       if (pass == PASS_SCAN &&
+                           !jbd2_commit_block_csum_verify(journal,
+                                                          bh->b_data)) {
+                               info->end_transaction = next_commit_ID;
+
+                               if (!JBD2_HAS_INCOMPAT_FEATURE(journal,
+                                    JBD2_FEATURE_INCOMPAT_ASYNC_COMMIT)) {
+                                       journal->j_failed_commit =
+                                               next_commit_ID;
+                                       brelse(bh);
+                                       break;
+                               }
+                       }
                        brelse(bh);
                        next_commit_ID++;
                        continue;
@@ -706,6 +802,25 @@ static int do_one_pass(journal_t *journal,
        return err;
 }
 
+static int jbd2_revoke_block_csum_verify(journal_t *j,
+                                        void *buf)
+{
+       struct jbd2_journal_revoke_tail *tail;
+       __u32 provided, calculated;
+
+       if (!JBD2_HAS_INCOMPAT_FEATURE(j, JBD2_FEATURE_INCOMPAT_CSUM_V2))
+               return 1;
+
+       tail = (struct jbd2_journal_revoke_tail *)(buf + j->j_blocksize -
+                       sizeof(struct jbd2_journal_revoke_tail));
+       provided = tail->r_checksum;
+       tail->r_checksum = 0;
+       calculated = jbd2_chksum(j, j->j_csum_seed, buf, j->j_blocksize);
+       tail->r_checksum = provided;
+
+       provided = be32_to_cpu(provided);
+       return provided == calculated;
+}
 
 /* Scan a revoke record, marking all blocks mentioned as revoked. */
 
@@ -720,6 +835,9 @@ static int scan_revoke_records(journal_t *journal, struct buffer_head *bh,
        offset = sizeof(jbd2_journal_revoke_header_t);
        max = be32_to_cpu(header->r_count);
 
+       if (!jbd2_revoke_block_csum_verify(journal, header))
+               return -EINVAL;
+
        if (JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_64BIT))
                record_len = 8;
 
index 6973705d6a3d9db1c96ed67f55c97c8a13ee2f6d..f30b80b4ce8bef98cab621bf731e13682661ca6d 100644 (file)
@@ -578,6 +578,7 @@ static void write_one_revoke_record(journal_t *journal,
                                    struct jbd2_revoke_record_s *record,
                                    int write_op)
 {
+       int csum_size = 0;
        struct journal_head *descriptor;
        int offset;
        journal_header_t *header;
@@ -592,9 +593,13 @@ static void write_one_revoke_record(journal_t *journal,
        descriptor = *descriptorp;
        offset = *offsetp;
 
+       /* Do we need to leave space at the end for a checksum? */
+       if (JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_CSUM_V2))
+               csum_size = sizeof(struct jbd2_journal_revoke_tail);
+
        /* Make sure we have a descriptor with space left for the record */
        if (descriptor) {
-               if (offset == journal->j_blocksize) {
+               if (offset >= journal->j_blocksize - csum_size) {
                        flush_descriptor(journal, descriptor, offset, write_op);
                        descriptor = NULL;
                }
@@ -631,6 +636,24 @@ static void write_one_revoke_record(journal_t *journal,
        *offsetp = offset;
 }
 
+static void jbd2_revoke_csum_set(journal_t *j,
+                                struct journal_head *descriptor)
+{
+       struct jbd2_journal_revoke_tail *tail;
+       __u32 csum;
+
+       if (!JBD2_HAS_INCOMPAT_FEATURE(j, JBD2_FEATURE_INCOMPAT_CSUM_V2))
+               return;
+
+       tail = (struct jbd2_journal_revoke_tail *)
+                       (jh2bh(descriptor)->b_data + j->j_blocksize -
+                       sizeof(struct jbd2_journal_revoke_tail));
+       tail->r_checksum = 0;
+       csum = jbd2_chksum(j, j->j_csum_seed, jh2bh(descriptor)->b_data,
+                          j->j_blocksize);
+       tail->r_checksum = cpu_to_be32(csum);
+}
+
 /*
  * Flush a revoke descriptor out to the journal.  If we are aborting,
  * this is a noop; otherwise we are generating a buffer which needs to
@@ -652,6 +675,8 @@ static void flush_descriptor(journal_t *journal,
 
        header = (jbd2_journal_revoke_header_t *) jh2bh(descriptor)->b_data;
        header->r_count = cpu_to_be32(offset);
+       jbd2_revoke_csum_set(journal, descriptor);
+
        set_buffer_jwrite(bh);
        BUFFER_TRACE(bh, "write");
        set_buffer_dirty(bh);
index ddcd3549c6c26cbc9cb9dd46831b189ed3c0441e..fb1ab9533b67277a557cd5f8ea9f7216b8284d4e 100644 (file)
@@ -162,8 +162,8 @@ static int start_this_handle(journal_t *journal, handle_t *handle,
 
 alloc_transaction:
        if (!journal->j_running_transaction) {
-               new_transaction = kmem_cache_alloc(transaction_cache,
-                                                  gfp_mask | __GFP_ZERO);
+               new_transaction = kmem_cache_zalloc(transaction_cache,
+                                                   gfp_mask);
                if (!new_transaction) {
                        /*
                         * If __GFP_FS is not present, then we may be
index 55a0c1dceadfddcf990b8fdbcfec015fc75fab32..44dca1f041c5cbc2b057e3eb7e13840c3edd25d7 100644 (file)
@@ -126,6 +126,10 @@ struct jffs2_sb_info {
        struct jffs2_inodirty *wbuf_inodes;
        struct rw_semaphore wbuf_sem;   /* Protects the write buffer */
 
+       struct delayed_work wbuf_dwork; /* write-buffer write-out work */
+       int wbuf_queued;                /* non-zero delayed work is queued */
+       spinlock_t wbuf_dwork_lock;     /* protects wbuf_dwork and and wbuf_queued */
+
        unsigned char *oobbuf;
        int oobavail; /* How many bytes are available for JFFS2 in OOB */
 #endif
index 1cd3aec9d9ae282dd31226d0717aaf69a55f414d..bcd983d7e7f99e7e295decc1d26092d464a14d9f 100644 (file)
@@ -95,6 +95,7 @@ static inline void jffs2_init_inode_info(struct jffs2_inode_info *f)
 #define jffs2_ubivol(c) (0)
 #define jffs2_ubivol_setup(c) (0)
 #define jffs2_ubivol_cleanup(c) do {} while (0)
+#define jffs2_dirty_trigger(c) do {} while (0)
 
 #else /* NAND and/or ECC'd NOR support present */
 
@@ -135,14 +136,10 @@ void jffs2_ubivol_cleanup(struct jffs2_sb_info *c);
 #define jffs2_nor_wbuf_flash(c) (c->mtd->type == MTD_NORFLASH && ! (c->mtd->flags & MTD_BIT_WRITEABLE))
 int jffs2_nor_wbuf_flash_setup(struct jffs2_sb_info *c);
 void jffs2_nor_wbuf_flash_cleanup(struct jffs2_sb_info *c);
+void jffs2_dirty_trigger(struct jffs2_sb_info *c);
 
 #endif /* WRITEBUFFER */
 
-static inline void jffs2_dirty_trigger(struct jffs2_sb_info *c)
-{
-       OFNI_BS_2SFFJ(c)->s_dirt = 1;
-}
-
 /* background.c */
 int jffs2_start_garbage_collect_thread(struct jffs2_sb_info *c);
 void jffs2_stop_garbage_collect_thread(struct jffs2_sb_info *c);
index f9916f312bd81e3590fde1c92a025458cb64ab11..bc586f204228633ed2d3bcf388991a0ec4b21ff8 100644 (file)
@@ -63,21 +63,6 @@ static void jffs2_i_init_once(void *foo)
        inode_init_once(&f->vfs_inode);
 }
 
-static void jffs2_write_super(struct super_block *sb)
-{
-       struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
-
-       lock_super(sb);
-       sb->s_dirt = 0;
-
-       if (!(sb->s_flags & MS_RDONLY)) {
-               jffs2_dbg(1, "%s()\n", __func__);
-               jffs2_flush_wbuf_gc(c, 0);
-       }
-
-       unlock_super(sb);
-}
-
 static const char *jffs2_compr_name(unsigned int compr)
 {
        switch (compr) {
@@ -113,8 +98,6 @@ static int jffs2_sync_fs(struct super_block *sb, int wait)
 {
        struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
 
-       jffs2_write_super(sb);
-
        mutex_lock(&c->alloc_sem);
        jffs2_flush_wbuf_pad(c);
        mutex_unlock(&c->alloc_sem);
@@ -251,7 +234,6 @@ static const struct super_operations jffs2_super_operations =
        .alloc_inode =  jffs2_alloc_inode,
        .destroy_inode =jffs2_destroy_inode,
        .put_super =    jffs2_put_super,
-       .write_super =  jffs2_write_super,
        .statfs =       jffs2_statfs,
        .remount_fs =   jffs2_remount_fs,
        .evict_inode =  jffs2_evict_inode,
@@ -319,9 +301,6 @@ static void jffs2_put_super (struct super_block *sb)
 
        jffs2_dbg(2, "%s()\n", __func__);
 
-       if (sb->s_dirt)
-               jffs2_write_super(sb);
-
        mutex_lock(&c->alloc_sem);
        jffs2_flush_wbuf_pad(c);
        mutex_unlock(&c->alloc_sem);
index 74d9be19df3f1fff1d7defdc7824c90240a302f6..6f4529d3697fd3f97d5b018dbe9f5c0362cee034 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/mtd/nand.h>
 #include <linux/jiffies.h>
 #include <linux/sched.h>
+#include <linux/writeback.h>
 
 #include "nodelist.h"
 
@@ -85,7 +86,7 @@ static void jffs2_wbuf_dirties_inode(struct jffs2_sb_info *c, uint32_t ino)
 {
        struct jffs2_inodirty *new;
 
-       /* Mark the superblock dirty so that kupdated will flush... */
+       /* Schedule delayed write-buffer write-out */
        jffs2_dirty_trigger(c);
 
        if (jffs2_wbuf_pending_for_ino(c, ino))
@@ -1148,6 +1149,47 @@ int jffs2_write_nand_badblock(struct jffs2_sb_info *c, struct jffs2_eraseblock *
        return 1;
 }
 
+static struct jffs2_sb_info *work_to_sb(struct work_struct *work)
+{
+       struct delayed_work *dwork;
+
+       dwork = container_of(work, struct delayed_work, work);
+       return container_of(dwork, struct jffs2_sb_info, wbuf_dwork);
+}
+
+static void delayed_wbuf_sync(struct work_struct *work)
+{
+       struct jffs2_sb_info *c = work_to_sb(work);
+       struct super_block *sb = OFNI_BS_2SFFJ(c);
+
+       spin_lock(&c->wbuf_dwork_lock);
+       c->wbuf_queued = 0;
+       spin_unlock(&c->wbuf_dwork_lock);
+
+       if (!(sb->s_flags & MS_RDONLY)) {
+               jffs2_dbg(1, "%s()\n", __func__);
+               jffs2_flush_wbuf_gc(c, 0);
+       }
+}
+
+void jffs2_dirty_trigger(struct jffs2_sb_info *c)
+{
+       struct super_block *sb = OFNI_BS_2SFFJ(c);
+       unsigned long delay;
+
+       if (sb->s_flags & MS_RDONLY)
+               return;
+
+       spin_lock(&c->wbuf_dwork_lock);
+       if (!c->wbuf_queued) {
+               jffs2_dbg(1, "%s()\n", __func__);
+               delay = msecs_to_jiffies(dirty_writeback_interval * 10);
+               queue_delayed_work(system_long_wq, &c->wbuf_dwork, delay);
+               c->wbuf_queued = 1;
+       }
+       spin_unlock(&c->wbuf_dwork_lock);
+}
+
 int jffs2_nand_flash_setup(struct jffs2_sb_info *c)
 {
        struct nand_ecclayout *oinfo = c->mtd->ecclayout;
@@ -1169,6 +1211,8 @@ int jffs2_nand_flash_setup(struct jffs2_sb_info *c)
 
        /* Initialise write buffer */
        init_rwsem(&c->wbuf_sem);
+       spin_lock_init(&c->wbuf_dwork_lock);
+       INIT_DELAYED_WORK(&c->wbuf_dwork, delayed_wbuf_sync);
        c->wbuf_pagesize = c->mtd->writesize;
        c->wbuf_ofs = 0xFFFFFFFF;
 
@@ -1207,8 +1251,8 @@ int jffs2_dataflash_setup(struct jffs2_sb_info *c) {
 
        /* Initialize write buffer */
        init_rwsem(&c->wbuf_sem);
-
-
+       spin_lock_init(&c->wbuf_dwork_lock);
+       INIT_DELAYED_WORK(&c->wbuf_dwork, delayed_wbuf_sync);
        c->wbuf_pagesize =  c->mtd->erasesize;
 
        /* Find a suitable c->sector_size
@@ -1267,6 +1311,9 @@ int jffs2_nor_wbuf_flash_setup(struct jffs2_sb_info *c) {
 
        /* Initialize write buffer */
        init_rwsem(&c->wbuf_sem);
+       spin_lock_init(&c->wbuf_dwork_lock);
+       INIT_DELAYED_WORK(&c->wbuf_dwork, delayed_wbuf_sync);
+
        c->wbuf_pagesize = c->mtd->writesize;
        c->wbuf_ofs = 0xFFFFFFFF;
 
@@ -1299,6 +1346,8 @@ int jffs2_ubivol_setup(struct jffs2_sb_info *c) {
                return 0;
 
        init_rwsem(&c->wbuf_sem);
+       spin_lock_init(&c->wbuf_dwork_lock);
+       INIT_DELAYED_WORK(&c->wbuf_dwork, delayed_wbuf_sync);
 
        c->wbuf_pagesize =  c->mtd->writesize;
        c->wbuf_ofs = 0xFFFFFFFF;
index ba1dc2eebd1ef8413d0593abfde9e14229169ab3..ca0a08001449a999e7070253ba51f7607291285e 100644 (file)
@@ -56,7 +56,7 @@ struct nlm_host *nlmclnt_init(const struct nlmclnt_initdata *nlm_init)
        u32 nlm_version = (nlm_init->nfs_version == 2) ? 1 : 4;
        int status;
 
-       status = lockd_up();
+       status = lockd_up(nlm_init->net);
        if (status < 0)
                return ERR_PTR(status);
 
@@ -65,7 +65,7 @@ struct nlm_host *nlmclnt_init(const struct nlmclnt_initdata *nlm_init)
                                   nlm_init->hostname, nlm_init->noresvport,
                                   nlm_init->net);
        if (host == NULL) {
-               lockd_down();
+               lockd_down(nlm_init->net);
                return ERR_PTR(-ENOLCK);
        }
 
@@ -80,8 +80,10 @@ EXPORT_SYMBOL_GPL(nlmclnt_init);
  */
 void nlmclnt_done(struct nlm_host *host)
 {
+       struct net *net = host->net;
+
        nlmclnt_release_host(host);
-       lockd_down();
+       lockd_down(net);
 }
 EXPORT_SYMBOL_GPL(nlmclnt_done);
 
@@ -220,11 +222,12 @@ reclaimer(void *ptr)
        struct nlm_wait   *block;
        struct file_lock *fl, *next;
        u32 nsmstate;
+       struct net *net = host->net;
 
        allow_signal(SIGKILL);
 
        down_write(&host->h_rwsem);
-       lockd_up();     /* note: this cannot fail as lockd is already running */
+       lockd_up(net);  /* note: this cannot fail as lockd is already running */
 
        dprintk("lockd: reclaiming locks for host %s\n", host->h_name);
 
@@ -275,6 +278,6 @@ restart:
 
        /* Release host handle after use */
        nlmclnt_release_host(host);
-       lockd_down();
+       lockd_down(net);
        return 0;
 }
index f49b9afc443690a2377db100ed7da33452ef98db..80938fda67e0e6fde67999d3556820b87b6acd33 100644 (file)
@@ -251,39 +251,40 @@ out_err:
        return err;
 }
 
-static int lockd_up_net(struct net *net)
+static int lockd_up_net(struct svc_serv *serv, struct net *net)
 {
        struct lockd_net *ln = net_generic(net, lockd_net_id);
-       struct svc_serv *serv = nlmsvc_rqst->rq_server;
        int error;
 
-       if (ln->nlmsvc_users)
+       if (ln->nlmsvc_users++)
                return 0;
 
-       error = svc_rpcb_setup(serv, net);
+       error = svc_bind(serv, net);
        if (error)
-               goto err_rpcb;
+               goto err_bind;
 
        error = make_socks(serv, net);
        if (error < 0)
                goto err_socks;
+       dprintk("lockd_up_net: per-net data created; net=%p\n", net);
        return 0;
 
 err_socks:
        svc_rpcb_cleanup(serv, net);
-err_rpcb:
+err_bind:
+       ln->nlmsvc_users--;
        return error;
 }
 
-static void lockd_down_net(struct net *net)
+static void lockd_down_net(struct svc_serv *serv, struct net *net)
 {
        struct lockd_net *ln = net_generic(net, lockd_net_id);
-       struct svc_serv *serv = nlmsvc_rqst->rq_server;
 
        if (ln->nlmsvc_users) {
                if (--ln->nlmsvc_users == 0) {
                        nlm_shutdown_hosts_net(net);
                        svc_shutdown_net(serv, net);
+                       dprintk("lockd_down_net: per-net data destroyed; net=%p\n", net);
                }
        } else {
                printk(KERN_ERR "lockd_down_net: no users! task=%p, net=%p\n",
@@ -292,22 +293,60 @@ static void lockd_down_net(struct net *net)
        }
 }
 
-/*
- * Bring up the lockd process if it's not already up.
- */
-int lockd_up(void)
+static int lockd_start_svc(struct svc_serv *serv)
+{
+       int error;
+
+       if (nlmsvc_rqst)
+               return 0;
+
+       /*
+        * Create the kernel thread and wait for it to start.
+        */
+       nlmsvc_rqst = svc_prepare_thread(serv, &serv->sv_pools[0], NUMA_NO_NODE);
+       if (IS_ERR(nlmsvc_rqst)) {
+               error = PTR_ERR(nlmsvc_rqst);
+               printk(KERN_WARNING
+                       "lockd_up: svc_rqst allocation failed, error=%d\n",
+                       error);
+               goto out_rqst;
+       }
+
+       svc_sock_update_bufs(serv);
+       serv->sv_maxconn = nlm_max_connections;
+
+       nlmsvc_task = kthread_run(lockd, nlmsvc_rqst, serv->sv_name);
+       if (IS_ERR(nlmsvc_task)) {
+               error = PTR_ERR(nlmsvc_task);
+               printk(KERN_WARNING
+                       "lockd_up: kthread_run failed, error=%d\n", error);
+               goto out_task;
+       }
+       dprintk("lockd_up: service started\n");
+       return 0;
+
+out_task:
+       svc_exit_thread(nlmsvc_rqst);
+       nlmsvc_task = NULL;
+out_rqst:
+       nlmsvc_rqst = NULL;
+       return error;
+}
+
+static struct svc_serv *lockd_create_svc(void)
 {
        struct svc_serv *serv;
-       int             error = 0;
-       struct net *net = current->nsproxy->net_ns;
 
-       mutex_lock(&nlmsvc_mutex);
        /*
         * Check whether we're already up and running.
         */
        if (nlmsvc_rqst) {
-               error = lockd_up_net(net);
-               goto out;
+               /*
+                * Note: increase service usage, because later in case of error
+                * svc_destroy() will be called.
+                */
+               svc_get(nlmsvc_rqst->rq_server);
+               return nlmsvc_rqst->rq_server;
        }
 
        /*
@@ -318,59 +357,53 @@ int lockd_up(void)
                printk(KERN_WARNING
                        "lockd_up: no pid, %d users??\n", nlmsvc_users);
 
-       error = -ENOMEM;
        serv = svc_create(&nlmsvc_program, LOCKD_BUFSIZE, NULL);
        if (!serv) {
                printk(KERN_WARNING "lockd_up: create service failed\n");
-               goto out;
+               return ERR_PTR(-ENOMEM);
        }
+       dprintk("lockd_up: service created\n");
+       return serv;
+}
 
-       error = make_socks(serv, net);
-       if (error < 0)
-               goto destroy_and_out;
+/*
+ * Bring up the lockd process if it's not already up.
+ */
+int lockd_up(struct net *net)
+{
+       struct svc_serv *serv;
+       int error;
 
-       /*
-        * Create the kernel thread and wait for it to start.
-        */
-       nlmsvc_rqst = svc_prepare_thread(serv, &serv->sv_pools[0], NUMA_NO_NODE);
-       if (IS_ERR(nlmsvc_rqst)) {
-               error = PTR_ERR(nlmsvc_rqst);
-               nlmsvc_rqst = NULL;
-               printk(KERN_WARNING
-                       "lockd_up: svc_rqst allocation failed, error=%d\n",
-                       error);
-               goto destroy_and_out;
+       mutex_lock(&nlmsvc_mutex);
+
+       serv = lockd_create_svc();
+       if (IS_ERR(serv)) {
+               error = PTR_ERR(serv);
+               goto err_create;
        }
 
-       svc_sock_update_bufs(serv);
-       serv->sv_maxconn = nlm_max_connections;
+       error = lockd_up_net(serv, net);
+       if (error < 0)
+               goto err_net;
 
-       nlmsvc_task = kthread_run(lockd, nlmsvc_rqst, serv->sv_name);
-       if (IS_ERR(nlmsvc_task)) {
-               error = PTR_ERR(nlmsvc_task);
-               svc_exit_thread(nlmsvc_rqst);
-               nlmsvc_task = NULL;
-               nlmsvc_rqst = NULL;
-               printk(KERN_WARNING
-                       "lockd_up: kthread_run failed, error=%d\n", error);
-               goto destroy_and_out;
-       }
+       error = lockd_start_svc(serv);
+       if (error < 0)
+               goto err_start;
 
+       nlmsvc_users++;
        /*
         * Note: svc_serv structures have an initial use count of 1,
         * so we exit through here on both success and failure.
         */
-destroy_and_out:
+err_net:
        svc_destroy(serv);
-out:
-       if (!error) {
-               struct lockd_net *ln = net_generic(net, lockd_net_id);
-
-               ln->nlmsvc_users++;
-               nlmsvc_users++;
-       }
+err_create:
        mutex_unlock(&nlmsvc_mutex);
        return error;
+
+err_start:
+       lockd_down_net(serv, net);
+       goto err_net;
 }
 EXPORT_SYMBOL_GPL(lockd_up);
 
@@ -378,14 +411,13 @@ EXPORT_SYMBOL_GPL(lockd_up);
  * Decrement the user count and bring down lockd if we're the last.
  */
 void
-lockd_down(void)
+lockd_down(struct net *net)
 {
        mutex_lock(&nlmsvc_mutex);
+       lockd_down_net(nlmsvc_rqst->rq_server, net);
        if (nlmsvc_users) {
-               if (--nlmsvc_users) {
-                       lockd_down_net(current->nsproxy->net_ns);
+               if (--nlmsvc_users)
                        goto out;
-               }
        } else {
                printk(KERN_ERR "lockd_down: no users! task=%p\n",
                        nlmsvc_task);
@@ -397,7 +429,9 @@ lockd_down(void)
                BUG();
        }
        kthread_stop(nlmsvc_task);
+       dprintk("lockd_down: service stopped\n");
        svc_exit_thread(nlmsvc_rqst);
+       dprintk("lockd_down: service destroyed\n");
        nlmsvc_task = NULL;
        nlmsvc_rqst = NULL;
 out:
index 4f441e46cef47bc67b08a3e82b78f389dfbbf818..814c51d0de4739e4b89e9091e00c17f284c0e2ba 100644 (file)
@@ -1636,12 +1636,13 @@ EXPORT_SYMBOL(flock_lock_file_wait);
 SYSCALL_DEFINE2(flock, unsigned int, fd, unsigned int, cmd)
 {
        struct file *filp;
+       int fput_needed;
        struct file_lock *lock;
        int can_sleep, unlock;
        int error;
 
        error = -EBADF;
-       filp = fget(fd);
+       filp = fget_light(fd, &fput_needed);
        if (!filp)
                goto out;
 
@@ -1674,7 +1675,7 @@ SYSCALL_DEFINE2(flock, unsigned int, fd, unsigned int, cmd)
        locks_free_lock(lock);
 
  out_putf:
-       fput(filp);
+       fput_light(filp, fput_needed);
  out:
        return error;
 }
index c651f02c9fecb930c97a2668adc075090c04b7c9..7d694194024ac4d2459e7cc3d60014bdff64e3ba 100644 (file)
@@ -449,7 +449,7 @@ static int unlazy_walk(struct nameidata *nd, struct dentry *dentry)
        mntget(nd->path.mnt);
 
        rcu_read_unlock();
-       br_read_unlock(vfsmount_lock);
+       br_read_unlock(&vfsmount_lock);
        nd->flags &= ~LOOKUP_RCU;
        return 0;
 
@@ -507,14 +507,14 @@ static int complete_walk(struct nameidata *nd)
                if (unlikely(!__d_rcu_to_refcount(dentry, nd->seq))) {
                        spin_unlock(&dentry->d_lock);
                        rcu_read_unlock();
-                       br_read_unlock(vfsmount_lock);
+                       br_read_unlock(&vfsmount_lock);
                        return -ECHILD;
                }
                BUG_ON(nd->inode != dentry->d_inode);
                spin_unlock(&dentry->d_lock);
                mntget(nd->path.mnt);
                rcu_read_unlock();
-               br_read_unlock(vfsmount_lock);
+               br_read_unlock(&vfsmount_lock);
        }
 
        if (likely(!(nd->flags & LOOKUP_JUMPED)))
@@ -681,15 +681,15 @@ int follow_up(struct path *path)
        struct mount *parent;
        struct dentry *mountpoint;
 
-       br_read_lock(vfsmount_lock);
+       br_read_lock(&vfsmount_lock);
        parent = mnt->mnt_parent;
        if (&parent->mnt == path->mnt) {
-               br_read_unlock(vfsmount_lock);
+               br_read_unlock(&vfsmount_lock);
                return 0;
        }
        mntget(&parent->mnt);
        mountpoint = dget(mnt->mnt_mountpoint);
-       br_read_unlock(vfsmount_lock);
+       br_read_unlock(&vfsmount_lock);
        dput(path->dentry);
        path->dentry = mountpoint;
        mntput(path->mnt);
@@ -947,7 +947,7 @@ failed:
        if (!(nd->flags & LOOKUP_ROOT))
                nd->root.mnt = NULL;
        rcu_read_unlock();
-       br_read_unlock(vfsmount_lock);
+       br_read_unlock(&vfsmount_lock);
        return -ECHILD;
 }
 
@@ -1125,8 +1125,8 @@ static struct dentry *__lookup_hash(struct qstr *name,
  *  small and for now I'd prefer to have fast path as straight as possible.
  *  It _is_ time-critical.
  */
-static int do_lookup(struct nameidata *nd, struct qstr *name,
-                       struct path *path, struct inode **inode)
+static int lookup_fast(struct nameidata *nd, struct qstr *name,
+                      struct path *path, struct inode **inode)
 {
        struct vfsmount *mnt = nd->path.mnt;
        struct dentry *dentry, *parent = nd->path.dentry;
@@ -1208,7 +1208,7 @@ unlazy:
                        goto need_lookup;
                }
        }
-done:
+
        path->mnt = mnt;
        path->dentry = dentry;
        err = follow_managed(path, nd->flags);
@@ -1222,6 +1222,17 @@ done:
        return 0;
 
 need_lookup:
+       return 1;
+}
+
+/* Fast lookup failed, do it the slow way */
+static int lookup_slow(struct nameidata *nd, struct qstr *name,
+                      struct path *path)
+{
+       struct dentry *dentry, *parent;
+       int err;
+
+       parent = nd->path.dentry;
        BUG_ON(nd->inode != parent->d_inode);
 
        mutex_lock(&parent->d_inode->i_mutex);
@@ -1229,7 +1240,16 @@ need_lookup:
        mutex_unlock(&parent->d_inode->i_mutex);
        if (IS_ERR(dentry))
                return PTR_ERR(dentry);
-       goto done;
+       path->mnt = nd->path.mnt;
+       path->dentry = dentry;
+       err = follow_managed(path, nd->flags);
+       if (unlikely(err < 0)) {
+               path_put_conditional(path, nd);
+               return err;
+       }
+       if (err)
+               nd->flags |= LOOKUP_JUMPED;
+       return 0;
 }
 
 static inline int may_lookup(struct nameidata *nd)
@@ -1265,7 +1285,7 @@ static void terminate_walk(struct nameidata *nd)
                if (!(nd->flags & LOOKUP_ROOT))
                        nd->root.mnt = NULL;
                rcu_read_unlock();
-               br_read_unlock(vfsmount_lock);
+               br_read_unlock(&vfsmount_lock);
        }
 }
 
@@ -1301,21 +1321,26 @@ static inline int walk_component(struct nameidata *nd, struct path *path,
         */
        if (unlikely(type != LAST_NORM))
                return handle_dots(nd, type);
-       err = do_lookup(nd, name, path, &inode);
+       err = lookup_fast(nd, name, path, &inode);
        if (unlikely(err)) {
-               terminate_walk(nd);
-               return err;
-       }
-       if (!inode) {
-               path_to_nameidata(path, nd);
-               terminate_walk(nd);
-               return -ENOENT;
+               if (err < 0)
+                       goto out_err;
+
+               err = lookup_slow(nd, name, path);
+               if (err < 0)
+                       goto out_err;
+
+               inode = path->dentry->d_inode;
        }
+       err = -ENOENT;
+       if (!inode)
+               goto out_path_put;
+
        if (should_follow_link(inode, follow)) {
                if (nd->flags & LOOKUP_RCU) {
                        if (unlikely(unlazy_walk(nd, path->dentry))) {
-                               terminate_walk(nd);
-                               return -ECHILD;
+                               err = -ECHILD;
+                               goto out_err;
                        }
                }
                BUG_ON(inode != path->dentry->d_inode);
@@ -1324,6 +1349,12 @@ static inline int walk_component(struct nameidata *nd, struct path *path,
        path_to_nameidata(path, nd);
        nd->inode = inode;
        return 0;
+
+out_path_put:
+       path_to_nameidata(path, nd);
+out_err:
+       terminate_walk(nd);
+       return err;
 }
 
 /*
@@ -1620,7 +1651,7 @@ static int path_init(int dfd, const char *name, unsigned int flags,
                nd->path = nd->root;
                nd->inode = inode;
                if (flags & LOOKUP_RCU) {
-                       br_read_lock(vfsmount_lock);
+                       br_read_lock(&vfsmount_lock);
                        rcu_read_lock();
                        nd->seq = __read_seqcount_begin(&nd->path.dentry->d_seq);
                } else {
@@ -1633,7 +1664,7 @@ static int path_init(int dfd, const char *name, unsigned int flags,
 
        if (*name=='/') {
                if (flags & LOOKUP_RCU) {
-                       br_read_lock(vfsmount_lock);
+                       br_read_lock(&vfsmount_lock);
                        rcu_read_lock();
                        set_root_rcu(nd);
                } else {
@@ -1646,7 +1677,7 @@ static int path_init(int dfd, const char *name, unsigned int flags,
                        struct fs_struct *fs = current->fs;
                        unsigned seq;
 
-                       br_read_lock(vfsmount_lock);
+                       br_read_lock(&vfsmount_lock);
                        rcu_read_lock();
 
                        do {
@@ -1682,7 +1713,7 @@ static int path_init(int dfd, const char *name, unsigned int flags,
                        if (fput_needed)
                                *fp = file;
                        nd->seq = __read_seqcount_begin(&nd->path.dentry->d_seq);
-                       br_read_lock(vfsmount_lock);
+                       br_read_lock(&vfsmount_lock);
                        rcu_read_lock();
                } else {
                        path_get(&file->f_path);
@@ -2169,6 +2200,10 @@ static struct file *do_last(struct nameidata *nd, struct path *path,
        int want_write = 0;
        int acc_mode = op->acc_mode;
        struct file *filp;
+       struct inode *inode;
+       int symlink_ok = 0;
+       struct path save_parent = { .dentry = NULL, .mnt = NULL };
+       bool retried = false;
        int error;
 
        nd->flags &= ~LOOKUP_PARENT;
@@ -2200,30 +2235,23 @@ static struct file *do_last(struct nameidata *nd, struct path *path,
        }
 
        if (!(open_flag & O_CREAT)) {
-               int symlink_ok = 0;
                if (nd->last.name[nd->last.len])
                        nd->flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY;
                if (open_flag & O_PATH && !(nd->flags & LOOKUP_FOLLOW))
                        symlink_ok = 1;
                /* we _can_ be in RCU mode here */
-               error = walk_component(nd, path, &nd->last, LAST_NORM,
-                                       !symlink_ok);
-               if (error < 0)
-                       return ERR_PTR(error);
-               if (error) /* symlink */
-                       return NULL;
-               /* sayonara */
-               error = complete_walk(nd);
-               if (error)
-                       return ERR_PTR(error);
+               error = lookup_fast(nd, &nd->last, path, &inode);
+               if (unlikely(error)) {
+                       if (error < 0)
+                               goto exit;
 
-               error = -ENOTDIR;
-               if (nd->flags & LOOKUP_DIRECTORY) {
-                       if (!nd->inode->i_op->lookup)
+                       error = lookup_slow(nd, &nd->last, path);
+                       if (error < 0)
                                goto exit;
+
+                       inode = path->dentry->d_inode;
                }
-               audit_inode(pathname, nd->path.dentry);
-               goto ok;
+               goto finish_lookup;
        }
 
        /* create side of things */
@@ -2241,6 +2269,7 @@ static struct file *do_last(struct nameidata *nd, struct path *path,
        if (nd->last.name[nd->last.len])
                goto exit;
 
+retry_lookup:
        mutex_lock(&dir->d_inode->i_mutex);
 
        dentry = lookup_hash(nd);
@@ -2302,22 +2331,49 @@ static struct file *do_last(struct nameidata *nd, struct path *path,
        if (error)
                nd->flags |= LOOKUP_JUMPED;
 
+       BUG_ON(nd->flags & LOOKUP_RCU);
+       inode = path->dentry->d_inode;
+finish_lookup:
+       /* we _can_ be in RCU mode here */
        error = -ENOENT;
-       if (!path->dentry->d_inode)
-               goto exit_dput;
+       if (!inode) {
+               path_to_nameidata(path, nd);
+               goto exit;
+       }
 
-       if (path->dentry->d_inode->i_op->follow_link)
+       if (should_follow_link(inode, !symlink_ok)) {
+               if (nd->flags & LOOKUP_RCU) {
+                       if (unlikely(unlazy_walk(nd, path->dentry))) {
+                               error = -ECHILD;
+                               goto exit;
+                       }
+               }
+               BUG_ON(inode != path->dentry->d_inode);
                return NULL;
+       }
 
-       path_to_nameidata(path, nd);
-       nd->inode = path->dentry->d_inode;
+       if ((nd->flags & LOOKUP_RCU) || nd->path.mnt != path->mnt) {
+               path_to_nameidata(path, nd);
+       } else {
+               save_parent.dentry = nd->path.dentry;
+               save_parent.mnt = mntget(path->mnt);
+               nd->path.dentry = path->dentry;
+
+       }
+       nd->inode = inode;
        /* Why this, you ask?  _Now_ we might have grown LOOKUP_JUMPED... */
        error = complete_walk(nd);
-       if (error)
+       if (error) {
+               path_put(&save_parent);
                return ERR_PTR(error);
+       }
        error = -EISDIR;
-       if (S_ISDIR(nd->inode->i_mode))
+       if ((open_flag & O_CREAT) && S_ISDIR(nd->inode->i_mode))
+               goto exit;
+       error = -ENOTDIR;
+       if ((nd->flags & LOOKUP_DIRECTORY) && !nd->inode->i_op->lookup)
                goto exit;
+       audit_inode(pathname, nd->path.dentry);
 ok:
        if (!S_ISREG(nd->inode->i_mode))
                will_truncate = 0;
@@ -2333,6 +2389,20 @@ common:
        if (error)
                goto exit;
        filp = nameidata_to_filp(nd);
+       if (filp == ERR_PTR(-EOPENSTALE) && save_parent.dentry && !retried) {
+               BUG_ON(save_parent.dentry != dir);
+               path_put(&nd->path);
+               nd->path = save_parent;
+               nd->inode = dir->d_inode;
+               save_parent.mnt = NULL;
+               save_parent.dentry = NULL;
+               if (want_write) {
+                       mnt_drop_write(nd->path.mnt);
+                       want_write = 0;
+               }
+               retried = true;
+               goto retry_lookup;
+       }
        if (!IS_ERR(filp)) {
                error = ima_file_check(filp, op->acc_mode);
                if (error) {
@@ -2352,7 +2422,8 @@ common:
 out:
        if (want_write)
                mnt_drop_write(nd->path.mnt);
-       path_put(&nd->path);
+       path_put(&save_parent);
+       terminate_walk(nd);
        return filp;
 
 exit_mutex_unlock:
@@ -2415,6 +2486,12 @@ out:
        if (base)
                fput(base);
        release_open_intent(nd);
+       if (filp == ERR_PTR(-EOPENSTALE)) {
+               if (flags & LOOKUP_RCU)
+                       filp = ERR_PTR(-ECHILD);
+               else
+                       filp = ERR_PTR(-ESTALE);
+       }
        return filp;
 
 out_filp:
index e6081996c9a2f9d26525740545445630c4737583..1e4a5fe3d7b7f789d66839f37b1f917c1fa3e2ba 100644 (file)
@@ -397,7 +397,7 @@ static int mnt_make_readonly(struct mount *mnt)
 {
        int ret = 0;
 
-       br_write_lock(vfsmount_lock);
+       br_write_lock(&vfsmount_lock);
        mnt->mnt.mnt_flags |= MNT_WRITE_HOLD;
        /*
         * After storing MNT_WRITE_HOLD, we'll read the counters. This store
@@ -431,15 +431,15 @@ static int mnt_make_readonly(struct mount *mnt)
         */
        smp_wmb();
        mnt->mnt.mnt_flags &= ~MNT_WRITE_HOLD;
-       br_write_unlock(vfsmount_lock);
+       br_write_unlock(&vfsmount_lock);
        return ret;
 }
 
 static void __mnt_unmake_readonly(struct mount *mnt)
 {
-       br_write_lock(vfsmount_lock);
+       br_write_lock(&vfsmount_lock);
        mnt->mnt.mnt_flags &= ~MNT_READONLY;
-       br_write_unlock(vfsmount_lock);
+       br_write_unlock(&vfsmount_lock);
 }
 
 int sb_prepare_remount_readonly(struct super_block *sb)
@@ -451,7 +451,7 @@ int sb_prepare_remount_readonly(struct super_block *sb)
        if (atomic_long_read(&sb->s_remove_count))
                return -EBUSY;
 
-       br_write_lock(vfsmount_lock);
+       br_write_lock(&vfsmount_lock);
        list_for_each_entry(mnt, &sb->s_mounts, mnt_instance) {
                if (!(mnt->mnt.mnt_flags & MNT_READONLY)) {
                        mnt->mnt.mnt_flags |= MNT_WRITE_HOLD;
@@ -473,7 +473,7 @@ int sb_prepare_remount_readonly(struct super_block *sb)
                if (mnt->mnt.mnt_flags & MNT_WRITE_HOLD)
                        mnt->mnt.mnt_flags &= ~MNT_WRITE_HOLD;
        }
-       br_write_unlock(vfsmount_lock);
+       br_write_unlock(&vfsmount_lock);
 
        return err;
 }
@@ -522,14 +522,14 @@ struct vfsmount *lookup_mnt(struct path *path)
 {
        struct mount *child_mnt;
 
-       br_read_lock(vfsmount_lock);
+       br_read_lock(&vfsmount_lock);
        child_mnt = __lookup_mnt(path->mnt, path->dentry, 1);
        if (child_mnt) {
                mnt_add_count(child_mnt, 1);
-               br_read_unlock(vfsmount_lock);
+               br_read_unlock(&vfsmount_lock);
                return &child_mnt->mnt;
        } else {
-               br_read_unlock(vfsmount_lock);
+               br_read_unlock(&vfsmount_lock);
                return NULL;
        }
 }
@@ -714,9 +714,9 @@ vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void
        mnt->mnt.mnt_sb = root->d_sb;
        mnt->mnt_mountpoint = mnt->mnt.mnt_root;
        mnt->mnt_parent = mnt;
-       br_write_lock(vfsmount_lock);
+       br_write_lock(&vfsmount_lock);
        list_add_tail(&mnt->mnt_instance, &root->d_sb->s_mounts);
-       br_write_unlock(vfsmount_lock);
+       br_write_unlock(&vfsmount_lock);
        return &mnt->mnt;
 }
 EXPORT_SYMBOL_GPL(vfs_kern_mount);
@@ -745,9 +745,9 @@ static struct mount *clone_mnt(struct mount *old, struct dentry *root,
                mnt->mnt.mnt_root = dget(root);
                mnt->mnt_mountpoint = mnt->mnt.mnt_root;
                mnt->mnt_parent = mnt;
-               br_write_lock(vfsmount_lock);
+               br_write_lock(&vfsmount_lock);
                list_add_tail(&mnt->mnt_instance, &sb->s_mounts);
-               br_write_unlock(vfsmount_lock);
+               br_write_unlock(&vfsmount_lock);
 
                if (flag & CL_SLAVE) {
                        list_add(&mnt->mnt_slave, &old->mnt_slave_list);
@@ -803,35 +803,36 @@ static void mntput_no_expire(struct mount *mnt)
 {
 put_again:
 #ifdef CONFIG_SMP
-       br_read_lock(vfsmount_lock);
+       br_read_lock(&vfsmount_lock);
        if (likely(atomic_read(&mnt->mnt_longterm))) {
                mnt_add_count(mnt, -1);
-               br_read_unlock(vfsmount_lock);
+               br_read_unlock(&vfsmount_lock);
                return;
        }
-       br_read_unlock(vfsmount_lock);
+       br_read_unlock(&vfsmount_lock);
 
-       br_write_lock(vfsmount_lock);
+       br_write_lock(&vfsmount_lock);
        mnt_add_count(mnt, -1);
        if (mnt_get_count(mnt)) {
-               br_write_unlock(vfsmount_lock);
+               br_write_unlock(&vfsmount_lock);
                return;
        }
 #else
        mnt_add_count(mnt, -1);
        if (likely(mnt_get_count(mnt)))
                return;
-       br_write_lock(vfsmount_lock);
+       br_write_lock(&vfsmount_lock);
 #endif
        if (unlikely(mnt->mnt_pinned)) {
                mnt_add_count(mnt, mnt->mnt_pinned + 1);
                mnt->mnt_pinned = 0;
-               br_write_unlock(vfsmount_lock);
+               br_write_unlock(&vfsmount_lock);
                acct_auto_close_mnt(&mnt->mnt);
                goto put_again;
        }
+
        list_del(&mnt->mnt_instance);
-       br_write_unlock(vfsmount_lock);
+       br_write_unlock(&vfsmount_lock);
        mntfree(mnt);
 }
 
@@ -857,21 +858,21 @@ EXPORT_SYMBOL(mntget);
 
 void mnt_pin(struct vfsmount *mnt)
 {
-       br_write_lock(vfsmount_lock);
+       br_write_lock(&vfsmount_lock);
        real_mount(mnt)->mnt_pinned++;
-       br_write_unlock(vfsmount_lock);
+       br_write_unlock(&vfsmount_lock);
 }
 EXPORT_SYMBOL(mnt_pin);
 
 void mnt_unpin(struct vfsmount *m)
 {
        struct mount *mnt = real_mount(m);
-       br_write_lock(vfsmount_lock);
+       br_write_lock(&vfsmount_lock);
        if (mnt->mnt_pinned) {
                mnt_add_count(mnt, 1);
                mnt->mnt_pinned--;
        }
-       br_write_unlock(vfsmount_lock);
+       br_write_unlock(&vfsmount_lock);
 }
 EXPORT_SYMBOL(mnt_unpin);
 
@@ -988,12 +989,12 @@ int may_umount_tree(struct vfsmount *m)
        BUG_ON(!m);
 
        /* write lock needed for mnt_get_count */
-       br_write_lock(vfsmount_lock);
+       br_write_lock(&vfsmount_lock);
        for (p = mnt; p; p = next_mnt(p, mnt)) {
                actual_refs += mnt_get_count(p);
                minimum_refs += 2;
        }
-       br_write_unlock(vfsmount_lock);
+       br_write_unlock(&vfsmount_lock);
 
        if (actual_refs > minimum_refs)
                return 0;
@@ -1020,10 +1021,10 @@ int may_umount(struct vfsmount *mnt)
 {
        int ret = 1;
        down_read(&namespace_sem);
-       br_write_lock(vfsmount_lock);
+       br_write_lock(&vfsmount_lock);
        if (propagate_mount_busy(real_mount(mnt), 2))
                ret = 0;
-       br_write_unlock(vfsmount_lock);
+       br_write_unlock(&vfsmount_lock);
        up_read(&namespace_sem);
        return ret;
 }
@@ -1040,13 +1041,13 @@ void release_mounts(struct list_head *head)
                        struct dentry *dentry;
                        struct mount *m;
 
-                       br_write_lock(vfsmount_lock);
+                       br_write_lock(&vfsmount_lock);
                        dentry = mnt->mnt_mountpoint;
                        m = mnt->mnt_parent;
                        mnt->mnt_mountpoint = mnt->mnt.mnt_root;
                        mnt->mnt_parent = mnt;
                        m->mnt_ghosts--;
-                       br_write_unlock(vfsmount_lock);
+                       br_write_unlock(&vfsmount_lock);
                        dput(dentry);
                        mntput(&m->mnt);
                }
@@ -1073,8 +1074,9 @@ void umount_tree(struct mount *mnt, int propagate, struct list_head *kill)
                list_del_init(&p->mnt_expire);
                list_del_init(&p->mnt_list);
                __touch_mnt_namespace(p->mnt_ns);
+               if (p->mnt_ns)
+                       __mnt_make_shortterm(p);
                p->mnt_ns = NULL;
-               __mnt_make_shortterm(p);
                list_del_init(&p->mnt_child);
                if (mnt_has_parent(p)) {
                        p->mnt_parent->mnt_ghosts++;
@@ -1112,12 +1114,12 @@ static int do_umount(struct mount *mnt, int flags)
                 * probably don't strictly need the lock here if we examined
                 * all race cases, but it's a slowpath.
                 */
-               br_write_lock(vfsmount_lock);
+               br_write_lock(&vfsmount_lock);
                if (mnt_get_count(mnt) != 2) {
-                       br_write_unlock(vfsmount_lock);
+                       br_write_unlock(&vfsmount_lock);
                        return -EBUSY;
                }
-               br_write_unlock(vfsmount_lock);
+               br_write_unlock(&vfsmount_lock);
 
                if (!xchg(&mnt->mnt_expiry_mark, 1))
                        return -EAGAIN;
@@ -1159,7 +1161,7 @@ static int do_umount(struct mount *mnt, int flags)
        }
 
        down_write(&namespace_sem);
-       br_write_lock(vfsmount_lock);
+       br_write_lock(&vfsmount_lock);
        event++;
 
        if (!(flags & MNT_DETACH))
@@ -1171,7 +1173,7 @@ static int do_umount(struct mount *mnt, int flags)
                        umount_tree(mnt, 1, &umount_list);
                retval = 0;
        }
-       br_write_unlock(vfsmount_lock);
+       br_write_unlock(&vfsmount_lock);
        up_write(&namespace_sem);
        release_mounts(&umount_list);
        return retval;
@@ -1286,19 +1288,19 @@ struct mount *copy_tree(struct mount *mnt, struct dentry *dentry,
                        q = clone_mnt(p, p->mnt.mnt_root, flag);
                        if (!q)
                                goto Enomem;
-                       br_write_lock(vfsmount_lock);
+                       br_write_lock(&vfsmount_lock);
                        list_add_tail(&q->mnt_list, &res->mnt_list);
                        attach_mnt(q, &path);
-                       br_write_unlock(vfsmount_lock);
+                       br_write_unlock(&vfsmount_lock);
                }
        }
        return res;
 Enomem:
        if (res) {
                LIST_HEAD(umount_list);
-               br_write_lock(vfsmount_lock);
+               br_write_lock(&vfsmount_lock);
                umount_tree(res, 0, &umount_list);
-               br_write_unlock(vfsmount_lock);
+               br_write_unlock(&vfsmount_lock);
                release_mounts(&umount_list);
        }
        return NULL;
@@ -1318,9 +1320,9 @@ void drop_collected_mounts(struct vfsmount *mnt)
 {
        LIST_HEAD(umount_list);
        down_write(&namespace_sem);
-       br_write_lock(vfsmount_lock);
+       br_write_lock(&vfsmount_lock);
        umount_tree(real_mount(mnt), 0, &umount_list);
-       br_write_unlock(vfsmount_lock);
+       br_write_unlock(&vfsmount_lock);
        up_write(&namespace_sem);
        release_mounts(&umount_list);
 }
@@ -1448,7 +1450,7 @@ static int attach_recursive_mnt(struct mount *source_mnt,
        if (err)
                goto out_cleanup_ids;
 
-       br_write_lock(vfsmount_lock);
+       br_write_lock(&vfsmount_lock);
 
        if (IS_MNT_SHARED(dest_mnt)) {
                for (p = source_mnt; p; p = next_mnt(p, source_mnt))
@@ -1467,7 +1469,7 @@ static int attach_recursive_mnt(struct mount *source_mnt,
                list_del_init(&child->mnt_hash);
                commit_tree(child);
        }
-       br_write_unlock(vfsmount_lock);
+       br_write_unlock(&vfsmount_lock);
 
        return 0;
 
@@ -1565,10 +1567,10 @@ static int do_change_type(struct path *path, int flag)
                        goto out_unlock;
        }
 
-       br_write_lock(vfsmount_lock);
+       br_write_lock(&vfsmount_lock);
        for (m = mnt; m; m = (recurse ? next_mnt(m, mnt) : NULL))
                change_mnt_propagation(m, type);
-       br_write_unlock(vfsmount_lock);
+       br_write_unlock(&vfsmount_lock);
 
  out_unlock:
        up_write(&namespace_sem);
@@ -1617,9 +1619,9 @@ static int do_loopback(struct path *path, char *old_name,
 
        err = graft_tree(mnt, path);
        if (err) {
-               br_write_lock(vfsmount_lock);
+               br_write_lock(&vfsmount_lock);
                umount_tree(mnt, 0, &umount_list);
-               br_write_unlock(vfsmount_lock);
+               br_write_unlock(&vfsmount_lock);
        }
 out2:
        unlock_mount(path);
@@ -1677,16 +1679,16 @@ static int do_remount(struct path *path, int flags, int mnt_flags,
        else
                err = do_remount_sb(sb, flags, data, 0);
        if (!err) {
-               br_write_lock(vfsmount_lock);
+               br_write_lock(&vfsmount_lock);
                mnt_flags |= mnt->mnt.mnt_flags & MNT_PROPAGATION_MASK;
                mnt->mnt.mnt_flags = mnt_flags;
-               br_write_unlock(vfsmount_lock);
+               br_write_unlock(&vfsmount_lock);
        }
        up_write(&sb->s_umount);
        if (!err) {
-               br_write_lock(vfsmount_lock);
+               br_write_lock(&vfsmount_lock);
                touch_mnt_namespace(mnt->mnt_ns);
-               br_write_unlock(vfsmount_lock);
+               br_write_unlock(&vfsmount_lock);
        }
        return err;
 }
@@ -1893,9 +1895,9 @@ fail:
        /* remove m from any expiration list it may be on */
        if (!list_empty(&mnt->mnt_expire)) {
                down_write(&namespace_sem);
-               br_write_lock(vfsmount_lock);
+               br_write_lock(&vfsmount_lock);
                list_del_init(&mnt->mnt_expire);
-               br_write_unlock(vfsmount_lock);
+               br_write_unlock(&vfsmount_lock);
                up_write(&namespace_sem);
        }
        mntput(m);
@@ -1911,11 +1913,11 @@ fail:
 void mnt_set_expiry(struct vfsmount *mnt, struct list_head *expiry_list)
 {
        down_write(&namespace_sem);
-       br_write_lock(vfsmount_lock);
+       br_write_lock(&vfsmount_lock);
 
        list_add_tail(&real_mount(mnt)->mnt_expire, expiry_list);
 
-       br_write_unlock(vfsmount_lock);
+       br_write_unlock(&vfsmount_lock);
        up_write(&namespace_sem);
 }
 EXPORT_SYMBOL(mnt_set_expiry);
@@ -1935,7 +1937,7 @@ void mark_mounts_for_expiry(struct list_head *mounts)
                return;
 
        down_write(&namespace_sem);
-       br_write_lock(vfsmount_lock);
+       br_write_lock(&vfsmount_lock);
 
        /* extract from the expiration list every vfsmount that matches the
         * following criteria:
@@ -1954,7 +1956,7 @@ void mark_mounts_for_expiry(struct list_head *mounts)
                touch_mnt_namespace(mnt->mnt_ns);
                umount_tree(mnt, 1, &umounts);
        }
-       br_write_unlock(vfsmount_lock);
+       br_write_unlock(&vfsmount_lock);
        up_write(&namespace_sem);
 
        release_mounts(&umounts);
@@ -2218,9 +2220,9 @@ void mnt_make_shortterm(struct vfsmount *m)
        struct mount *mnt = real_mount(m);
        if (atomic_add_unless(&mnt->mnt_longterm, -1, 1))
                return;
-       br_write_lock(vfsmount_lock);
+       br_write_lock(&vfsmount_lock);
        atomic_dec(&mnt->mnt_longterm);
-       br_write_unlock(vfsmount_lock);
+       br_write_unlock(&vfsmount_lock);
 #endif
 }
 
@@ -2250,9 +2252,9 @@ static struct mnt_namespace *dup_mnt_ns(struct mnt_namespace *mnt_ns,
                return ERR_PTR(-ENOMEM);
        }
        new_ns->root = new;
-       br_write_lock(vfsmount_lock);
+       br_write_lock(&vfsmount_lock);
        list_add_tail(&new_ns->list, &new->mnt_list);
-       br_write_unlock(vfsmount_lock);
+       br_write_unlock(&vfsmount_lock);
 
        /*
         * Second pass: switch the tsk->fs->* elements and mark new vfsmounts
@@ -2416,9 +2418,9 @@ bool is_path_reachable(struct mount *mnt, struct dentry *dentry,
 int path_is_under(struct path *path1, struct path *path2)
 {
        int res;
-       br_read_lock(vfsmount_lock);
+       br_read_lock(&vfsmount_lock);
        res = is_path_reachable(real_mount(path1->mnt), path1->dentry, path2);
-       br_read_unlock(vfsmount_lock);
+       br_read_unlock(&vfsmount_lock);
        return res;
 }
 EXPORT_SYMBOL(path_is_under);
@@ -2505,7 +2507,7 @@ SYSCALL_DEFINE2(pivot_root, const char __user *, new_root,
        /* make sure we can reach put_old from new_root */
        if (!is_path_reachable(real_mount(old.mnt), old.dentry, &new))
                goto out4;
-       br_write_lock(vfsmount_lock);
+       br_write_lock(&vfsmount_lock);
        detach_mnt(new_mnt, &parent_path);
        detach_mnt(root_mnt, &root_parent);
        /* mount old root on put_old */
@@ -2513,7 +2515,7 @@ SYSCALL_DEFINE2(pivot_root, const char __user *, new_root,
        /* mount new_root on / */
        attach_mnt(new_mnt, &root_parent);
        touch_mnt_namespace(current->nsproxy->mnt_ns);
-       br_write_unlock(vfsmount_lock);
+       br_write_unlock(&vfsmount_lock);
        chroot_fs_refs(&root, &new);
        error = 0;
 out4:
@@ -2576,7 +2578,7 @@ void __init mnt_init(void)
        for (u = 0; u < HASH_SIZE; u++)
                INIT_LIST_HEAD(&mount_hashtable[u]);
 
-       br_lock_init(vfsmount_lock);
+       br_lock_init(&vfsmount_lock);
 
        err = sysfs_init();
        if (err)
@@ -2596,9 +2598,9 @@ void put_mnt_ns(struct mnt_namespace *ns)
        if (!atomic_dec_and_test(&ns->count))
                return;
        down_write(&namespace_sem);
-       br_write_lock(vfsmount_lock);
+       br_write_lock(&vfsmount_lock);
        umount_tree(ns->root, 0, &umount_list);
-       br_write_unlock(vfsmount_lock);
+       br_write_unlock(&vfsmount_lock);
        up_write(&namespace_sem);
        release_mounts(&umount_list);
        kfree(ns);
index 3ff5fcc1528fd21ae18a7a240ec9f2920ec30d32..122e260247f53c663550073fda567a4342b0ba63 100644 (file)
@@ -221,6 +221,10 @@ ncp_file_write(struct file *file, const char __user *buf, size_t count, loff_t *
 
        already_written = 0;
 
+       errno = file_update_time(file);
+       if (errno)
+               goto outrel;
+
        bouncebuffer = vmalloc(bufsize);
        if (!bouncebuffer) {
                errno = -EIO;   /* -ENOMEM */
@@ -252,8 +256,6 @@ ncp_file_write(struct file *file, const char __user *buf, size_t count, loff_t *
        }
        vfree(bouncebuffer);
 
-       file_update_time(file);
-
        *ppos = pos;
 
        if (pos > i_size_read(inode)) {
index 4af803f13516c98deaf7372af2dda0499e329fe6..54cc0cdb3dcbda111e24a3a67e7953e5173dd07e 100644 (file)
@@ -23,17 +23,17 @@ struct ncp_mount_data_kernel {
        unsigned long    flags;         /* NCP_MOUNT_* flags */
        unsigned int     int_flags;     /* internal flags */
 #define NCP_IMOUNT_LOGGEDIN_POSSIBLE   0x0001
-       __kernel_uid32_t mounted_uid;   /* Who may umount() this filesystem? */
+       uid_t            mounted_uid;   /* Who may umount() this filesystem? */
        struct pid      *wdog_pid;      /* Who cares for our watchdog packets? */
        unsigned int     ncp_fd;        /* The socket to the ncp port */
        unsigned int     time_out;      /* How long should I wait after
                                           sending a NCP request? */
        unsigned int     retry_count;   /* And how often should I retry? */
        unsigned char    mounted_vol[NCP_VOLNAME_LEN + 1];
-       __kernel_uid32_t uid;
-       __kernel_gid32_t gid;
-       __kernel_mode_t  file_mode;
-       __kernel_mode_t  dir_mode;
+       uid_t            uid;
+       gid_t            gid;
+       umode_t          file_mode;
+       umode_t          dir_mode;
        int              info_fd;
 };
 
index 2a0e6c599147aac9e66a5969c9c00593aa0dd380..f90f4f5cd421dc7be9db2151ead1d8d458acf096 100644 (file)
@@ -29,9 +29,20 @@ config NFS_FS
 
          If unsure, say N.
 
+config NFS_V2
+       bool "NFS client support for NFS version 2"
+       depends on NFS_FS
+       default y
+       help
+         This option enables support for version 2 of the NFS protocol
+         (RFC 1094) in the kernel's NFS client.
+
+         If unsure, say Y.
+
 config NFS_V3
        bool "NFS client support for NFS version 3"
        depends on NFS_FS
+       default y
        help
          This option enables support for version 3 of the NFS protocol
          (RFC 1813) in the kernel's NFS client.
index b58613d0abb3f62eaa3bb29fd4b8141ebdb596d6..7ddd45d9f1707d24a7661a402d95f58aef108c03 100644 (file)
@@ -4,11 +4,12 @@
 
 obj-$(CONFIG_NFS_FS) += nfs.o
 
-nfs-y                  := client.o dir.o file.o getroot.o inode.o super.o nfs2xdr.o \
-                          direct.o pagelist.o proc.o read.o symlink.o unlink.o \
+nfs-y                  := client.o dir.o file.o getroot.o inode.o super.o \
+                          direct.o pagelist.o read.o symlink.o unlink.o \
                           write.o namespace.o mount_clnt.o \
                           dns_resolve.o cache_lib.o
 nfs-$(CONFIG_ROOT_NFS) += nfsroot.o
+nfs-$(CONFIG_NFS_V2)   += proc.o nfs2xdr.o
 nfs-$(CONFIG_NFS_V3)   += nfs3proc.o nfs3xdr.o
 nfs-$(CONFIG_NFS_V3_ACL)       += nfs3acl.o
 nfs-$(CONFIG_NFS_V4)   += nfs4proc.o nfs4xdr.o nfs4state.o nfs4renewd.o \
index 7f6a23f0244e7340f2861ac6fb0d3a2d20721287..7ae8a608956f60d7343fdfa9e77be7532b2db17d 100644 (file)
@@ -187,7 +187,6 @@ static void bl_end_io_read(struct bio *bio, int err)
        struct parallel_io *par = bio->bi_private;
        const int uptodate = test_bit(BIO_UPTODATE, &bio->bi_flags);
        struct bio_vec *bvec = bio->bi_io_vec + bio->bi_vcnt - 1;
-       struct nfs_read_data *rdata = (struct nfs_read_data *)par->data;
 
        do {
                struct page *page = bvec->bv_page;
@@ -198,9 +197,12 @@ static void bl_end_io_read(struct bio *bio, int err)
                        SetPageUptodate(page);
        } while (bvec >= bio->bi_io_vec);
        if (!uptodate) {
-               if (!rdata->pnfs_error)
-                       rdata->pnfs_error = -EIO;
-               pnfs_set_lo_fail(rdata->lseg);
+               struct nfs_read_data *rdata = par->data;
+               struct nfs_pgio_header *header = rdata->header;
+
+               if (!header->pnfs_error)
+                       header->pnfs_error = -EIO;
+               pnfs_set_lo_fail(header->lseg);
        }
        bio_put(bio);
        put_parallel(par);
@@ -221,7 +223,7 @@ bl_end_par_io_read(void *data, int unused)
 {
        struct nfs_read_data *rdata = data;
 
-       rdata->task.tk_status = rdata->pnfs_error;
+       rdata->task.tk_status = rdata->header->pnfs_error;
        INIT_WORK(&rdata->task.u.tk_work, bl_read_cleanup);
        schedule_work(&rdata->task.u.tk_work);
 }
@@ -229,6 +231,7 @@ bl_end_par_io_read(void *data, int unused)
 static enum pnfs_try_status
 bl_read_pagelist(struct nfs_read_data *rdata)
 {
+       struct nfs_pgio_header *header = rdata->header;
        int i, hole;
        struct bio *bio = NULL;
        struct pnfs_block_extent *be = NULL, *cow_read = NULL;
@@ -239,7 +242,7 @@ bl_read_pagelist(struct nfs_read_data *rdata)
        int pg_index = rdata->args.pgbase >> PAGE_CACHE_SHIFT;
 
        dprintk("%s enter nr_pages %u offset %lld count %u\n", __func__,
-              rdata->npages, f_offset, (unsigned int)rdata->args.count);
+              rdata->pages.npages, f_offset, (unsigned int)rdata->args.count);
 
        par = alloc_parallel(rdata);
        if (!par)
@@ -249,17 +252,17 @@ bl_read_pagelist(struct nfs_read_data *rdata)
 
        isect = (sector_t) (f_offset >> SECTOR_SHIFT);
        /* Code assumes extents are page-aligned */
-       for (i = pg_index; i < rdata->npages; i++) {
+       for (i = pg_index; i < rdata->pages.npages; i++) {
                if (!extent_length) {
                        /* We've used up the previous extent */
                        bl_put_extent(be);
                        bl_put_extent(cow_read);
                        bio = bl_submit_bio(READ, bio);
                        /* Get the next one */
-                       be = bl_find_get_extent(BLK_LSEG2EXT(rdata->lseg),
+                       be = bl_find_get_extent(BLK_LSEG2EXT(header->lseg),
                                             isect, &cow_read);
                        if (!be) {
-                               rdata->pnfs_error = -EIO;
+                               header->pnfs_error = -EIO;
                                goto out;
                        }
                        extent_length = be->be_length -
@@ -282,11 +285,12 @@ bl_read_pagelist(struct nfs_read_data *rdata)
                        struct pnfs_block_extent *be_read;
 
                        be_read = (hole && cow_read) ? cow_read : be;
-                       bio = bl_add_page_to_bio(bio, rdata->npages - i, READ,
+                       bio = bl_add_page_to_bio(bio, rdata->pages.npages - i,
+                                                READ,
                                                 isect, pages[i], be_read,
                                                 bl_end_io_read, par);
                        if (IS_ERR(bio)) {
-                               rdata->pnfs_error = PTR_ERR(bio);
+                               header->pnfs_error = PTR_ERR(bio);
                                bio = NULL;
                                goto out;
                        }
@@ -294,9 +298,9 @@ bl_read_pagelist(struct nfs_read_data *rdata)
                isect += PAGE_CACHE_SECTORS;
                extent_length -= PAGE_CACHE_SECTORS;
        }
-       if ((isect << SECTOR_SHIFT) >= rdata->inode->i_size) {
+       if ((isect << SECTOR_SHIFT) >= header->inode->i_size) {
                rdata->res.eof = 1;
-               rdata->res.count = rdata->inode->i_size - f_offset;
+               rdata->res.count = header->inode->i_size - f_offset;
        } else {
                rdata->res.count = (isect << SECTOR_SHIFT) - f_offset;
        }
@@ -345,7 +349,6 @@ static void bl_end_io_write_zero(struct bio *bio, int err)
        struct parallel_io *par = bio->bi_private;
        const int uptodate = test_bit(BIO_UPTODATE, &bio->bi_flags);
        struct bio_vec *bvec = bio->bi_io_vec + bio->bi_vcnt - 1;
-       struct nfs_write_data *wdata = (struct nfs_write_data *)par->data;
 
        do {
                struct page *page = bvec->bv_page;
@@ -358,9 +361,12 @@ static void bl_end_io_write_zero(struct bio *bio, int err)
        } while (bvec >= bio->bi_io_vec);
 
        if (unlikely(!uptodate)) {
-               if (!wdata->pnfs_error)
-                       wdata->pnfs_error = -EIO;
-               pnfs_set_lo_fail(wdata->lseg);
+               struct nfs_write_data *data = par->data;
+               struct nfs_pgio_header *header = data->header;
+
+               if (!header->pnfs_error)
+                       header->pnfs_error = -EIO;
+               pnfs_set_lo_fail(header->lseg);
        }
        bio_put(bio);
        put_parallel(par);
@@ -370,12 +376,13 @@ static void bl_end_io_write(struct bio *bio, int err)
 {
        struct parallel_io *par = bio->bi_private;
        const int uptodate = test_bit(BIO_UPTODATE, &bio->bi_flags);
-       struct nfs_write_data *wdata = (struct nfs_write_data *)par->data;
+       struct nfs_write_data *data = par->data;
+       struct nfs_pgio_header *header = data->header;
 
        if (!uptodate) {
-               if (!wdata->pnfs_error)
-                       wdata->pnfs_error = -EIO;
-               pnfs_set_lo_fail(wdata->lseg);
+               if (!header->pnfs_error)
+                       header->pnfs_error = -EIO;
+               pnfs_set_lo_fail(header->lseg);
        }
        bio_put(bio);
        put_parallel(par);
@@ -391,9 +398,9 @@ static void bl_write_cleanup(struct work_struct *work)
        dprintk("%s enter\n", __func__);
        task = container_of(work, struct rpc_task, u.tk_work);
        wdata = container_of(task, struct nfs_write_data, task);
-       if (likely(!wdata->pnfs_error)) {
+       if (likely(!wdata->header->pnfs_error)) {
                /* Marks for LAYOUTCOMMIT */
-               mark_extents_written(BLK_LSEG2EXT(wdata->lseg),
+               mark_extents_written(BLK_LSEG2EXT(wdata->header->lseg),
                                     wdata->args.offset, wdata->args.count);
        }
        pnfs_ld_write_done(wdata);
@@ -404,12 +411,12 @@ static void bl_end_par_io_write(void *data, int num_se)
 {
        struct nfs_write_data *wdata = data;
 
-       if (unlikely(wdata->pnfs_error)) {
-               bl_free_short_extents(&BLK_LSEG2EXT(wdata->lseg)->bl_inval,
+       if (unlikely(wdata->header->pnfs_error)) {
+               bl_free_short_extents(&BLK_LSEG2EXT(wdata->header->lseg)->bl_inval,
                                        num_se);
        }
 
-       wdata->task.tk_status = wdata->pnfs_error;
+       wdata->task.tk_status = wdata->header->pnfs_error;
        wdata->verf.committed = NFS_FILE_SYNC;
        INIT_WORK(&wdata->task.u.tk_work, bl_write_cleanup);
        schedule_work(&wdata->task.u.tk_work);
@@ -540,6 +547,7 @@ check_page:
 static enum pnfs_try_status
 bl_write_pagelist(struct nfs_write_data *wdata, int sync)
 {
+       struct nfs_pgio_header *header = wdata->header;
        int i, ret, npg_zero, pg_index, last = 0;
        struct bio *bio = NULL;
        struct pnfs_block_extent *be = NULL, *cow_read = NULL;
@@ -552,7 +560,7 @@ bl_write_pagelist(struct nfs_write_data *wdata, int sync)
        pgoff_t index;
        u64 temp;
        int npg_per_block =
-           NFS_SERVER(wdata->inode)->pnfs_blksize >> PAGE_CACHE_SHIFT;
+           NFS_SERVER(header->inode)->pnfs_blksize >> PAGE_CACHE_SHIFT;
 
        dprintk("%s enter, %Zu@%lld\n", __func__, count, offset);
        /* At this point, wdata->pages is a (sequential) list of nfs_pages.
@@ -566,7 +574,7 @@ bl_write_pagelist(struct nfs_write_data *wdata, int sync)
        /* At this point, have to be more careful with error handling */
 
        isect = (sector_t) ((offset & (long)PAGE_CACHE_MASK) >> SECTOR_SHIFT);
-       be = bl_find_get_extent(BLK_LSEG2EXT(wdata->lseg), isect, &cow_read);
+       be = bl_find_get_extent(BLK_LSEG2EXT(header->lseg), isect, &cow_read);
        if (!be || !is_writable(be, isect)) {
                dprintk("%s no matching extents!\n", __func__);
                goto out_mds;
@@ -597,10 +605,10 @@ fill_invalid_ext:
                        dprintk("%s zero %dth page: index %lu isect %llu\n",
                                __func__, npg_zero, index,
                                (unsigned long long)isect);
-                       page = bl_find_get_zeroing_page(wdata->inode, index,
+                       page = bl_find_get_zeroing_page(header->inode, index,
                                                        cow_read);
                        if (unlikely(IS_ERR(page))) {
-                               wdata->pnfs_error = PTR_ERR(page);
+                               header->pnfs_error = PTR_ERR(page);
                                goto out;
                        } else if (page == NULL)
                                goto next_page;
@@ -612,7 +620,7 @@ fill_invalid_ext:
                                        __func__, ret);
                                end_page_writeback(page);
                                page_cache_release(page);
-                               wdata->pnfs_error = ret;
+                               header->pnfs_error = ret;
                                goto out;
                        }
                        if (likely(!bl_push_one_short_extent(be->be_inval)))
@@ -620,11 +628,11 @@ fill_invalid_ext:
                        else {
                                end_page_writeback(page);
                                page_cache_release(page);
-                               wdata->pnfs_error = -ENOMEM;
+                               header->pnfs_error = -ENOMEM;
                                goto out;
                        }
                        /* FIXME: This should be done in bi_end_io */
-                       mark_extents_written(BLK_LSEG2EXT(wdata->lseg),
+                       mark_extents_written(BLK_LSEG2EXT(header->lseg),
                                             page->index << PAGE_CACHE_SHIFT,
                                             PAGE_CACHE_SIZE);
 
@@ -632,7 +640,7 @@ fill_invalid_ext:
                                                 isect, page, be,
                                                 bl_end_io_write_zero, par);
                        if (IS_ERR(bio)) {
-                               wdata->pnfs_error = PTR_ERR(bio);
+                               header->pnfs_error = PTR_ERR(bio);
                                bio = NULL;
                                goto out;
                        }
@@ -647,16 +655,16 @@ next_page:
 
        /* Middle pages */
        pg_index = wdata->args.pgbase >> PAGE_CACHE_SHIFT;
-       for (i = pg_index; i < wdata->npages; i++) {
+       for (i = pg_index; i < wdata->pages.npages; i++) {
                if (!extent_length) {
                        /* We've used up the previous extent */
                        bl_put_extent(be);
                        bio = bl_submit_bio(WRITE, bio);
                        /* Get the next one */
-                       be = bl_find_get_extent(BLK_LSEG2EXT(wdata->lseg),
+                       be = bl_find_get_extent(BLK_LSEG2EXT(header->lseg),
                                             isect, NULL);
                        if (!be || !is_writable(be, isect)) {
-                               wdata->pnfs_error = -EINVAL;
+                               header->pnfs_error = -EINVAL;
                                goto out;
                        }
                        if (be->be_state == PNFS_BLOCK_INVALID_DATA) {
@@ -664,7 +672,7 @@ next_page:
                                                                be->be_inval)))
                                        par->bse_count++;
                                else {
-                                       wdata->pnfs_error = -ENOMEM;
+                                       header->pnfs_error = -ENOMEM;
                                        goto out;
                                }
                        }
@@ -677,15 +685,15 @@ next_page:
                        if (unlikely(ret)) {
                                dprintk("%s bl_mark_sectors_init fail %d\n",
                                        __func__, ret);
-                               wdata->pnfs_error = ret;
+                               header->pnfs_error = ret;
                                goto out;
                        }
                }
-               bio = bl_add_page_to_bio(bio, wdata->npages - i, WRITE,
+               bio = bl_add_page_to_bio(bio, wdata->pages.npages - i, WRITE,
                                         isect, pages[i], be,
                                         bl_end_io_write, par);
                if (IS_ERR(bio)) {
-                       wdata->pnfs_error = PTR_ERR(bio);
+                       header->pnfs_error = PTR_ERR(bio);
                        bio = NULL;
                        goto out;
                }
index a5c88a554d921455256bb4dbeea7eaa498da5499..c96554245ccf7d90703c80d9a3e3f81b48fa65ee 100644 (file)
@@ -123,7 +123,7 @@ nfs4_blk_decode_device(struct nfs_server *server,
        uint8_t *dataptr;
        DECLARE_WAITQUEUE(wq, current);
        int offset, len, i, rc;
-       struct net *net = server->nfs_client->net;
+       struct net *net = server->nfs_client->cl_net;
        struct nfs_net *nn = net_generic(net, nfs_net_id);
        struct bl_dev_msg *reply = &nn->bl_mount_reply;
 
index eb95f5091c1aff93930e17a829a808023edc2e12..970659daa323865a113d25075d461c50c7f7dc7c 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/kthread.h>
 #include <linux/sunrpc/svcauth_gss.h>
 #include <linux/sunrpc/bc_xprt.h>
+#include <linux/nsproxy.h>
 
 #include <net/inet_sock.h>
 
@@ -253,6 +254,7 @@ int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt)
        char svc_name[12];
        int ret = 0;
        int minorversion_setup;
+       struct net *net = current->nsproxy->net_ns;
 
        mutex_lock(&nfs_callback_mutex);
        if (cb_info->users++ || cb_info->task != NULL) {
@@ -265,6 +267,12 @@ int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt)
                goto out_err;
        }
 
+       ret = svc_bind(serv, net);
+       if (ret < 0) {
+               printk(KERN_WARNING "NFS: bind callback service failed\n");
+               goto out_err;
+       }
+
        minorversion_setup =  nfs_minorversion_callback_svc_setup(minorversion,
                                        serv, xprt, &rqstp, &callback_svc);
        if (!minorversion_setup) {
@@ -306,6 +314,8 @@ out_err:
        dprintk("NFS: Couldn't create callback socket or server thread; "
                "err = %d\n", ret);
        cb_info->users--;
+       if (serv)
+               svc_shutdown_net(serv, net);
        goto out;
 }
 
@@ -320,6 +330,7 @@ void nfs_callback_down(int minorversion)
        cb_info->users--;
        if (cb_info->users == 0 && cb_info->task != NULL) {
                kthread_stop(cb_info->task);
+               svc_shutdown_net(cb_info->serv, current->nsproxy->net_ns);
                svc_exit_thread(cb_info->rqst);
                cb_info->serv = NULL;
                cb_info->rqst = NULL;
@@ -332,7 +343,7 @@ void nfs_callback_down(int minorversion)
 int
 check_gss_callback_principal(struct nfs_client *clp, struct svc_rqst *rqstp)
 {
-       char *p = svc_gss_principal(rqstp);
+       char *p = rqstp->rq_cred.cr_principal;
 
        if (rqstp->rq_authop->flavour != RPC_AUTH_GSS)
                return 1;
index 60f7e4ec842cf1d4fd48f21862d1d5ece26efc88..7d108753af81e9783ab1465c8bb9986452a6cf00 100644 (file)
@@ -65,7 +65,7 @@ static DECLARE_WAIT_QUEUE_HEAD(nfs_client_active_wq);
 static int nfs_get_cb_ident_idr(struct nfs_client *clp, int minorversion)
 {
        int ret = 0;
-       struct nfs_net *nn = net_generic(clp->net, nfs_net_id);
+       struct nfs_net *nn = net_generic(clp->cl_net, nfs_net_id);
 
        if (clp->rpc_ops->version != 4 || minorversion != 0)
                return ret;
@@ -90,7 +90,9 @@ static bool nfs4_disable_idmapping = true;
  * RPC cruft for NFS
  */
 static const struct rpc_version *nfs_version[5] = {
+#ifdef CONFIG_NFS_V2
        [2]                     = &nfs_version2,
+#endif
 #ifdef CONFIG_NFS_V3
        [3]                     = &nfs_version3,
 #endif
@@ -129,6 +131,7 @@ const struct rpc_program nfsacl_program = {
 #endif  /* CONFIG_NFS_V3_ACL */
 
 struct nfs_client_initdata {
+       unsigned long init_flags;
        const char *hostname;
        const struct sockaddr *addr;
        size_t addrlen;
@@ -172,7 +175,7 @@ static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_
        clp->cl_rpcclient = ERR_PTR(-EINVAL);
 
        clp->cl_proto = cl_init->proto;
-       clp->net = get_net(cl_init->net);
+       clp->cl_net = get_net(cl_init->net);
 
 #ifdef CONFIG_NFS_V4
        err = nfs_get_cb_ident_idr(clp, cl_init->minorversion);
@@ -182,7 +185,6 @@ static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_
        spin_lock_init(&clp->cl_lock);
        INIT_DELAYED_WORK(&clp->cl_renewd, nfs4_renew_state);
        rpc_init_wait_queue(&clp->cl_rpcwaitq, "NFS client");
-       clp->cl_boot_time = CURRENT_TIME;
        clp->cl_state = 1 << NFS4CLNT_LEASE_EXPIRED;
        clp->cl_minorversion = cl_init->minorversion;
        clp->cl_mvops = nfs_v4_minor_ops[cl_init->minorversion];
@@ -207,6 +209,7 @@ static void nfs4_shutdown_session(struct nfs_client *clp)
        if (nfs4_has_session(clp)) {
                nfs4_deviceid_purge_client(clp);
                nfs4_destroy_session(clp->cl_session);
+               nfs4_destroy_clientid(clp);
        }
 
 }
@@ -235,6 +238,9 @@ static void nfs4_shutdown_client(struct nfs_client *clp)
                nfs_idmap_delete(clp);
 
        rpc_destroy_wait_queue(&clp->cl_rpcwaitq);
+       kfree(clp->cl_serverowner);
+       kfree(clp->cl_serverscope);
+       kfree(clp->cl_implid);
 }
 
 /* idr_remove_all is not needed as all id's are removed by nfs_put_client */
@@ -248,7 +254,7 @@ void nfs_cleanup_cb_ident_idr(struct net *net)
 /* nfs_client_lock held */
 static void nfs_cb_idr_remove_locked(struct nfs_client *clp)
 {
-       struct nfs_net *nn = net_generic(clp->net, nfs_net_id);
+       struct nfs_net *nn = net_generic(clp->cl_net, nfs_net_id);
 
        if (clp->cl_cb_ident)
                idr_remove(&nn->cb_ident_idr, clp->cl_cb_ident);
@@ -301,10 +307,8 @@ static void nfs_free_client(struct nfs_client *clp)
        if (clp->cl_machine_cred != NULL)
                put_rpccred(clp->cl_machine_cred);
 
-       put_net(clp->net);
+       put_net(clp->cl_net);
        kfree(clp->cl_hostname);
-       kfree(clp->server_scope);
-       kfree(clp->impl_id);
        kfree(clp);
 
        dprintk("<-- nfs_free_client()\n");
@@ -321,7 +325,7 @@ void nfs_put_client(struct nfs_client *clp)
                return;
 
        dprintk("--> nfs_put_client({%d})\n", atomic_read(&clp->cl_count));
-       nn = net_generic(clp->net, nfs_net_id);
+       nn = net_generic(clp->cl_net, nfs_net_id);
 
        if (atomic_dec_and_lock(&clp->cl_count, &nn->nfs_client_lock)) {
                list_del(&clp->cl_share_link);
@@ -456,6 +460,8 @@ static bool nfs4_cb_match_client(const struct sockaddr *addr,
            clp->cl_cons_state == NFS_CS_SESSION_INITING))
                return false;
 
+       smp_rmb();
+
        /* Match the version and minorversion */
        if (clp->rpc_ops->version != 4 ||
            clp->cl_minorversion != minorversion)
@@ -504,6 +510,47 @@ static struct nfs_client *nfs_match_client(const struct nfs_client_initdata *dat
        return NULL;
 }
 
+static bool nfs_client_init_is_complete(const struct nfs_client *clp)
+{
+       return clp->cl_cons_state != NFS_CS_INITING;
+}
+
+int nfs_wait_client_init_complete(const struct nfs_client *clp)
+{
+       return wait_event_killable(nfs_client_active_wq,
+                       nfs_client_init_is_complete(clp));
+}
+
+/*
+ * Found an existing client.  Make sure it's ready before returning.
+ */
+static struct nfs_client *
+nfs_found_client(const struct nfs_client_initdata *cl_init,
+                struct nfs_client *clp)
+{
+       int error;
+
+       error = nfs_wait_client_init_complete(clp);
+       if (error < 0) {
+               nfs_put_client(clp);
+               return ERR_PTR(-ERESTARTSYS);
+       }
+
+       if (clp->cl_cons_state < NFS_CS_READY) {
+               error = clp->cl_cons_state;
+               nfs_put_client(clp);
+               return ERR_PTR(error);
+       }
+
+       smp_rmb();
+
+       BUG_ON(clp->cl_cons_state != NFS_CS_READY);
+
+       dprintk("<-- %s found nfs_client %p for %s\n",
+               __func__, clp, cl_init->hostname ?: "");
+       return clp;
+}
+
 /*
  * Look up a client by IP address and protocol version
  * - creates a new record if one doesn't yet exist
@@ -512,11 +559,9 @@ static struct nfs_client *
 nfs_get_client(const struct nfs_client_initdata *cl_init,
               const struct rpc_timeout *timeparms,
               const char *ip_addr,
-              rpc_authflavor_t authflavour,
-              int noresvport)
+              rpc_authflavor_t authflavour)
 {
        struct nfs_client *clp, *new = NULL;
-       int error;
        struct nfs_net *nn = net_generic(cl_init->net, nfs_net_id);
 
        dprintk("--> nfs_get_client(%s,v%u)\n",
@@ -527,60 +572,29 @@ nfs_get_client(const struct nfs_client_initdata *cl_init,
                spin_lock(&nn->nfs_client_lock);
 
                clp = nfs_match_client(cl_init);
-               if (clp)
-                       goto found_client;
-               if (new)
-                       goto install_client;
+               if (clp) {
+                       spin_unlock(&nn->nfs_client_lock);
+                       if (new)
+                               nfs_free_client(new);
+                       return nfs_found_client(cl_init, clp);
+               }
+               if (new) {
+                       list_add(&new->cl_share_link, &nn->nfs_client_list);
+                       spin_unlock(&nn->nfs_client_lock);
+                       new->cl_flags = cl_init->init_flags;
+                       return cl_init->rpc_ops->init_client(new,
+                                               timeparms, ip_addr,
+                                               authflavour);
+               }
 
                spin_unlock(&nn->nfs_client_lock);
 
                new = nfs_alloc_client(cl_init);
        } while (!IS_ERR(new));
 
-       dprintk("--> nfs_get_client() = %ld [failed]\n", PTR_ERR(new));
+       dprintk("<-- nfs_get_client() Failed to find %s (%ld)\n",
+               cl_init->hostname ?: "", PTR_ERR(new));
        return new;
-
-       /* install a new client and return with it unready */
-install_client:
-       clp = new;
-       list_add(&clp->cl_share_link, &nn->nfs_client_list);
-       spin_unlock(&nn->nfs_client_lock);
-
-       error = cl_init->rpc_ops->init_client(clp, timeparms, ip_addr,
-                                             authflavour, noresvport);
-       if (error < 0) {
-               nfs_put_client(clp);
-               return ERR_PTR(error);
-       }
-       dprintk("--> nfs_get_client() = %p [new]\n", clp);
-       return clp;
-
-       /* found an existing client
-        * - make sure it's ready before returning
-        */
-found_client:
-       spin_unlock(&nn->nfs_client_lock);
-
-       if (new)
-               nfs_free_client(new);
-
-       error = wait_event_killable(nfs_client_active_wq,
-                               clp->cl_cons_state < NFS_CS_INITING);
-       if (error < 0) {
-               nfs_put_client(clp);
-               return ERR_PTR(-ERESTARTSYS);
-       }
-
-       if (clp->cl_cons_state < NFS_CS_READY) {
-               error = clp->cl_cons_state;
-               nfs_put_client(clp);
-               return ERR_PTR(error);
-       }
-
-       BUG_ON(clp->cl_cons_state != NFS_CS_READY);
-
-       dprintk("--> nfs_get_client() = %p [share]\n", clp);
-       return clp;
 }
 
 /*
@@ -588,26 +602,11 @@ found_client:
  */
 void nfs_mark_client_ready(struct nfs_client *clp, int state)
 {
+       smp_wmb();
        clp->cl_cons_state = state;
        wake_up_all(&nfs_client_active_wq);
 }
 
-/*
- * With sessions, the client is not marked ready until after a
- * successful EXCHANGE_ID and CREATE_SESSION.
- *
- * Map errors cl_cons_state errors to EPROTONOSUPPORT to indicate
- * other versions of NFS can be tried.
- */
-int nfs4_check_client_ready(struct nfs_client *clp)
-{
-       if (!nfs4_has_session(clp))
-               return 0;
-       if (clp->cl_cons_state < NFS_CS_READY)
-               return -EPROTONOSUPPORT;
-       return 0;
-}
-
 /*
  * Initialise the timeout values for a connection
  */
@@ -654,12 +653,11 @@ static void nfs_init_timeout_values(struct rpc_timeout *to, int proto,
  */
 static int nfs_create_rpc_client(struct nfs_client *clp,
                                 const struct rpc_timeout *timeparms,
-                                rpc_authflavor_t flavor,
-                                int discrtry, int noresvport)
+                                rpc_authflavor_t flavor)
 {
        struct rpc_clnt         *clnt = NULL;
        struct rpc_create_args args = {
-               .net            = clp->net,
+               .net            = clp->cl_net,
                .protocol       = clp->cl_proto,
                .address        = (struct sockaddr *)&clp->cl_addr,
                .addrsize       = clp->cl_addrlen,
@@ -670,9 +668,9 @@ static int nfs_create_rpc_client(struct nfs_client *clp,
                .authflavor     = flavor,
        };
 
-       if (discrtry)
+       if (test_bit(NFS_CS_DISCRTRY, &clp->cl_flags))
                args.flags |= RPC_CLNT_CREATE_DISCRTRY;
-       if (noresvport)
+       if (test_bit(NFS_CS_NORESVPORT, &clp->cl_flags))
                args.flags |= RPC_CLNT_CREATE_NONPRIVPORT;
 
        if (!IS_ERR(clp->cl_rpcclient))
@@ -713,7 +711,7 @@ static int nfs_start_lockd(struct nfs_server *server)
                .nfs_version    = clp->rpc_ops->version,
                .noresvport     = server->flags & NFS_MOUNT_NORESVPORT ?
                                        1 : 0,
-               .net            = clp->net,
+               .net            = clp->cl_net,
        };
 
        if (nlm_init.nfs_version > 3)
@@ -805,36 +803,43 @@ static int nfs_init_server_rpcclient(struct nfs_server *server,
        return 0;
 }
 
-/*
- * Initialise an NFS2 or NFS3 client
+/**
+ * nfs_init_client - Initialise an NFS2 or NFS3 client
+ *
+ * @clp: nfs_client to initialise
+ * @timeparms: timeout parameters for underlying RPC transport
+ * @ip_addr: IP presentation address (not used)
+ * @authflavor: authentication flavor for underlying RPC transport
+ *
+ * Returns pointer to an NFS client, or an ERR_PTR value.
  */
-int nfs_init_client(struct nfs_client *clp, const struct rpc_timeout *timeparms,
-                   const char *ip_addr, rpc_authflavor_t authflavour,
-                   int noresvport)
+struct nfs_client *nfs_init_client(struct nfs_client *clp,
+                   const struct rpc_timeout *timeparms,
+                   const char *ip_addr, rpc_authflavor_t authflavour)
 {
        int error;
 
        if (clp->cl_cons_state == NFS_CS_READY) {
                /* the client is already initialised */
                dprintk("<-- nfs_init_client() = 0 [already %p]\n", clp);
-               return 0;
+               return clp;
        }
 
        /*
         * Create a client RPC handle for doing FSSTAT with UNIX auth only
         * - RFC 2623, sec 2.3.2
         */
-       error = nfs_create_rpc_client(clp, timeparms, RPC_AUTH_UNIX,
-                                     0, noresvport);
+       error = nfs_create_rpc_client(clp, timeparms, RPC_AUTH_UNIX);
        if (error < 0)
                goto error;
        nfs_mark_client_ready(clp, NFS_CS_READY);
-       return 0;
+       return clp;
 
 error:
        nfs_mark_client_ready(clp, error);
+       nfs_put_client(clp);
        dprintk("<-- nfs_init_client() = xerror %d\n", error);
-       return error;
+       return ERR_PTR(error);
 }
 
 /*
@@ -847,7 +852,7 @@ static int nfs_init_server(struct nfs_server *server,
                .hostname = data->nfs_server.hostname,
                .addr = (const struct sockaddr *)&data->nfs_server.address,
                .addrlen = data->nfs_server.addrlen,
-               .rpc_ops = &nfs_v2_clientops,
+               .rpc_ops = NULL,
                .proto = data->nfs_server.protocol,
                .net = data->net,
        };
@@ -857,17 +862,28 @@ static int nfs_init_server(struct nfs_server *server,
 
        dprintk("--> nfs_init_server()\n");
 
+       switch (data->version) {
+#ifdef CONFIG_NFS_V2
+       case 2:
+               cl_init.rpc_ops = &nfs_v2_clientops;
+               break;
+#endif
 #ifdef CONFIG_NFS_V3
-       if (data->version == 3)
+       case 3:
                cl_init.rpc_ops = &nfs_v3_clientops;
+               break;
 #endif
+       default:
+               return -EPROTONOSUPPORT;
+       }
 
        nfs_init_timeout_values(&timeparms, data->nfs_server.protocol,
                        data->timeo, data->retrans);
+       if (data->flags & NFS_MOUNT_NORESVPORT)
+               set_bit(NFS_CS_NORESVPORT, &cl_init.init_flags);
 
        /* Allocate or find a client reference we can use */
-       clp = nfs_get_client(&cl_init, &timeparms, NULL, RPC_AUTH_UNIX,
-                            data->flags & NFS_MOUNT_NORESVPORT);
+       clp = nfs_get_client(&cl_init, &timeparms, NULL, RPC_AUTH_UNIX);
        if (IS_ERR(clp)) {
                dprintk("<-- nfs_init_server() = error %ld\n", PTR_ERR(clp));
                return PTR_ERR(clp);
@@ -880,7 +896,7 @@ static int nfs_init_server(struct nfs_server *server,
        server->options = data->options;
        server->caps |= NFS_CAP_HARDLINKS|NFS_CAP_SYMLINKS|NFS_CAP_FILEID|
                NFS_CAP_MODE|NFS_CAP_NLINK|NFS_CAP_OWNER|NFS_CAP_OWNER_GROUP|
-               NFS_CAP_ATIME|NFS_CAP_CTIME|NFS_CAP_MTIME;
+               NFS_CAP_ATIME|NFS_CAP_CTIME|NFS_CAP_MTIME|NFS_CAP_CHANGE_ATTR;
 
        if (data->rsize)
                server->rsize = nfs_block_size(data->rsize, NULL);
@@ -1048,7 +1064,7 @@ static void nfs_server_copy_userdata(struct nfs_server *target, struct nfs_serve
 static void nfs_server_insert_lists(struct nfs_server *server)
 {
        struct nfs_client *clp = server->nfs_client;
-       struct nfs_net *nn = net_generic(clp->net, nfs_net_id);
+       struct nfs_net *nn = net_generic(clp->cl_net, nfs_net_id);
 
        spin_lock(&nn->nfs_client_lock);
        list_add_tail_rcu(&server->client_link, &clp->cl_superblocks);
@@ -1065,7 +1081,7 @@ static void nfs_server_remove_lists(struct nfs_server *server)
 
        if (clp == NULL)
                return;
-       nn = net_generic(clp->net, nfs_net_id);
+       nn = net_generic(clp->cl_net, nfs_net_id);
        spin_lock(&nn->nfs_client_lock);
        list_del_rcu(&server->client_link);
        if (list_empty(&clp->cl_superblocks))
@@ -1333,21 +1349,27 @@ static int nfs4_init_client_minor_version(struct nfs_client *clp)
                 * so that the client back channel can find the
                 * nfs_client struct
                 */
-               clp->cl_cons_state = NFS_CS_SESSION_INITING;
+               nfs_mark_client_ready(clp, NFS_CS_SESSION_INITING);
        }
 #endif /* CONFIG_NFS_V4_1 */
 
        return nfs4_init_callback(clp);
 }
 
-/*
- * Initialise an NFS4 client record
+/**
+ * nfs4_init_client - Initialise an NFS4 client record
+ *
+ * @clp: nfs_client to initialise
+ * @timeparms: timeout parameters for underlying RPC transport
+ * @ip_addr: callback IP address in presentation format
+ * @authflavor: authentication flavor for underlying RPC transport
+ *
+ * Returns pointer to an NFS client, or an ERR_PTR value.
  */
-int nfs4_init_client(struct nfs_client *clp,
-                    const struct rpc_timeout *timeparms,
-                    const char *ip_addr,
-                    rpc_authflavor_t authflavour,
-                    int noresvport)
+struct nfs_client *nfs4_init_client(struct nfs_client *clp,
+                                   const struct rpc_timeout *timeparms,
+                                   const char *ip_addr,
+                                   rpc_authflavor_t authflavour)
 {
        char buf[INET6_ADDRSTRLEN + 1];
        int error;
@@ -1355,14 +1377,14 @@ int nfs4_init_client(struct nfs_client *clp,
        if (clp->cl_cons_state == NFS_CS_READY) {
                /* the client is initialised already */
                dprintk("<-- nfs4_init_client() = 0 [already %p]\n", clp);
-               return 0;
+               return clp;
        }
 
        /* Check NFS protocol revision and initialize RPC op vector */
        clp->rpc_ops = &nfs_v4_clientops;
 
-       error = nfs_create_rpc_client(clp, timeparms, authflavour,
-                                     1, noresvport);
+       __set_bit(NFS_CS_DISCRTRY, &clp->cl_flags);
+       error = nfs_create_rpc_client(clp, timeparms, authflavour);
        if (error < 0)
                goto error;
 
@@ -1395,12 +1417,13 @@ int nfs4_init_client(struct nfs_client *clp,
 
        if (!nfs4_has_session(clp))
                nfs_mark_client_ready(clp, NFS_CS_READY);
-       return 0;
+       return clp;
 
 error:
        nfs_mark_client_ready(clp, error);
+       nfs_put_client(clp);
        dprintk("<-- nfs4_init_client() = xerror %d\n", error);
-       return error;
+       return ERR_PTR(error);
 }
 
 /*
@@ -1429,9 +1452,11 @@ static int nfs4_set_client(struct nfs_server *server,
 
        dprintk("--> nfs4_set_client()\n");
 
+       if (server->flags & NFS_MOUNT_NORESVPORT)
+               set_bit(NFS_CS_NORESVPORT, &cl_init.init_flags);
+
        /* Allocate or find a client reference we can use */
-       clp = nfs_get_client(&cl_init, timeparms, ip_addr, authflavour,
-                            server->flags & NFS_MOUNT_NORESVPORT);
+       clp = nfs_get_client(&cl_init, timeparms, ip_addr, authflavour);
        if (IS_ERR(clp)) {
                error = PTR_ERR(clp);
                goto error;
@@ -1465,8 +1490,8 @@ error:
  * the MDS.
  */
 struct nfs_client *nfs4_set_ds_client(struct nfs_client* mds_clp,
-               const struct sockaddr *ds_addr,
-               int ds_addrlen, int ds_proto)
+               const struct sockaddr *ds_addr, int ds_addrlen,
+               int ds_proto, unsigned int ds_timeo, unsigned int ds_retrans)
 {
        struct nfs_client_initdata cl_init = {
                .addr = ds_addr,
@@ -1474,14 +1499,9 @@ struct nfs_client *nfs4_set_ds_client(struct nfs_client* mds_clp,
                .rpc_ops = &nfs_v4_clientops,
                .proto = ds_proto,
                .minorversion = mds_clp->cl_minorversion,
-               .net = mds_clp->net,
-       };
-       struct rpc_timeout ds_timeout = {
-               .to_initval = 15 * HZ,
-               .to_maxval = 15 * HZ,
-               .to_retries = 1,
-               .to_exponential = 1,
+               .net = mds_clp->cl_net,
        };
+       struct rpc_timeout ds_timeout;
        struct nfs_client *clp;
 
        /*
@@ -1489,8 +1509,9 @@ struct nfs_client *nfs4_set_ds_client(struct nfs_client* mds_clp,
         * cl_ipaddr so as to use the same EXCHANGE_ID co_ownerid as the MDS
         * (section 13.1 RFC 5661).
         */
+       nfs_init_timeout_values(&ds_timeout, ds_proto, ds_timeo, ds_retrans);
        clp = nfs_get_client(&cl_init, &ds_timeout, mds_clp->cl_ipaddr,
-                            mds_clp->cl_rpcclient->cl_auth->au_flavor, 0);
+                            mds_clp->cl_rpcclient->cl_auth->au_flavor);
 
        dprintk("<-- %s %p\n", __func__, clp);
        return clp;
@@ -1701,7 +1722,7 @@ struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data,
                                rpc_protocol(parent_server->client),
                                parent_server->client->cl_timeout,
                                parent_client->cl_mvops->minor_version,
-                               parent_client->net);
+                               parent_client->cl_net);
        if (error < 0)
                goto error;
 
@@ -1805,6 +1826,7 @@ void nfs_clients_init(struct net *net)
        idr_init(&nn->cb_ident_idr);
 #endif
        spin_lock_init(&nn->nfs_client_lock);
+       nn->boot_time = CURRENT_TIME;
 }
 
 #ifdef CONFIG_PROC_FS
index 89af1d269274f3a91401f704226dec43f9c02528..bd3a9601d32d9915a70e1888ab5a710671e00aac 100644 (file)
@@ -316,6 +316,10 @@ out:
  * nfs_client_return_marked_delegations - return previously marked delegations
  * @clp: nfs_client to process
  *
+ * Note that this function is designed to be called by the state
+ * manager thread. For this reason, it cannot flush the dirty data,
+ * since that could deadlock in case of a state recovery error.
+ *
  * Returns zero on success, or a negative errno value.
  */
 int nfs_client_return_marked_delegations(struct nfs_client *clp)
@@ -340,11 +344,9 @@ restart:
                                                                server);
                        rcu_read_unlock();
 
-                       if (delegation != NULL) {
-                               filemap_flush(inode->i_mapping);
+                       if (delegation != NULL)
                                err = __nfs_inode_return_delegation(inode,
                                                                delegation, 0);
-                       }
                        iput(inode);
                        if (!err)
                                goto restart;
@@ -380,6 +382,10 @@ void nfs_inode_return_delegation_noreclaim(struct inode *inode)
  * nfs_inode_return_delegation - synchronously return a delegation
  * @inode: inode to process
  *
+ * This routine will always flush any dirty data to disk on the
+ * assumption that if we need to return the delegation, then
+ * we should stop caching.
+ *
  * Returns zero on success, or a negative errno value.
  */
 int nfs_inode_return_delegation(struct inode *inode)
@@ -389,10 +395,10 @@ int nfs_inode_return_delegation(struct inode *inode)
        struct nfs_delegation *delegation;
        int err = 0;
 
+       nfs_wb_all(inode);
        if (rcu_access_pointer(nfsi->delegation) != NULL) {
                delegation = nfs_detach_delegation(nfsi, server);
                if (delegation != NULL) {
-                       nfs_wb_all(inode);
                        err = __nfs_inode_return_delegation(inode, delegation, 1);
                }
        }
@@ -538,6 +544,8 @@ int nfs_async_inode_return_delegation(struct inode *inode,
        struct nfs_client *clp = server->nfs_client;
        struct nfs_delegation *delegation;
 
+       filemap_flush(inode->i_mapping);
+
        rcu_read_lock();
        delegation = rcu_dereference(NFS_I(inode)->delegation);
 
index cd6a7a8dadae9054e5bd5557c0226bfd8accc116..72709c4193fa28ac3afeba63f65e2f46e954eb23 100644 (file)
@@ -66,6 +66,7 @@ static inline int nfs_have_delegation(struct inode *inode, fmode_t flags)
 
 static inline int nfs_inode_return_delegation(struct inode *inode)
 {
+       nfs_wb_all(inode);
        return 0;
 }
 #endif
index eedd24d0ad2efc6a02e7ff7b552e2e707adadfbd..f430057ff3b397c2fe1f523bf5fcea4135276f8c 100644 (file)
@@ -474,6 +474,29 @@ different:
        return 0;
 }
 
+static
+bool nfs_use_readdirplus(struct inode *dir, struct file *filp)
+{
+       if (!nfs_server_capable(dir, NFS_CAP_READDIRPLUS))
+               return false;
+       if (test_and_clear_bit(NFS_INO_ADVISE_RDPLUS, &NFS_I(dir)->flags))
+               return true;
+       if (filp->f_pos == 0)
+               return true;
+       return false;
+}
+
+/*
+ * This function is called by the lookup code to request the use of
+ * readdirplus to accelerate any future lookups in the same
+ * directory.
+ */
+static
+void nfs_advise_use_readdirplus(struct inode *dir)
+{
+       set_bit(NFS_INO_ADVISE_RDPLUS, &NFS_I(dir)->flags);
+}
+
 static
 void nfs_prime_dcache(struct dentry *parent, struct nfs_entry *entry)
 {
@@ -871,7 +894,7 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
        desc->file = filp;
        desc->dir_cookie = &dir_ctx->dir_cookie;
        desc->decode = NFS_PROTO(inode)->decode_dirent;
-       desc->plus = NFS_USE_READDIRPLUS(inode);
+       desc->plus = nfs_use_readdirplus(inode, filp) ? 1 : 0;
 
        nfs_block_sillyrename(dentry);
        res = nfs_revalidate_mapping(inode, filp->f_mapping);
@@ -1111,7 +1134,7 @@ static int nfs_lookup_revalidate(struct dentry *dentry, struct nameidata *nd)
        if (!inode) {
                if (nfs_neg_need_reval(dir, dentry, nd))
                        goto out_bad;
-               goto out_valid;
+               goto out_valid_noent;
        }
 
        if (is_bad_inode(inode)) {
@@ -1140,7 +1163,7 @@ static int nfs_lookup_revalidate(struct dentry *dentry, struct nameidata *nd)
        if (fhandle == NULL || fattr == NULL)
                goto out_error;
 
-       error = NFS_PROTO(dir)->lookup(NFS_SERVER(dir)->client, dir, &dentry->d_name, fhandle, fattr);
+       error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr);
        if (error)
                goto out_bad;
        if (nfs_compare_fh(NFS_FH(inode), fhandle))
@@ -1153,6 +1176,9 @@ static int nfs_lookup_revalidate(struct dentry *dentry, struct nameidata *nd)
 out_set_verifier:
        nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
  out_valid:
+       /* Success: notify readdir to use READDIRPLUS */
+       nfs_advise_use_readdirplus(dir);
+ out_valid_noent:
        dput(parent);
        dfprintk(LOOKUPCACHE, "NFS: %s(%s/%s) is valid\n",
                        __func__, dentry->d_parent->d_name.name,
@@ -1296,7 +1322,7 @@ static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, stru
        parent = dentry->d_parent;
        /* Protect against concurrent sillydeletes */
        nfs_block_sillyrename(parent);
-       error = NFS_PROTO(dir)->lookup(NFS_SERVER(dir)->client, dir, &dentry->d_name, fhandle, fattr);
+       error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr);
        if (error == -ENOENT)
                goto no_entry;
        if (error < 0) {
@@ -1308,6 +1334,9 @@ static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, stru
        if (IS_ERR(res))
                goto out_unblock_sillyrename;
 
+       /* Success: notify readdir to use READDIRPLUS */
+       nfs_advise_use_readdirplus(dir);
+
 no_entry:
        res = d_materialise_unique(dentry, inode);
        if (res != NULL) {
@@ -1325,10 +1354,10 @@ out:
 }
 
 #ifdef CONFIG_NFS_V4
-static int nfs_open_revalidate(struct dentry *, struct nameidata *);
+static int nfs4_lookup_revalidate(struct dentry *, struct nameidata *);
 
 const struct dentry_operations nfs4_dentry_operations = {
-       .d_revalidate   = nfs_open_revalidate,
+       .d_revalidate   = nfs4_lookup_revalidate,
        .d_delete       = nfs_dentry_delete,
        .d_iput         = nfs_dentry_iput,
        .d_automount    = nfs_d_automount,
@@ -1490,13 +1519,11 @@ no_open:
        return nfs_lookup(dir, dentry, nd);
 }
 
-static int nfs_open_revalidate(struct dentry *dentry, struct nameidata *nd)
+static int nfs4_lookup_revalidate(struct dentry *dentry, struct nameidata *nd)
 {
        struct dentry *parent = NULL;
        struct inode *inode;
        struct inode *dir;
-       struct nfs_open_context *ctx;
-       struct iattr attr;
        int openflags, ret = 0;
 
        if (nd->flags & LOOKUP_RCU)
@@ -1525,57 +1552,13 @@ static int nfs_open_revalidate(struct dentry *dentry, struct nameidata *nd)
        /* We cannot do exclusive creation on a positive dentry */
        if ((openflags & (O_CREAT|O_EXCL)) == (O_CREAT|O_EXCL))
                goto no_open_dput;
-       /* We can't create new files here */
-       openflags &= ~(O_CREAT|O_EXCL);
-
-       ctx = create_nfs_open_context(dentry, openflags);
-       ret = PTR_ERR(ctx);
-       if (IS_ERR(ctx))
-               goto out;
 
-       attr.ia_valid = ATTR_OPEN;
-       if (openflags & O_TRUNC) {
-               attr.ia_valid |= ATTR_SIZE;
-               attr.ia_size = 0;
-               nfs_wb_all(inode);
-       }
+       /* Let f_op->open() actually open (and revalidate) the file */
+       ret = 1;
 
-       /*
-        * Note: we're not holding inode->i_mutex and so may be racing with
-        * operations that change the directory. We therefore save the
-        * change attribute *before* we do the RPC call.
-        */
-       inode = NFS_PROTO(dir)->open_context(dir, ctx, openflags, &attr);
-       if (IS_ERR(inode)) {
-               ret = PTR_ERR(inode);
-               switch (ret) {
-               case -EPERM:
-               case -EACCES:
-               case -EDQUOT:
-               case -ENOSPC:
-               case -EROFS:
-                       goto out_put_ctx;
-               default:
-                       goto out_drop;
-               }
-       }
-       iput(inode);
-       if (inode != dentry->d_inode)
-               goto out_drop;
-
-       nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
-       ret = nfs_intent_set_file(nd, ctx);
-       if (ret >= 0)
-               ret = 1;
 out:
        dput(parent);
        return ret;
-out_drop:
-       d_drop(dentry);
-       ret = 0;
-out_put_ctx:
-       put_nfs_open_context(ctx);
-       goto out;
 
 no_open_dput:
        dput(parent);
@@ -1643,7 +1626,7 @@ int nfs_instantiate(struct dentry *dentry, struct nfs_fh *fhandle,
        if (dentry->d_inode)
                goto out;
        if (fhandle->size == 0) {
-               error = NFS_PROTO(dir)->lookup(NFS_SERVER(dir)->client, dir, &dentry->d_name, fhandle, fattr);
+               error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr);
                if (error)
                        goto out_error;
        }
index 481be7f7bdd3b953179a2ab354c9f05fc4773925..ad2775d3e219b65f6efed3454d6422f8822de0e8 100644 (file)
@@ -56,6 +56,7 @@
 
 #include "internal.h"
 #include "iostat.h"
+#include "pnfs.h"
 
 #define NFSDBG_FACILITY                NFSDBG_VFS
 
@@ -81,16 +82,19 @@ struct nfs_direct_req {
        struct completion       completion;     /* wait for i/o completion */
 
        /* commit state */
-       struct list_head        rewrite_list;   /* saved nfs_write_data structs */
-       struct nfs_write_data * commit_data;    /* special write_data for commits */
+       struct nfs_mds_commit_info mds_cinfo;   /* Storage for cinfo */
+       struct pnfs_ds_commit_info ds_cinfo;    /* Storage for cinfo */
+       struct work_struct      work;
        int                     flags;
 #define NFS_ODIRECT_DO_COMMIT          (1)     /* an unstable reply was received */
 #define NFS_ODIRECT_RESCHED_WRITES     (2)     /* write verification failed */
        struct nfs_writeverf    verf;           /* unstable write verifier */
 };
 
+static const struct nfs_pgio_completion_ops nfs_direct_write_completion_ops;
+static const struct nfs_commit_completion_ops nfs_direct_commit_completion_ops;
 static void nfs_direct_write_complete(struct nfs_direct_req *dreq, struct inode *inode);
-static const struct rpc_call_ops nfs_write_direct_ops;
+static void nfs_direct_write_schedule_work(struct work_struct *work);
 
 static inline void get_dreq(struct nfs_direct_req *dreq)
 {
@@ -124,22 +128,6 @@ ssize_t nfs_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov, loff_
        return -EINVAL;
 }
 
-static void nfs_direct_dirty_pages(struct page **pages, unsigned int pgbase, size_t count)
-{
-       unsigned int npages;
-       unsigned int i;
-
-       if (count == 0)
-               return;
-       pages += (pgbase >> PAGE_SHIFT);
-       npages = (count + (pgbase & ~PAGE_MASK) + PAGE_SIZE - 1) >> PAGE_SHIFT;
-       for (i = 0; i < npages; i++) {
-               struct page *page = pages[i];
-               if (!PageCompound(page))
-                       set_page_dirty(page);
-       }
-}
-
 static void nfs_direct_release_pages(struct page **pages, unsigned int npages)
 {
        unsigned int i;
@@ -147,26 +135,30 @@ static void nfs_direct_release_pages(struct page **pages, unsigned int npages)
                page_cache_release(pages[i]);
 }
 
+void nfs_init_cinfo_from_dreq(struct nfs_commit_info *cinfo,
+                             struct nfs_direct_req *dreq)
+{
+       cinfo->lock = &dreq->lock;
+       cinfo->mds = &dreq->mds_cinfo;
+       cinfo->ds = &dreq->ds_cinfo;
+       cinfo->dreq = dreq;
+       cinfo->completion_ops = &nfs_direct_commit_completion_ops;
+}
+
 static inline struct nfs_direct_req *nfs_direct_req_alloc(void)
 {
        struct nfs_direct_req *dreq;
 
-       dreq = kmem_cache_alloc(nfs_direct_cachep, GFP_KERNEL);
+       dreq = kmem_cache_zalloc(nfs_direct_cachep, GFP_KERNEL);
        if (!dreq)
                return NULL;
 
        kref_init(&dreq->kref);
        kref_get(&dreq->kref);
        init_completion(&dreq->completion);
-       INIT_LIST_HEAD(&dreq->rewrite_list);
-       dreq->iocb = NULL;
-       dreq->ctx = NULL;
-       dreq->l_ctx = NULL;
+       INIT_LIST_HEAD(&dreq->mds_cinfo.list);
+       INIT_WORK(&dreq->work, nfs_direct_write_schedule_work);
        spin_lock_init(&dreq->lock);
-       atomic_set(&dreq->io_count, 0);
-       dreq->count = 0;
-       dreq->error = 0;
-       dreq->flags = 0;
 
        return dreq;
 }
@@ -226,47 +218,80 @@ static void nfs_direct_complete(struct nfs_direct_req *dreq)
        nfs_direct_req_release(dreq);
 }
 
-/*
- * We must hold a reference to all the pages in this direct read request
- * until the RPCs complete.  This could be long *after* we are woken up in
- * nfs_direct_wait (for instance, if someone hits ^C on a slow server).
- */
-static void nfs_direct_read_result(struct rpc_task *task, void *calldata)
+static void nfs_direct_readpage_release(struct nfs_page *req)
 {
-       struct nfs_read_data *data = calldata;
-
-       nfs_readpage_result(task, data);
+       dprintk("NFS: direct read done (%s/%lld %d@%lld)\n",
+               req->wb_context->dentry->d_inode->i_sb->s_id,
+               (long long)NFS_FILEID(req->wb_context->dentry->d_inode),
+               req->wb_bytes,
+               (long long)req_offset(req));
+       nfs_release_request(req);
 }
 
-static void nfs_direct_read_release(void *calldata)
+static void nfs_direct_read_completion(struct nfs_pgio_header *hdr)
 {
+       unsigned long bytes = 0;
+       struct nfs_direct_req *dreq = hdr->dreq;
 
-       struct nfs_read_data *data = calldata;
-       struct nfs_direct_req *dreq = (struct nfs_direct_req *) data->req;
-       int status = data->task.tk_status;
+       if (test_bit(NFS_IOHDR_REDO, &hdr->flags))
+               goto out_put;
 
        spin_lock(&dreq->lock);
-       if (unlikely(status < 0)) {
-               dreq->error = status;
-               spin_unlock(&dreq->lock);
-       } else {
-               dreq->count += data->res.count;
-               spin_unlock(&dreq->lock);
-               nfs_direct_dirty_pages(data->pagevec,
-                               data->args.pgbase,
-                               data->res.count);
-       }
-       nfs_direct_release_pages(data->pagevec, data->npages);
+       if (test_bit(NFS_IOHDR_ERROR, &hdr->flags) && (hdr->good_bytes == 0))
+               dreq->error = hdr->error;
+       else
+               dreq->count += hdr->good_bytes;
+       spin_unlock(&dreq->lock);
 
+       while (!list_empty(&hdr->pages)) {
+               struct nfs_page *req = nfs_list_entry(hdr->pages.next);
+               struct page *page = req->wb_page;
+
+               if (test_bit(NFS_IOHDR_EOF, &hdr->flags)) {
+                       if (bytes > hdr->good_bytes)
+                               zero_user(page, 0, PAGE_SIZE);
+                       else if (hdr->good_bytes - bytes < PAGE_SIZE)
+                               zero_user_segment(page,
+                                       hdr->good_bytes & ~PAGE_MASK,
+                                       PAGE_SIZE);
+               }
+               if (!PageCompound(page)) {
+                       if (test_bit(NFS_IOHDR_ERROR, &hdr->flags)) {
+                               if (bytes < hdr->good_bytes)
+                                       set_page_dirty(page);
+                       } else
+                               set_page_dirty(page);
+               }
+               bytes += req->wb_bytes;
+               nfs_list_remove_request(req);
+               nfs_direct_readpage_release(req);
+       }
+out_put:
        if (put_dreq(dreq))
                nfs_direct_complete(dreq);
-       nfs_readdata_free(data);
+       hdr->release(hdr);
+}
+
+static void nfs_read_sync_pgio_error(struct list_head *head)
+{
+       struct nfs_page *req;
+
+       while (!list_empty(head)) {
+               req = nfs_list_entry(head->next);
+               nfs_list_remove_request(req);
+               nfs_release_request(req);
+       }
 }
 
-static const struct rpc_call_ops nfs_read_direct_ops = {
-       .rpc_call_prepare = nfs_read_prepare,
-       .rpc_call_done = nfs_direct_read_result,
-       .rpc_release = nfs_direct_read_release,
+static void nfs_direct_pgio_init(struct nfs_pgio_header *hdr)
+{
+       get_dreq(hdr->dreq);
+}
+
+static const struct nfs_pgio_completion_ops nfs_direct_read_completion_ops = {
+       .error_cleanup = nfs_read_sync_pgio_error,
+       .init_hdr = nfs_direct_pgio_init,
+       .completion = nfs_direct_read_completion,
 };
 
 /*
@@ -276,107 +301,82 @@ static const struct rpc_call_ops nfs_read_direct_ops = {
  * handled automatically by nfs_direct_read_result().  Otherwise, if
  * no requests have been sent, just return an error.
  */
-static ssize_t nfs_direct_read_schedule_segment(struct nfs_direct_req *dreq,
+static ssize_t nfs_direct_read_schedule_segment(struct nfs_pageio_descriptor *desc,
                                                const struct iovec *iov,
                                                loff_t pos)
 {
+       struct nfs_direct_req *dreq = desc->pg_dreq;
        struct nfs_open_context *ctx = dreq->ctx;
        struct inode *inode = ctx->dentry->d_inode;
        unsigned long user_addr = (unsigned long)iov->iov_base;
        size_t count = iov->iov_len;
        size_t rsize = NFS_SERVER(inode)->rsize;
-       struct rpc_task *task;
-       struct rpc_message msg = {
-               .rpc_cred = ctx->cred,
-       };
-       struct rpc_task_setup task_setup_data = {
-               .rpc_client = NFS_CLIENT(inode),
-               .rpc_message = &msg,
-               .callback_ops = &nfs_read_direct_ops,
-               .workqueue = nfsiod_workqueue,
-               .flags = RPC_TASK_ASYNC,
-       };
        unsigned int pgbase;
        int result;
        ssize_t started = 0;
+       struct page **pagevec = NULL;
+       unsigned int npages;
 
        do {
-               struct nfs_read_data *data;
                size_t bytes;
+               int i;
 
                pgbase = user_addr & ~PAGE_MASK;
-               bytes = min(rsize,count);
+               bytes = min(max_t(size_t, rsize, PAGE_SIZE), count);
 
                result = -ENOMEM;
-               data = nfs_readdata_alloc(nfs_page_array_len(pgbase, bytes));
-               if (unlikely(!data))
+               npages = nfs_page_array_len(pgbase, bytes);
+               if (!pagevec)
+                       pagevec = kmalloc(npages * sizeof(struct page *),
+                                         GFP_KERNEL);
+               if (!pagevec)
                        break;
-
                down_read(&current->mm->mmap_sem);
                result = get_user_pages(current, current->mm, user_addr,
-                                       data->npages, 1, 0, data->pagevec, NULL);
+                                       npages, 1, 0, pagevec, NULL);
                up_read(&current->mm->mmap_sem);
-               if (result < 0) {
-                       nfs_readdata_free(data);
+               if (result < 0)
                        break;
-               }
-               if ((unsigned)result < data->npages) {
+               if ((unsigned)result < npages) {
                        bytes = result * PAGE_SIZE;
                        if (bytes <= pgbase) {
-                               nfs_direct_release_pages(data->pagevec, result);
-                               nfs_readdata_free(data);
+                               nfs_direct_release_pages(pagevec, result);
                                break;
                        }
                        bytes -= pgbase;
-                       data->npages = result;
+                       npages = result;
                }
 
-               get_dreq(dreq);
-
-               data->req = (struct nfs_page *) dreq;
-               data->inode = inode;
-               data->cred = msg.rpc_cred;
-               data->args.fh = NFS_FH(inode);
-               data->args.context = ctx;
-               data->args.lock_context = dreq->l_ctx;
-               data->args.offset = pos;
-               data->args.pgbase = pgbase;
-               data->args.pages = data->pagevec;
-               data->args.count = bytes;
-               data->res.fattr = &data->fattr;
-               data->res.eof = 0;
-               data->res.count = bytes;
-               nfs_fattr_init(&data->fattr);
-               msg.rpc_argp = &data->args;
-               msg.rpc_resp = &data->res;
-
-               task_setup_data.task = &data->task;
-               task_setup_data.callback_data = data;
-               NFS_PROTO(inode)->read_setup(data, &msg);
-
-               task = rpc_run_task(&task_setup_data);
-               if (IS_ERR(task))
-                       break;
-               rpc_put_task(task);
-
-               dprintk("NFS: %5u initiated direct read call "
-                       "(req %s/%Ld, %zu bytes @ offset %Lu)\n",
-                               data->task.tk_pid,
-                               inode->i_sb->s_id,
-                               (long long)NFS_FILEID(inode),
-                               bytes,
-                               (unsigned long long)data->args.offset);
-
-               started += bytes;
-               user_addr += bytes;
-               pos += bytes;
-               /* FIXME: Remove this unnecessary math from final patch */
-               pgbase += bytes;
-               pgbase &= ~PAGE_MASK;
-               BUG_ON(pgbase != (user_addr & ~PAGE_MASK));
-
-               count -= bytes;
-       } while (count != 0);
+               for (i = 0; i < npages; i++) {
+                       struct nfs_page *req;
+                       unsigned int req_len = min_t(size_t, bytes, PAGE_SIZE - pgbase);
+                       /* XXX do we need to do the eof zeroing found in async_filler? */
+                       req = nfs_create_request(dreq->ctx, dreq->inode,
+                                                pagevec[i],
+                                                pgbase, req_len);
+                       if (IS_ERR(req)) {
+                               result = PTR_ERR(req);
+                               break;
+                       }
+                       req->wb_index = pos >> PAGE_SHIFT;
+                       req->wb_offset = pos & ~PAGE_MASK;
+                       if (!nfs_pageio_add_request(desc, req)) {
+                               result = desc->pg_error;
+                               nfs_release_request(req);
+                               break;
+                       }
+                       pgbase = 0;
+                       bytes -= req_len;
+                       started += req_len;
+                       user_addr += req_len;
+                       pos += req_len;
+                       count -= req_len;
+               }
+               /* The nfs_page now hold references to these pages */
+               nfs_direct_release_pages(pagevec, npages);
+       } while (count != 0 && result >= 0);
+
+       kfree(pagevec);
 
        if (started)
                return started;
@@ -388,15 +388,19 @@ static ssize_t nfs_direct_read_schedule_iovec(struct nfs_direct_req *dreq,
                                              unsigned long nr_segs,
                                              loff_t pos)
 {
+       struct nfs_pageio_descriptor desc;
        ssize_t result = -EINVAL;
        size_t requested_bytes = 0;
        unsigned long seg;
 
+       nfs_pageio_init_read(&desc, dreq->inode,
+                            &nfs_direct_read_completion_ops);
        get_dreq(dreq);
+       desc.pg_dreq = dreq;
 
        for (seg = 0; seg < nr_segs; seg++) {
                const struct iovec *vec = &iov[seg];
-               result = nfs_direct_read_schedule_segment(dreq, vec, pos);
+               result = nfs_direct_read_schedule_segment(&desc, vec, pos);
                if (result < 0)
                        break;
                requested_bytes += result;
@@ -405,6 +409,8 @@ static ssize_t nfs_direct_read_schedule_iovec(struct nfs_direct_req *dreq,
                pos += vec->iov_len;
        }
 
+       nfs_pageio_complete(&desc);
+
        /*
         * If no bytes were started, return the error, and let the
         * generic layer handle the completion.
@@ -441,104 +447,70 @@ static ssize_t nfs_direct_read(struct kiocb *iocb, const struct iovec *iov,
        result = nfs_direct_read_schedule_iovec(dreq, iov, nr_segs, pos);
        if (!result)
                result = nfs_direct_wait(dreq);
+       NFS_I(inode)->read_io += result;
 out_release:
        nfs_direct_req_release(dreq);
 out:
        return result;
 }
 
-static void nfs_direct_free_writedata(struct nfs_direct_req *dreq)
+static void nfs_inode_dio_write_done(struct inode *inode)
 {
-       while (!list_empty(&dreq->rewrite_list)) {
-               struct nfs_write_data *data = list_entry(dreq->rewrite_list.next, struct nfs_write_data, pages);
-               list_del(&data->pages);
-               nfs_direct_release_pages(data->pagevec, data->npages);
-               nfs_writedata_free(data);
-       }
+       nfs_zap_mapping(inode, inode->i_mapping);
+       inode_dio_done(inode);
 }
 
 #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4)
 static void nfs_direct_write_reschedule(struct nfs_direct_req *dreq)
 {
-       struct inode *inode = dreq->inode;
-       struct list_head *p;
-       struct nfs_write_data *data;
-       struct rpc_task *task;
-       struct rpc_message msg = {
-               .rpc_cred = dreq->ctx->cred,
-       };
-       struct rpc_task_setup task_setup_data = {
-               .rpc_client = NFS_CLIENT(inode),
-               .rpc_message = &msg,
-               .callback_ops = &nfs_write_direct_ops,
-               .workqueue = nfsiod_workqueue,
-               .flags = RPC_TASK_ASYNC,
-       };
+       struct nfs_pageio_descriptor desc;
+       struct nfs_page *req, *tmp;
+       LIST_HEAD(reqs);
+       struct nfs_commit_info cinfo;
+       LIST_HEAD(failed);
+
+       nfs_init_cinfo_from_dreq(&cinfo, dreq);
+       pnfs_recover_commit_reqs(dreq->inode, &reqs, &cinfo);
+       spin_lock(cinfo.lock);
+       nfs_scan_commit_list(&cinfo.mds->list, &reqs, &cinfo, 0);
+       spin_unlock(cinfo.lock);
 
        dreq->count = 0;
        get_dreq(dreq);
 
-       list_for_each(p, &dreq->rewrite_list) {
-               data = list_entry(p, struct nfs_write_data, pages);
-
-               get_dreq(dreq);
-
-               /* Use stable writes */
-               data->args.stable = NFS_FILE_SYNC;
-
-               /*
-                * Reset data->res.
-                */
-               nfs_fattr_init(&data->fattr);
-               data->res.count = data->args.count;
-               memset(&data->verf, 0, sizeof(data->verf));
-
-               /*
-                * Reuse data->task; data->args should not have changed
-                * since the original request was sent.
-                */
-               task_setup_data.task = &data->task;
-               task_setup_data.callback_data = data;
-               msg.rpc_argp = &data->args;
-               msg.rpc_resp = &data->res;
-               NFS_PROTO(inode)->write_setup(data, &msg);
-
-               /*
-                * We're called via an RPC callback, so BKL is already held.
-                */
-               task = rpc_run_task(&task_setup_data);
-               if (!IS_ERR(task))
-                       rpc_put_task(task);
-
-               dprintk("NFS: %5u rescheduled direct write call (req %s/%Ld, %u bytes @ offset %Lu)\n",
-                               data->task.tk_pid,
-                               inode->i_sb->s_id,
-                               (long long)NFS_FILEID(inode),
-                               data->args.count,
-                               (unsigned long long)data->args.offset);
+       nfs_pageio_init_write(&desc, dreq->inode, FLUSH_STABLE,
+                             &nfs_direct_write_completion_ops);
+       desc.pg_dreq = dreq;
+
+       list_for_each_entry_safe(req, tmp, &reqs, wb_list) {
+               if (!nfs_pageio_add_request(&desc, req)) {
+                       nfs_list_add_request(req, &failed);
+                       spin_lock(cinfo.lock);
+                       dreq->flags = 0;
+                       dreq->error = -EIO;
+                       spin_unlock(cinfo.lock);
+               }
        }
+       nfs_pageio_complete(&desc);
 
-       if (put_dreq(dreq))
-               nfs_direct_write_complete(dreq, inode);
-}
-
-static void nfs_direct_commit_result(struct rpc_task *task, void *calldata)
-{
-       struct nfs_write_data *data = calldata;
+       while (!list_empty(&failed))
+               nfs_unlock_and_release_request(req);
 
-       /* Call the NFS version-specific code */
-       NFS_PROTO(data->inode)->commit_done(task, data);
+       if (put_dreq(dreq))
+               nfs_direct_write_complete(dreq, dreq->inode);
 }
 
-static void nfs_direct_commit_release(void *calldata)
+static void nfs_direct_commit_complete(struct nfs_commit_data *data)
 {
-       struct nfs_write_data *data = calldata;
-       struct nfs_direct_req *dreq = (struct nfs_direct_req *) data->req;
+       struct nfs_direct_req *dreq = data->dreq;
+       struct nfs_commit_info cinfo;
+       struct nfs_page *req;
        int status = data->task.tk_status;
 
+       nfs_init_cinfo_from_dreq(&cinfo, dreq);
        if (status < 0) {
                dprintk("NFS: %5u commit failed with error %d.\n",
-                               data->task.tk_pid, status);
+                       data->task.tk_pid, status);
                dreq->flags = NFS_ODIRECT_RESCHED_WRITES;
        } else if (memcmp(&dreq->verf, &data->verf, sizeof(data->verf))) {
                dprintk("NFS: %5u commit verify failed\n", data->task.tk_pid);
@@ -546,62 +518,47 @@ static void nfs_direct_commit_release(void *calldata)
        }
 
        dprintk("NFS: %5u commit returned %d\n", data->task.tk_pid, status);
-       nfs_direct_write_complete(dreq, data->inode);
-       nfs_commit_free(data);
+       while (!list_empty(&data->pages)) {
+               req = nfs_list_entry(data->pages.next);
+               nfs_list_remove_request(req);
+               if (dreq->flags == NFS_ODIRECT_RESCHED_WRITES) {
+                       /* Note the rewrite will go through mds */
+                       kref_get(&req->wb_kref);
+                       nfs_mark_request_commit(req, NULL, &cinfo);
+               }
+               nfs_unlock_and_release_request(req);
+       }
+
+       if (atomic_dec_and_test(&cinfo.mds->rpcs_out))
+               nfs_direct_write_complete(dreq, data->inode);
 }
 
-static const struct rpc_call_ops nfs_commit_direct_ops = {
-       .rpc_call_prepare = nfs_write_prepare,
-       .rpc_call_done = nfs_direct_commit_result,
-       .rpc_release = nfs_direct_commit_release,
+static void nfs_direct_error_cleanup(struct nfs_inode *nfsi)
+{
+       /* There is no lock to clear */
+}
+
+static const struct nfs_commit_completion_ops nfs_direct_commit_completion_ops = {
+       .completion = nfs_direct_commit_complete,
+       .error_cleanup = nfs_direct_error_cleanup,
 };
 
 static void nfs_direct_commit_schedule(struct nfs_direct_req *dreq)
 {
-       struct nfs_write_data *data = dreq->commit_data;
-       struct rpc_task *task;
-       struct rpc_message msg = {
-               .rpc_argp = &data->args,
-               .rpc_resp = &data->res,
-               .rpc_cred = dreq->ctx->cred,
-       };
-       struct rpc_task_setup task_setup_data = {
-               .task = &data->task,
-               .rpc_client = NFS_CLIENT(dreq->inode),
-               .rpc_message = &msg,
-               .callback_ops = &nfs_commit_direct_ops,
-               .callback_data = data,
-               .workqueue = nfsiod_workqueue,
-               .flags = RPC_TASK_ASYNC,
-       };
-
-       data->inode = dreq->inode;
-       data->cred = msg.rpc_cred;
-
-       data->args.fh = NFS_FH(data->inode);
-       data->args.offset = 0;
-       data->args.count = 0;
-       data->args.context = dreq->ctx;
-       data->args.lock_context = dreq->l_ctx;
-       data->res.count = 0;
-       data->res.fattr = &data->fattr;
-       data->res.verf = &data->verf;
-       nfs_fattr_init(&data->fattr);
-
-       NFS_PROTO(data->inode)->commit_setup(data, &msg);
-
-       /* Note: task.tk_ops->rpc_release will free dreq->commit_data */
-       dreq->commit_data = NULL;
-
-       dprintk("NFS: %5u initiated commit call\n", data->task.tk_pid);
-
-       task = rpc_run_task(&task_setup_data);
-       if (!IS_ERR(task))
-               rpc_put_task(task);
+       int res;
+       struct nfs_commit_info cinfo;
+       LIST_HEAD(mds_list);
+
+       nfs_init_cinfo_from_dreq(&cinfo, dreq);
+       nfs_scan_commit(dreq->inode, &mds_list, &cinfo);
+       res = nfs_generic_commit_list(dreq->inode, &mds_list, 0, &cinfo);
+       if (res < 0) /* res == -ENOMEM */
+               nfs_direct_write_reschedule(dreq);
 }
 
-static void nfs_direct_write_complete(struct nfs_direct_req *dreq, struct inode *inode)
+static void nfs_direct_write_schedule_work(struct work_struct *work)
 {
+       struct nfs_direct_req *dreq = container_of(work, struct nfs_direct_req, work);
        int flags = dreq->flags;
 
        dreq->flags = 0;
@@ -613,89 +570,32 @@ static void nfs_direct_write_complete(struct nfs_direct_req *dreq, struct inode
                        nfs_direct_write_reschedule(dreq);
                        break;
                default:
-                       if (dreq->commit_data != NULL)
-                               nfs_commit_free(dreq->commit_data);
-                       nfs_direct_free_writedata(dreq);
-                       nfs_zap_mapping(inode, inode->i_mapping);
+                       nfs_inode_dio_write_done(dreq->inode);
                        nfs_direct_complete(dreq);
        }
 }
 
-static void nfs_alloc_commit_data(struct nfs_direct_req *dreq)
+static void nfs_direct_write_complete(struct nfs_direct_req *dreq, struct inode *inode)
 {
-       dreq->commit_data = nfs_commitdata_alloc();
-       if (dreq->commit_data != NULL)
-               dreq->commit_data->req = (struct nfs_page *) dreq;
+       schedule_work(&dreq->work); /* Calls nfs_direct_write_schedule_work */
 }
+
 #else
-static inline void nfs_alloc_commit_data(struct nfs_direct_req *dreq)
+static void nfs_direct_write_schedule_work(struct work_struct *work)
 {
-       dreq->commit_data = NULL;
 }
 
 static void nfs_direct_write_complete(struct nfs_direct_req *dreq, struct inode *inode)
 {
-       nfs_direct_free_writedata(dreq);
-       nfs_zap_mapping(inode, inode->i_mapping);
+       nfs_inode_dio_write_done(inode);
        nfs_direct_complete(dreq);
 }
 #endif
 
-static void nfs_direct_write_result(struct rpc_task *task, void *calldata)
-{
-       struct nfs_write_data *data = calldata;
-
-       nfs_writeback_done(task, data);
-}
-
 /*
  * NB: Return the value of the first error return code.  Subsequent
  *     errors after the first one are ignored.
  */
-static void nfs_direct_write_release(void *calldata)
-{
-       struct nfs_write_data *data = calldata;
-       struct nfs_direct_req *dreq = (struct nfs_direct_req *) data->req;
-       int status = data->task.tk_status;
-
-       spin_lock(&dreq->lock);
-
-       if (unlikely(status < 0)) {
-               /* An error has occurred, so we should not commit */
-               dreq->flags = 0;
-               dreq->error = status;
-       }
-       if (unlikely(dreq->error != 0))
-               goto out_unlock;
-
-       dreq->count += data->res.count;
-
-       if (data->res.verf->committed != NFS_FILE_SYNC) {
-               switch (dreq->flags) {
-                       case 0:
-                               memcpy(&dreq->verf, &data->verf, sizeof(dreq->verf));
-                               dreq->flags = NFS_ODIRECT_DO_COMMIT;
-                               break;
-                       case NFS_ODIRECT_DO_COMMIT:
-                               if (memcmp(&dreq->verf, &data->verf, sizeof(dreq->verf))) {
-                                       dprintk("NFS: %5u write verify failed\n", data->task.tk_pid);
-                                       dreq->flags = NFS_ODIRECT_RESCHED_WRITES;
-                               }
-               }
-       }
-out_unlock:
-       spin_unlock(&dreq->lock);
-
-       if (put_dreq(dreq))
-               nfs_direct_write_complete(dreq, data->inode);
-}
-
-static const struct rpc_call_ops nfs_write_direct_ops = {
-       .rpc_call_prepare = nfs_write_prepare,
-       .rpc_call_done = nfs_direct_write_result,
-       .rpc_release = nfs_direct_write_release,
-};
-
 /*
  * For each wsize'd chunk of the user's buffer, dispatch an NFS WRITE
  * operation.  If nfs_writedata_alloc() or get_user_pages() fails,
@@ -703,132 +603,189 @@ static const struct rpc_call_ops nfs_write_direct_ops = {
  * handled automatically by nfs_direct_write_result().  Otherwise, if
  * no requests have been sent, just return an error.
  */
-static ssize_t nfs_direct_write_schedule_segment(struct nfs_direct_req *dreq,
+static ssize_t nfs_direct_write_schedule_segment(struct nfs_pageio_descriptor *desc,
                                                 const struct iovec *iov,
-                                                loff_t pos, int sync)
+                                                loff_t pos)
 {
+       struct nfs_direct_req *dreq = desc->pg_dreq;
        struct nfs_open_context *ctx = dreq->ctx;
        struct inode *inode = ctx->dentry->d_inode;
        unsigned long user_addr = (unsigned long)iov->iov_base;
        size_t count = iov->iov_len;
-       struct rpc_task *task;
-       struct rpc_message msg = {
-               .rpc_cred = ctx->cred,
-       };
-       struct rpc_task_setup task_setup_data = {
-               .rpc_client = NFS_CLIENT(inode),
-               .rpc_message = &msg,
-               .callback_ops = &nfs_write_direct_ops,
-               .workqueue = nfsiod_workqueue,
-               .flags = RPC_TASK_ASYNC,
-       };
        size_t wsize = NFS_SERVER(inode)->wsize;
        unsigned int pgbase;
        int result;
        ssize_t started = 0;
+       struct page **pagevec = NULL;
+       unsigned int npages;
 
        do {
-               struct nfs_write_data *data;
                size_t bytes;
+               int i;
 
                pgbase = user_addr & ~PAGE_MASK;
-               bytes = min(wsize,count);
+               bytes = min(max_t(size_t, wsize, PAGE_SIZE), count);
 
                result = -ENOMEM;
-               data = nfs_writedata_alloc(nfs_page_array_len(pgbase, bytes));
-               if (unlikely(!data))
+               npages = nfs_page_array_len(pgbase, bytes);
+               if (!pagevec)
+                       pagevec = kmalloc(npages * sizeof(struct page *), GFP_KERNEL);
+               if (!pagevec)
                        break;
 
                down_read(&current->mm->mmap_sem);
                result = get_user_pages(current, current->mm, user_addr,
-                                       data->npages, 0, 0, data->pagevec, NULL);
+                                       npages, 0, 0, pagevec, NULL);
                up_read(&current->mm->mmap_sem);
-               if (result < 0) {
-                       nfs_writedata_free(data);
+               if (result < 0)
                        break;
-               }
-               if ((unsigned)result < data->npages) {
+
+               if ((unsigned)result < npages) {
                        bytes = result * PAGE_SIZE;
                        if (bytes <= pgbase) {
-                               nfs_direct_release_pages(data->pagevec, result);
-                               nfs_writedata_free(data);
+                               nfs_direct_release_pages(pagevec, result);
                                break;
                        }
                        bytes -= pgbase;
-                       data->npages = result;
+                       npages = result;
                }
 
-               get_dreq(dreq);
-
-               list_move_tail(&data->pages, &dreq->rewrite_list);
-
-               data->req = (struct nfs_page *) dreq;
-               data->inode = inode;
-               data->cred = msg.rpc_cred;
-               data->args.fh = NFS_FH(inode);
-               data->args.context = ctx;
-               data->args.lock_context = dreq->l_ctx;
-               data->args.offset = pos;
-               data->args.pgbase = pgbase;
-               data->args.pages = data->pagevec;
-               data->args.count = bytes;
-               data->args.stable = sync;
-               data->res.fattr = &data->fattr;
-               data->res.count = bytes;
-               data->res.verf = &data->verf;
-               nfs_fattr_init(&data->fattr);
-
-               task_setup_data.task = &data->task;
-               task_setup_data.callback_data = data;
-               msg.rpc_argp = &data->args;
-               msg.rpc_resp = &data->res;
-               NFS_PROTO(inode)->write_setup(data, &msg);
-
-               task = rpc_run_task(&task_setup_data);
-               if (IS_ERR(task))
-                       break;
-               rpc_put_task(task);
-
-               dprintk("NFS: %5u initiated direct write call "
-                       "(req %s/%Ld, %zu bytes @ offset %Lu)\n",
-                               data->task.tk_pid,
-                               inode->i_sb->s_id,
-                               (long long)NFS_FILEID(inode),
-                               bytes,
-                               (unsigned long long)data->args.offset);
+               for (i = 0; i < npages; i++) {
+                       struct nfs_page *req;
+                       unsigned int req_len = min_t(size_t, bytes, PAGE_SIZE - pgbase);
 
-               started += bytes;
-               user_addr += bytes;
-               pos += bytes;
-
-               /* FIXME: Remove this useless math from the final patch */
-               pgbase += bytes;
-               pgbase &= ~PAGE_MASK;
-               BUG_ON(pgbase != (user_addr & ~PAGE_MASK));
+                       req = nfs_create_request(dreq->ctx, dreq->inode,
+                                                pagevec[i],
+                                                pgbase, req_len);
+                       if (IS_ERR(req)) {
+                               result = PTR_ERR(req);
+                               break;
+                       }
+                       nfs_lock_request(req);
+                       req->wb_index = pos >> PAGE_SHIFT;
+                       req->wb_offset = pos & ~PAGE_MASK;
+                       if (!nfs_pageio_add_request(desc, req)) {
+                               result = desc->pg_error;
+                               nfs_unlock_and_release_request(req);
+                               break;
+                       }
+                       pgbase = 0;
+                       bytes -= req_len;
+                       started += req_len;
+                       user_addr += req_len;
+                       pos += req_len;
+                       count -= req_len;
+               }
+               /* The nfs_page now hold references to these pages */
+               nfs_direct_release_pages(pagevec, npages);
+       } while (count != 0 && result >= 0);
 
-               count -= bytes;
-       } while (count != 0);
+       kfree(pagevec);
 
        if (started)
                return started;
        return result < 0 ? (ssize_t) result : -EFAULT;
 }
 
+static void nfs_direct_write_completion(struct nfs_pgio_header *hdr)
+{
+       struct nfs_direct_req *dreq = hdr->dreq;
+       struct nfs_commit_info cinfo;
+       int bit = -1;
+       struct nfs_page *req = nfs_list_entry(hdr->pages.next);
+
+       if (test_bit(NFS_IOHDR_REDO, &hdr->flags))
+               goto out_put;
+
+       nfs_init_cinfo_from_dreq(&cinfo, dreq);
+
+       spin_lock(&dreq->lock);
+
+       if (test_bit(NFS_IOHDR_ERROR, &hdr->flags)) {
+               dreq->flags = 0;
+               dreq->error = hdr->error;
+       }
+       if (dreq->error != 0)
+               bit = NFS_IOHDR_ERROR;
+       else {
+               dreq->count += hdr->good_bytes;
+               if (test_bit(NFS_IOHDR_NEED_RESCHED, &hdr->flags)) {
+                       dreq->flags = NFS_ODIRECT_RESCHED_WRITES;
+                       bit = NFS_IOHDR_NEED_RESCHED;
+               } else if (test_bit(NFS_IOHDR_NEED_COMMIT, &hdr->flags)) {
+                       if (dreq->flags == NFS_ODIRECT_RESCHED_WRITES)
+                               bit = NFS_IOHDR_NEED_RESCHED;
+                       else if (dreq->flags == 0) {
+                               memcpy(&dreq->verf, &req->wb_verf,
+                                      sizeof(dreq->verf));
+                               bit = NFS_IOHDR_NEED_COMMIT;
+                               dreq->flags = NFS_ODIRECT_DO_COMMIT;
+                       } else if (dreq->flags == NFS_ODIRECT_DO_COMMIT) {
+                               if (memcmp(&dreq->verf, &req->wb_verf, sizeof(dreq->verf))) {
+                                       dreq->flags = NFS_ODIRECT_RESCHED_WRITES;
+                                       bit = NFS_IOHDR_NEED_RESCHED;
+                               } else
+                                       bit = NFS_IOHDR_NEED_COMMIT;
+                       }
+               }
+       }
+       spin_unlock(&dreq->lock);
+
+       while (!list_empty(&hdr->pages)) {
+               req = nfs_list_entry(hdr->pages.next);
+               nfs_list_remove_request(req);
+               switch (bit) {
+               case NFS_IOHDR_NEED_RESCHED:
+               case NFS_IOHDR_NEED_COMMIT:
+                       kref_get(&req->wb_kref);
+                       nfs_mark_request_commit(req, hdr->lseg, &cinfo);
+               }
+               nfs_unlock_and_release_request(req);
+       }
+
+out_put:
+       if (put_dreq(dreq))
+               nfs_direct_write_complete(dreq, hdr->inode);
+       hdr->release(hdr);
+}
+
+static void nfs_write_sync_pgio_error(struct list_head *head)
+{
+       struct nfs_page *req;
+
+       while (!list_empty(head)) {
+               req = nfs_list_entry(head->next);
+               nfs_list_remove_request(req);
+               nfs_unlock_and_release_request(req);
+       }
+}
+
+static const struct nfs_pgio_completion_ops nfs_direct_write_completion_ops = {
+       .error_cleanup = nfs_write_sync_pgio_error,
+       .init_hdr = nfs_direct_pgio_init,
+       .completion = nfs_direct_write_completion,
+};
+
 static ssize_t nfs_direct_write_schedule_iovec(struct nfs_direct_req *dreq,
                                               const struct iovec *iov,
                                               unsigned long nr_segs,
-                                              loff_t pos, int sync)
+                                              loff_t pos)
 {
+       struct nfs_pageio_descriptor desc;
+       struct inode *inode = dreq->inode;
        ssize_t result = 0;
        size_t requested_bytes = 0;
        unsigned long seg;
 
+       nfs_pageio_init_write(&desc, inode, FLUSH_COND_STABLE,
+                             &nfs_direct_write_completion_ops);
+       desc.pg_dreq = dreq;
        get_dreq(dreq);
+       atomic_inc(&inode->i_dio_count);
 
        for (seg = 0; seg < nr_segs; seg++) {
                const struct iovec *vec = &iov[seg];
-               result = nfs_direct_write_schedule_segment(dreq, vec,
-                                                          pos, sync);
+               result = nfs_direct_write_schedule_segment(&desc, vec, pos);
                if (result < 0)
                        break;
                requested_bytes += result;
@@ -836,12 +793,15 @@ static ssize_t nfs_direct_write_schedule_iovec(struct nfs_direct_req *dreq,
                        break;
                pos += vec->iov_len;
        }
+       nfs_pageio_complete(&desc);
+       NFS_I(dreq->inode)->write_io += desc.pg_bytes_written;
 
        /*
         * If no bytes were started, return the error, and let the
         * generic layer handle the completion.
         */
        if (requested_bytes == 0) {
+               inode_dio_done(inode);
                nfs_direct_req_release(dreq);
                return result < 0 ? result : -EIO;
        }
@@ -858,16 +818,10 @@ static ssize_t nfs_direct_write(struct kiocb *iocb, const struct iovec *iov,
        ssize_t result = -ENOMEM;
        struct inode *inode = iocb->ki_filp->f_mapping->host;
        struct nfs_direct_req *dreq;
-       size_t wsize = NFS_SERVER(inode)->wsize;
-       int sync = NFS_UNSTABLE;
 
        dreq = nfs_direct_req_alloc();
        if (!dreq)
                goto out;
-       nfs_alloc_commit_data(dreq);
-
-       if (dreq->commit_data == NULL || count <= wsize)
-               sync = NFS_FILE_SYNC;
 
        dreq->inode = inode;
        dreq->ctx = get_nfs_open_context(nfs_file_open_context(iocb->ki_filp));
@@ -877,7 +831,7 @@ static ssize_t nfs_direct_write(struct kiocb *iocb, const struct iovec *iov,
        if (!is_sync_kiocb(iocb))
                dreq->iocb = iocb;
 
-       result = nfs_direct_write_schedule_iovec(dreq, iov, nr_segs, pos, sync);
+       result = nfs_direct_write_schedule_iovec(dreq, iov, nr_segs, pos);
        if (!result)
                result = nfs_direct_wait(dreq);
 out_release:
@@ -997,10 +951,15 @@ ssize_t nfs_file_direct_write(struct kiocb *iocb, const struct iovec *iov,
        task_io_account_write(count);
 
        retval = nfs_direct_write(iocb, iov, nr_segs, pos, count);
+       if (retval > 0) {
+               struct inode *inode = mapping->host;
 
-       if (retval > 0)
                iocb->ki_pos = pos + retval;
-
+               spin_lock(&inode->i_lock);
+               if (i_size_read(inode) < iocb->ki_pos)
+                       i_size_write(inode, iocb->ki_pos);
+               spin_unlock(&inode->i_lock);
+       }
 out:
        return retval;
 }
index aa9b709fd328d7178cd1c4bdb417fb6f454472af..a6708e6b438dd55f2924e5bb78c809c1575a97a9 100644 (file)
@@ -174,6 +174,13 @@ nfs_file_flush(struct file *file, fl_owner_t id)
        if ((file->f_mode & FMODE_WRITE) == 0)
                return 0;
 
+       /*
+        * If we're holding a write delegation, then just start the i/o
+        * but don't wait for completion (or send a commit).
+        */
+       if (nfs_have_delegation(inode, FMODE_WRITE))
+               return filemap_fdatawrite(file->f_mapping);
+
        /* Flush writes to the server and return any errors */
        return vfs_fsync(file, 0);
 }
@@ -417,6 +424,7 @@ static int nfs_write_end(struct file *file, struct address_space *mapping,
 
        if (status < 0)
                return status;
+       NFS_I(mapping->host)->write_io += copied;
        return copied;
 }
 
@@ -871,12 +879,81 @@ const struct file_operations nfs_file_operations = {
 static int
 nfs4_file_open(struct inode *inode, struct file *filp)
 {
+       struct nfs_open_context *ctx;
+       struct dentry *dentry = filp->f_path.dentry;
+       struct dentry *parent = NULL;
+       struct inode *dir;
+       unsigned openflags = filp->f_flags;
+       struct iattr attr;
+       int err;
+
+       BUG_ON(inode != dentry->d_inode);
        /*
-        * NFSv4 opens are handled in d_lookup and d_revalidate. If we get to
-        * this point, then something is very wrong
+        * If no cached dentry exists or if it's negative, NFSv4 handled the
+        * opens in ->lookup() or ->create().
+        *
+        * We only get this far for a cached positive dentry.  We skipped
+        * revalidation, so handle it here by dropping the dentry and returning
+        * -EOPENSTALE.  The VFS will retry the lookup/create/open.
         */
-       dprintk("NFS: %s called! inode=%p filp=%p\n", __func__, inode, filp);
-       return -ENOTDIR;
+
+       dprintk("NFS: open file(%s/%s)\n",
+               dentry->d_parent->d_name.name,
+               dentry->d_name.name);
+
+       if ((openflags & O_ACCMODE) == 3)
+               openflags--;
+
+       /* We can't create new files here */
+       openflags &= ~(O_CREAT|O_EXCL);
+
+       parent = dget_parent(dentry);
+       dir = parent->d_inode;
+
+       ctx = alloc_nfs_open_context(filp->f_path.dentry, filp->f_mode);
+       err = PTR_ERR(ctx);
+       if (IS_ERR(ctx))
+               goto out;
+
+       attr.ia_valid = ATTR_OPEN;
+       if (openflags & O_TRUNC) {
+               attr.ia_valid |= ATTR_SIZE;
+               attr.ia_size = 0;
+               nfs_wb_all(inode);
+       }
+
+       inode = NFS_PROTO(dir)->open_context(dir, ctx, openflags, &attr);
+       if (IS_ERR(inode)) {
+               err = PTR_ERR(inode);
+               switch (err) {
+               case -EPERM:
+               case -EACCES:
+               case -EDQUOT:
+               case -ENOSPC:
+               case -EROFS:
+                       goto out_put_ctx;
+               default:
+                       goto out_drop;
+               }
+       }
+       iput(inode);
+       if (inode != dentry->d_inode)
+               goto out_drop;
+
+       nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
+       nfs_file_set_open_context(filp, ctx);
+       err = 0;
+
+out_put_ctx:
+       put_nfs_open_context(ctx);
+out:
+       dput(parent);
+       return err;
+
+out_drop:
+       d_drop(dentry);
+       err = -EOPENSTALE;
+       goto out_put_ctx;
 }
 
 const struct file_operations nfs4_file_operations = {
index ae65c16b3670ebb5ed6da1d214f2cde16fd7f73e..c817787fbdb4024738a84d804a8800cd7b6dbedf 100644 (file)
@@ -64,23 +64,12 @@ void nfs_fscache_release_client_cookie(struct nfs_client *clp)
  * either by the 'fsc=xxx' option to mount, or by inheriting it from the parent
  * superblock across an automount point of some nature.
  */
-void nfs_fscache_get_super_cookie(struct super_block *sb, const char *uniq,
-                                 struct nfs_clone_mount *mntdata)
+void nfs_fscache_get_super_cookie(struct super_block *sb, const char *uniq, int ulen)
 {
        struct nfs_fscache_key *key, *xkey;
        struct nfs_server *nfss = NFS_SB(sb);
        struct rb_node **p, *parent;
-       int diff, ulen;
-
-       if (uniq) {
-               ulen = strlen(uniq);
-       } else if (mntdata) {
-               struct nfs_server *mnt_s = NFS_SB(mntdata->sb);
-               if (mnt_s->fscache_key) {
-                       uniq = mnt_s->fscache_key->key.uniquifier;
-                       ulen = mnt_s->fscache_key->key.uniq_len;
-               }
-       }
+       int diff;
 
        if (!uniq) {
                uniq = "";
index b9c572d0679f8ced0aead467778d56229de9a03b..c5b11b53ff33b33d4249a88443b102ae33f00df5 100644 (file)
@@ -73,9 +73,7 @@ extern void nfs_fscache_unregister(void);
 extern void nfs_fscache_get_client_cookie(struct nfs_client *);
 extern void nfs_fscache_release_client_cookie(struct nfs_client *);
 
-extern void nfs_fscache_get_super_cookie(struct super_block *,
-                                        const char *,
-                                        struct nfs_clone_mount *);
+extern void nfs_fscache_get_super_cookie(struct super_block *, const char *, int);
 extern void nfs_fscache_release_super_cookie(struct super_block *);
 
 extern void nfs_fscache_init_inode_cookie(struct inode *);
@@ -172,12 +170,6 @@ static inline void nfs_fscache_unregister(void) {}
 static inline void nfs_fscache_get_client_cookie(struct nfs_client *clp) {}
 static inline void nfs_fscache_release_client_cookie(struct nfs_client *clp) {}
 
-static inline void nfs_fscache_get_super_cookie(
-       struct super_block *sb,
-       const char *uniq,
-       struct nfs_clone_mount *mntdata)
-{
-}
 static inline void nfs_fscache_release_super_cookie(struct super_block *sb) {}
 
 static inline void nfs_fscache_init_inode_cookie(struct inode *inode) {}
index 4ca6f5c8038e02dfddb3a8a33dec71a8cb20295f..8abfb19bd3aa3b739611ce1f97025d643539b09c 100644 (file)
@@ -150,7 +150,7 @@ int nfs4_get_rootfh(struct nfs_server *server, struct nfs_fh *mntfh)
                goto out;
 
        /* Start by getting the root filehandle from the server */
-       ret = server->nfs_client->rpc_ops->getroot(server, mntfh, &fsinfo);
+       ret = nfs4_proc_get_rootfh(server, mntfh, &fsinfo);
        if (ret < 0) {
                dprintk("nfs4_get_rootfh: getroot error = %d\n", -ret);
                goto out;
@@ -178,87 +178,4 @@ out:
        return ret;
 }
 
-/*
- * get an NFS4 root dentry from the root filehandle
- */
-struct dentry *nfs4_get_root(struct super_block *sb, struct nfs_fh *mntfh,
-                            const char *devname)
-{
-       struct nfs_server *server = NFS_SB(sb);
-       struct nfs_fattr *fattr = NULL;
-       struct dentry *ret;
-       struct inode *inode;
-       void *name = kstrdup(devname, GFP_KERNEL);
-       int error;
-
-       dprintk("--> nfs4_get_root()\n");
-
-       if (!name)
-               return ERR_PTR(-ENOMEM);
-
-       /* get the info about the server and filesystem */
-       error = nfs4_server_capabilities(server, mntfh);
-       if (error < 0) {
-               dprintk("nfs_get_root: getcaps error = %d\n",
-                       -error);
-               kfree(name);
-               return ERR_PTR(error);
-       }
-
-       fattr = nfs_alloc_fattr();
-       if (fattr == NULL) {
-               kfree(name);
-               return ERR_PTR(-ENOMEM);
-       }
-
-       /* get the actual root for this mount */
-       error = server->nfs_client->rpc_ops->getattr(server, mntfh, fattr);
-       if (error < 0) {
-               dprintk("nfs_get_root: getattr error = %d\n", -error);
-               ret = ERR_PTR(error);
-               goto out;
-       }
-
-       if (fattr->valid & NFS_ATTR_FATTR_FSID &&
-           !nfs_fsid_equal(&server->fsid, &fattr->fsid))
-               memcpy(&server->fsid, &fattr->fsid, sizeof(server->fsid));
-
-       inode = nfs_fhget(sb, mntfh, fattr);
-       if (IS_ERR(inode)) {
-               dprintk("nfs_get_root: get root inode failed\n");
-               ret = ERR_CAST(inode);
-               goto out;
-       }
-
-       error = nfs_superblock_set_dummy_root(sb, inode);
-       if (error != 0) {
-               ret = ERR_PTR(error);
-               goto out;
-       }
-
-       /* root dentries normally start off anonymous and get spliced in later
-        * if the dentry tree reaches them; however if the dentry already
-        * exists, we'll pick it up at this point and use it as the root
-        */
-       ret = d_obtain_alias(inode);
-       if (IS_ERR(ret)) {
-               dprintk("nfs_get_root: get root dentry failed\n");
-               goto out;
-       }
-
-       security_d_instantiate(ret, inode);
-       spin_lock(&ret->d_lock);
-       if (IS_ROOT(ret) && !(ret->d_flags & DCACHE_NFSFS_RENAMED)) {
-               ret->d_fsdata = name;
-               name = NULL;
-       }
-       spin_unlock(&ret->d_lock);
-out:
-       if (name)
-               kfree(name);
-       nfs_free_fattr(fattr);
-       dprintk("<-- nfs4_get_root()\n");
-       return ret;
-}
-
 #endif /* CONFIG_NFS_V4 */
index ba3019f5934c21a610a96569b1d239b90eca0459..b5b86a05059c8c0cf157495878bad3621a25a8dc 100644 (file)
@@ -415,7 +415,7 @@ static int __nfs_idmap_register(struct dentry *dir,
 static void nfs_idmap_unregister(struct nfs_client *clp,
                                      struct rpc_pipe *pipe)
 {
-       struct net *net = clp->net;
+       struct net *net = clp->cl_net;
        struct super_block *pipefs_sb;
 
        pipefs_sb = rpc_get_sb_net(net);
@@ -429,7 +429,7 @@ static int nfs_idmap_register(struct nfs_client *clp,
                                   struct idmap *idmap,
                                   struct rpc_pipe *pipe)
 {
-       struct net *net = clp->net;
+       struct net *net = clp->cl_net;
        struct super_block *pipefs_sb;
        int err = 0;
 
@@ -530,9 +530,25 @@ static struct nfs_client *nfs_get_client_for_event(struct net *net, int event)
        struct nfs_net *nn = net_generic(net, nfs_net_id);
        struct dentry *cl_dentry;
        struct nfs_client *clp;
+       int err;
 
+restart:
        spin_lock(&nn->nfs_client_lock);
        list_for_each_entry(clp, &nn->nfs_client_list, cl_share_link) {
+               /* Wait for initialisation to finish */
+               if (clp->cl_cons_state == NFS_CS_INITING) {
+                       atomic_inc(&clp->cl_count);
+                       spin_unlock(&nn->nfs_client_lock);
+                       err = nfs_wait_client_init_complete(clp);
+                       nfs_put_client(clp);
+                       if (err)
+                               return NULL;
+                       goto restart;
+               }
+               /* Skip nfs_clients that failed to initialise */
+               if (clp->cl_cons_state < 0)
+                       continue;
+               smp_rmb();
                if (clp->rpc_ops != &nfs_v4_clientops)
                        continue;
                cl_dentry = clp->cl_idmap->idmap_pipe->dentry;
@@ -640,20 +656,16 @@ static int nfs_idmap_legacy_upcall(struct key_construction *cons,
        struct idmap_msg *im;
        struct idmap *idmap = (struct idmap *)aux;
        struct key *key = cons->key;
-       int ret;
+       int ret = -ENOMEM;
 
        /* msg and im are freed in idmap_pipe_destroy_msg */
        msg = kmalloc(sizeof(*msg), GFP_KERNEL);
-       if (IS_ERR(msg)) {
-               ret = PTR_ERR(msg);
+       if (!msg)
                goto out0;
-       }
 
        im = kmalloc(sizeof(*im), GFP_KERNEL);
-       if (IS_ERR(im)) {
-               ret = PTR_ERR(im);
+       if (!im)
                goto out1;
-       }
 
        ret = nfs_idmap_prepare_message(key->description, im, msg);
        if (ret < 0)
index c6073139b402f1250634dfa892ca6ae4a4885cc3..e605d695dbcb7b746d633f37e5ceb509deb7b790 100644 (file)
@@ -285,9 +285,7 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
                inode->i_mode = fattr->mode;
                if ((fattr->valid & NFS_ATTR_FATTR_MODE) == 0
                                && nfs_server_capable(inode, NFS_CAP_MODE))
-                       nfsi->cache_validity |= NFS_INO_INVALID_ATTR
-                               | NFS_INO_INVALID_ACCESS
-                               | NFS_INO_INVALID_ACL;
+                       nfsi->cache_validity |= NFS_INO_INVALID_ATTR;
                /* Why so? Because we want revalidate for devices/FIFOs, and
                 * that's precisely what we have in nfs_file_inode_operations.
                 */
@@ -300,8 +298,6 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
                        inode->i_op = NFS_SB(sb)->nfs_client->rpc_ops->dir_inode_ops;
                        inode->i_fop = &nfs_dir_operations;
                        inode->i_data.a_ops = &nfs_dir_aops;
-                       if (nfs_server_capable(inode, NFS_CAP_READDIRPLUS))
-                               set_bit(NFS_INO_ADVISE_RDPLUS, &NFS_I(inode)->flags);
                        /* Deal with crossing mountpoints */
                        if (fattr->valid & NFS_ATTR_FATTR_MOUNTPOINT ||
                                        fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL) {
@@ -327,6 +323,8 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
                inode->i_gid = -2;
                inode->i_blocks = 0;
                memset(nfsi->cookieverf, 0, sizeof(nfsi->cookieverf));
+               nfsi->write_io = 0;
+               nfsi->read_io = 0;
 
                nfsi->read_cache_jiffies = fattr->time_start;
                nfsi->attr_gencount = fattr->gencount;
@@ -337,24 +335,19 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
                if (fattr->valid & NFS_ATTR_FATTR_MTIME)
                        inode->i_mtime = fattr->mtime;
                else if (nfs_server_capable(inode, NFS_CAP_MTIME))
-                       nfsi->cache_validity |= NFS_INO_INVALID_ATTR
-                               | NFS_INO_INVALID_DATA;
+                       nfsi->cache_validity |= NFS_INO_INVALID_ATTR;
                if (fattr->valid & NFS_ATTR_FATTR_CTIME)
                        inode->i_ctime = fattr->ctime;
                else if (nfs_server_capable(inode, NFS_CAP_CTIME))
-                       nfsi->cache_validity |= NFS_INO_INVALID_ATTR
-                               | NFS_INO_INVALID_ACCESS
-                               | NFS_INO_INVALID_ACL;
+                       nfsi->cache_validity |= NFS_INO_INVALID_ATTR;
                if (fattr->valid & NFS_ATTR_FATTR_CHANGE)
                        inode->i_version = fattr->change_attr;
                else if (nfs_server_capable(inode, NFS_CAP_CHANGE_ATTR))
-                       nfsi->cache_validity |= NFS_INO_INVALID_ATTR
-                               | NFS_INO_INVALID_DATA;
+                       nfsi->cache_validity |= NFS_INO_INVALID_ATTR;
                if (fattr->valid & NFS_ATTR_FATTR_SIZE)
                        inode->i_size = nfs_size_to_loff_t(fattr->size);
                else
                        nfsi->cache_validity |= NFS_INO_INVALID_ATTR
-                               | NFS_INO_INVALID_DATA
                                | NFS_INO_REVAL_PAGECACHE;
                if (fattr->valid & NFS_ATTR_FATTR_NLINK)
                        set_nlink(inode, fattr->nlink);
@@ -363,15 +356,11 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
                if (fattr->valid & NFS_ATTR_FATTR_OWNER)
                        inode->i_uid = fattr->uid;
                else if (nfs_server_capable(inode, NFS_CAP_OWNER))
-                       nfsi->cache_validity |= NFS_INO_INVALID_ATTR
-                               | NFS_INO_INVALID_ACCESS
-                               | NFS_INO_INVALID_ACL;
+                       nfsi->cache_validity |= NFS_INO_INVALID_ATTR;
                if (fattr->valid & NFS_ATTR_FATTR_GROUP)
                        inode->i_gid = fattr->gid;
                else if (nfs_server_capable(inode, NFS_CAP_OWNER_GROUP))
-                       nfsi->cache_validity |= NFS_INO_INVALID_ATTR
-                               | NFS_INO_INVALID_ACCESS
-                               | NFS_INO_INVALID_ACL;
+                       nfsi->cache_validity |= NFS_INO_INVALID_ATTR;
                if (fattr->valid & NFS_ATTR_FATTR_BLOCKS_USED)
                        inode->i_blocks = fattr->du.nfs2.blocks;
                if (fattr->valid & NFS_ATTR_FATTR_SPACE_USED) {
@@ -429,8 +418,10 @@ nfs_setattr(struct dentry *dentry, struct iattr *attr)
                return 0;
 
        /* Write all dirty data */
-       if (S_ISREG(inode->i_mode))
+       if (S_ISREG(inode->i_mode)) {
+               nfs_inode_dio_wait(inode);
                nfs_wb_all(inode);
+       }
 
        fattr = nfs_alloc_fattr();
        if (fattr == NULL)
@@ -514,6 +505,7 @@ int nfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
 
        /* Flush out writes to the server in order to update c/mtime.  */
        if (S_ISREG(inode->i_mode)) {
+               nfs_inode_dio_wait(inode);
                err = filemap_write_and_wait(inode->i_mapping);
                if (err)
                        goto out;
@@ -654,6 +646,7 @@ struct nfs_open_context *alloc_nfs_open_context(struct dentry *dentry, fmode_t f
        nfs_init_lock_context(&ctx->lock_context);
        ctx->lock_context.open_context = ctx;
        INIT_LIST_HEAD(&ctx->list);
+       ctx->mdsthreshold = NULL;
        return ctx;
 }
 
@@ -682,6 +675,7 @@ static void __put_nfs_open_context(struct nfs_open_context *ctx, int is_sync)
                put_rpccred(ctx->cred);
        dput(ctx->dentry);
        nfs_sb_deactive(sb);
+       kfree(ctx->mdsthreshold);
        kfree(ctx);
 }
 
@@ -870,6 +864,15 @@ static int nfs_invalidate_mapping(struct inode *inode, struct address_space *map
        return 0;
 }
 
+static bool nfs_mapping_need_revalidate_inode(struct inode *inode)
+{
+       if (nfs_have_delegated_attributes(inode))
+               return false;
+       return (NFS_I(inode)->cache_validity & NFS_INO_REVAL_PAGECACHE)
+               || nfs_attribute_timeout(inode)
+               || NFS_STALE(inode);
+}
+
 /**
  * nfs_revalidate_mapping - Revalidate the pagecache
  * @inode - pointer to host inode
@@ -880,9 +883,7 @@ int nfs_revalidate_mapping(struct inode *inode, struct address_space *mapping)
        struct nfs_inode *nfsi = NFS_I(inode);
        int ret = 0;
 
-       if ((nfsi->cache_validity & NFS_INO_REVAL_PAGECACHE)
-                       || nfs_attribute_cache_expired(inode)
-                       || NFS_STALE(inode)) {
+       if (nfs_mapping_need_revalidate_inode(inode)) {
                ret = __nfs_revalidate_inode(NFS_SERVER(inode), inode);
                if (ret < 0)
                        goto out;
@@ -948,6 +949,8 @@ static int nfs_check_inode_attributes(struct inode *inode, struct nfs_fattr *fat
        unsigned long invalid = 0;
 
 
+       if (nfs_have_delegated_attributes(inode))
+               return 0;
        /* Has the inode gone and changed behind our back? */
        if ((fattr->valid & NFS_ATTR_FATTR_FILEID) && nfsi->fileid != fattr->fileid)
                return -EIO;
@@ -960,7 +963,7 @@ static int nfs_check_inode_attributes(struct inode *inode, struct nfs_fattr *fat
 
        /* Verify a few of the more important attributes */
        if ((fattr->valid & NFS_ATTR_FATTR_MTIME) && !timespec_equal(&inode->i_mtime, &fattr->mtime))
-               invalid |= NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE;
+               invalid |= NFS_INO_INVALID_ATTR;
 
        if (fattr->valid & NFS_ATTR_FATTR_SIZE) {
                cur_size = i_size_read(inode);
@@ -1279,14 +1282,26 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
                        nfs_display_fhandle_hash(NFS_FH(inode)),
                        atomic_read(&inode->i_count), fattr->valid);
 
-       if ((fattr->valid & NFS_ATTR_FATTR_FILEID) && nfsi->fileid != fattr->fileid)
-               goto out_fileid;
+       if ((fattr->valid & NFS_ATTR_FATTR_FILEID) && nfsi->fileid != fattr->fileid) {
+               printk(KERN_ERR "NFS: server %s error: fileid changed\n"
+                       "fsid %s: expected fileid 0x%Lx, got 0x%Lx\n",
+                       NFS_SERVER(inode)->nfs_client->cl_hostname,
+                       inode->i_sb->s_id, (long long)nfsi->fileid,
+                       (long long)fattr->fileid);
+               goto out_err;
+       }
 
        /*
         * Make sure the inode's type hasn't changed.
         */
-       if ((fattr->valid & NFS_ATTR_FATTR_TYPE) && (inode->i_mode & S_IFMT) != (fattr->mode & S_IFMT))
-               goto out_changed;
+       if ((fattr->valid & NFS_ATTR_FATTR_TYPE) && (inode->i_mode & S_IFMT) != (fattr->mode & S_IFMT)) {
+               /*
+               * Big trouble! The inode has become a different object.
+               */
+               printk(KERN_DEBUG "NFS: %s: inode %ld mode changed, %07o to %07o\n",
+                               __func__, inode->i_ino, inode->i_mode, fattr->mode);
+               goto out_err;
+       }
 
        server = NFS_SERVER(inode);
        /* Update the fsid? */
@@ -1314,7 +1329,11 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
                if (inode->i_version != fattr->change_attr) {
                        dprintk("NFS: change_attr change on server for file %s/%ld\n",
                                        inode->i_sb->s_id, inode->i_ino);
-                       invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL;
+                       invalid |= NFS_INO_INVALID_ATTR
+                               | NFS_INO_INVALID_DATA
+                               | NFS_INO_INVALID_ACCESS
+                               | NFS_INO_INVALID_ACL
+                               | NFS_INO_REVAL_PAGECACHE;
                        if (S_ISDIR(inode->i_mode))
                                nfs_force_lookup_revalidate(inode);
                        inode->i_version = fattr->change_attr;
@@ -1323,38 +1342,15 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
                invalid |= save_cache_validity;
 
        if (fattr->valid & NFS_ATTR_FATTR_MTIME) {
-               /* NFSv2/v3: Check if the mtime agrees */
-               if (!timespec_equal(&inode->i_mtime, &fattr->mtime)) {
-                       dprintk("NFS: mtime change on server for file %s/%ld\n",
-                                       inode->i_sb->s_id, inode->i_ino);
-                       invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA;
-                       if (S_ISDIR(inode->i_mode))
-                               nfs_force_lookup_revalidate(inode);
-                       memcpy(&inode->i_mtime, &fattr->mtime, sizeof(inode->i_mtime));
-               }
+               memcpy(&inode->i_mtime, &fattr->mtime, sizeof(inode->i_mtime));
        } else if (server->caps & NFS_CAP_MTIME)
                invalid |= save_cache_validity & (NFS_INO_INVALID_ATTR
-                               | NFS_INO_INVALID_DATA
-                               | NFS_INO_REVAL_PAGECACHE
                                | NFS_INO_REVAL_FORCED);
 
        if (fattr->valid & NFS_ATTR_FATTR_CTIME) {
-               /* If ctime has changed we should definitely clear access+acl caches */
-               if (!timespec_equal(&inode->i_ctime, &fattr->ctime)) {
-                       invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL;
-                       /* and probably clear data for a directory too as utimes can cause
-                        * havoc with our cache.
-                        */
-                       if (S_ISDIR(inode->i_mode)) {
-                               invalid |= NFS_INO_INVALID_DATA;
-                               nfs_force_lookup_revalidate(inode);
-                       }
-                       memcpy(&inode->i_ctime, &fattr->ctime, sizeof(inode->i_ctime));
-               }
+               memcpy(&inode->i_ctime, &fattr->ctime, sizeof(inode->i_ctime));
        } else if (server->caps & NFS_CAP_CTIME)
                invalid |= save_cache_validity & (NFS_INO_INVALID_ATTR
-                               | NFS_INO_INVALID_ACCESS
-                               | NFS_INO_INVALID_ACL
                                | NFS_INO_REVAL_FORCED);
 
        /* Check if our cached file size is stale */
@@ -1466,12 +1462,6 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
                nfsi->cache_validity |= invalid;
 
        return 0;
- out_changed:
-       /*
-        * Big trouble! The inode has become a different object.
-        */
-       printk(KERN_DEBUG "NFS: %s: inode %ld mode changed, %07o to %07o\n",
-                       __func__, inode->i_ino, inode->i_mode, fattr->mode);
  out_err:
        /*
         * No need to worry about unhashing the dentry, as the
@@ -1480,13 +1470,6 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
         */
        nfs_invalidate_inode(inode);
        return -ESTALE;
-
- out_fileid:
-       printk(KERN_ERR "NFS: server %s error: fileid changed\n"
-               "fsid %s: expected fileid 0x%Lx, got 0x%Lx\n",
-               NFS_SERVER(inode)->nfs_client->cl_hostname, inode->i_sb->s_id,
-               (long long)nfsi->fileid, (long long)fattr->fileid);
-       goto out_err;
 }
 
 
@@ -1547,7 +1530,7 @@ static inline void nfs4_init_once(struct nfs_inode *nfsi)
        nfsi->delegation_state = 0;
        init_rwsem(&nfsi->rwsem);
        nfsi->layout = NULL;
-       atomic_set(&nfsi->commits_outstanding, 0);
+       atomic_set(&nfsi->commit_info.rpcs_out, 0);
 #endif
 }
 
@@ -1559,9 +1542,9 @@ static void init_once(void *foo)
        INIT_LIST_HEAD(&nfsi->open_files);
        INIT_LIST_HEAD(&nfsi->access_cache_entry_lru);
        INIT_LIST_HEAD(&nfsi->access_cache_inode_lru);
-       INIT_LIST_HEAD(&nfsi->commit_list);
+       INIT_LIST_HEAD(&nfsi->commit_info.list);
        nfsi->npages = 0;
-       nfsi->ncommit = 0;
+       nfsi->commit_info.ncommit = 0;
        atomic_set(&nfsi->silly_count, 1);
        INIT_HLIST_HEAD(&nfsi->silly_list);
        init_waitqueue_head(&nfsi->waitqueue);
index b777bdaba4c52e72ee86a1d6c1e67ec381a37788..18f99ef7134387128507e8ffd93fff6943b241d6 100644 (file)
@@ -103,6 +103,7 @@ struct nfs_parsed_mount_data {
        unsigned int            version;
        unsigned int            minorversion;
        char                    *fscache_uniq;
+       bool                    need_mount;
 
        struct {
                struct sockaddr_storage address;
@@ -167,11 +168,13 @@ extern struct nfs_server *nfs_clone_server(struct nfs_server *,
                                           struct nfs_fh *,
                                           struct nfs_fattr *,
                                           rpc_authflavor_t);
+extern int nfs_wait_client_init_complete(const struct nfs_client *clp);
 extern void nfs_mark_client_ready(struct nfs_client *clp, int state);
-extern int nfs4_check_client_ready(struct nfs_client *clp);
 extern struct nfs_client *nfs4_set_ds_client(struct nfs_client* mds_clp,
                                             const struct sockaddr *ds_addr,
-                                            int ds_addrlen, int ds_proto);
+                                            int ds_addrlen, int ds_proto,
+                                            unsigned int ds_timeo,
+                                            unsigned int ds_retrans);
 #ifdef CONFIG_PROC_FS
 extern int __init nfs_fs_proc_init(void);
 extern void nfs_fs_proc_exit(void);
@@ -185,21 +188,11 @@ static inline void nfs_fs_proc_exit(void)
 }
 #endif
 
-/* nfs4namespace.c */
-#ifdef CONFIG_NFS_V4
-extern struct vfsmount *nfs_do_refmount(struct rpc_clnt *client, struct dentry *dentry);
-#else
-static inline
-struct vfsmount *nfs_do_refmount(struct rpc_clnt *client, struct dentry *dentry)
-{
-       return ERR_PTR(-ENOENT);
-}
-#endif
-
 /* callback_xdr.c */
 extern struct svc_version nfs4_callback_version1;
 extern struct svc_version nfs4_callback_version4;
 
+struct nfs_pageio_descriptor;
 /* pagelist.c */
 extern int __init nfs_init_nfspagecache(void);
 extern void nfs_destroy_nfspagecache(void);
@@ -210,9 +203,13 @@ extern void nfs_destroy_writepagecache(void);
 
 extern int __init nfs_init_directcache(void);
 extern void nfs_destroy_directcache(void);
+extern bool nfs_pgarray_set(struct nfs_page_array *p, unsigned int pagecount);
+extern void nfs_pgheader_init(struct nfs_pageio_descriptor *desc,
+                             struct nfs_pgio_header *hdr,
+                             void (*release)(struct nfs_pgio_header *hdr));
+void nfs_set_pgio_error(struct nfs_pgio_header *hdr, int error, loff_t pos);
 
 /* nfs2xdr.c */
-extern int nfs_stat_to_errno(enum nfs_stat);
 extern struct rpc_procinfo nfs_procedures[];
 extern int nfs2_decode_dirent(struct xdr_stream *,
                                struct nfs_entry *, int);
@@ -237,14 +234,13 @@ extern const u32 nfs41_maxwrite_overhead;
 extern struct rpc_procinfo nfs4_procedures[];
 #endif
 
-extern int nfs4_init_ds_session(struct nfs_client *clp);
+extern int nfs4_init_ds_session(struct nfs_client *, unsigned long);
 
 /* proc.c */
 void nfs_close_context(struct nfs_open_context *ctx, int is_sync);
-extern int nfs_init_client(struct nfs_client *clp,
+extern struct nfs_client *nfs_init_client(struct nfs_client *clp,
                           const struct rpc_timeout *timeparms,
-                          const char *ip_addr, rpc_authflavor_t authflavour,
-                          int noresvport);
+                          const char *ip_addr, rpc_authflavor_t authflavour);
 
 /* dir.c */
 extern int nfs_access_cache_shrinker(struct shrinker *shrink,
@@ -280,9 +276,10 @@ extern void nfs_sb_deactive(struct super_block *sb);
 extern char *nfs_path(char **p, struct dentry *dentry,
                      char *buffer, ssize_t buflen);
 extern struct vfsmount *nfs_d_automount(struct path *path);
-#ifdef CONFIG_NFS_V4
-rpc_authflavor_t nfs_find_best_sec(struct nfs4_secinfo_flavors *);
-#endif
+struct vfsmount *nfs_submount(struct nfs_server *, struct dentry *,
+                             struct nfs_fh *, struct nfs_fattr *);
+struct vfsmount *nfs_do_submount(struct dentry *, struct nfs_fh *,
+                                struct nfs_fattr *, rpc_authflavor_t);
 
 /* getroot.c */
 extern struct dentry *nfs_get_root(struct super_block *, struct nfs_fh *,
@@ -294,46 +291,73 @@ extern struct dentry *nfs4_get_root(struct super_block *, struct nfs_fh *,
 extern int nfs4_get_rootfh(struct nfs_server *server, struct nfs_fh *mntfh);
 #endif
 
-struct nfs_pageio_descriptor;
+struct nfs_pgio_completion_ops;
 /* read.c */
-extern int nfs_initiate_read(struct nfs_read_data *data, struct rpc_clnt *clnt,
-                            const struct rpc_call_ops *call_ops);
+extern struct nfs_read_header *nfs_readhdr_alloc(void);
+extern void nfs_readhdr_free(struct nfs_pgio_header *hdr);
+extern void nfs_pageio_init_read(struct nfs_pageio_descriptor *pgio,
+                       struct inode *inode,
+                       const struct nfs_pgio_completion_ops *compl_ops);
+extern int nfs_initiate_read(struct rpc_clnt *clnt,
+                            struct nfs_read_data *data,
+                            const struct rpc_call_ops *call_ops, int flags);
 extern void nfs_read_prepare(struct rpc_task *task, void *calldata);
 extern int nfs_generic_pagein(struct nfs_pageio_descriptor *desc,
-               struct list_head *head);
-
+                             struct nfs_pgio_header *hdr);
 extern void nfs_pageio_init_read_mds(struct nfs_pageio_descriptor *pgio,
-               struct inode *inode);
+                       struct inode *inode,
+                       const struct nfs_pgio_completion_ops *compl_ops);
 extern void nfs_pageio_reset_read_mds(struct nfs_pageio_descriptor *pgio);
 extern void nfs_readdata_release(struct nfs_read_data *rdata);
 
 /* write.c */
+extern void nfs_pageio_init_write(struct nfs_pageio_descriptor *pgio,
+                       struct inode *inode, int ioflags,
+                       const struct nfs_pgio_completion_ops *compl_ops);
+extern struct nfs_write_header *nfs_writehdr_alloc(void);
+extern void nfs_writehdr_free(struct nfs_pgio_header *hdr);
 extern int nfs_generic_flush(struct nfs_pageio_descriptor *desc,
-               struct list_head *head);
+                            struct nfs_pgio_header *hdr);
 extern void nfs_pageio_init_write_mds(struct nfs_pageio_descriptor *pgio,
-                                 struct inode *inode, int ioflags);
+                       struct inode *inode, int ioflags,
+                       const struct nfs_pgio_completion_ops *compl_ops);
 extern void nfs_pageio_reset_write_mds(struct nfs_pageio_descriptor *pgio);
 extern void nfs_writedata_release(struct nfs_write_data *wdata);
-extern void nfs_commit_free(struct nfs_write_data *p);
-extern int nfs_initiate_write(struct nfs_write_data *data,
-                             struct rpc_clnt *clnt,
+extern void nfs_commit_free(struct nfs_commit_data *p);
+extern int nfs_initiate_write(struct rpc_clnt *clnt,
+                             struct nfs_write_data *data,
                              const struct rpc_call_ops *call_ops,
-                             int how);
+                             int how, int flags);
 extern void nfs_write_prepare(struct rpc_task *task, void *calldata);
-extern int nfs_initiate_commit(struct nfs_write_data *data,
-                              struct rpc_clnt *clnt,
+extern void nfs_commit_prepare(struct rpc_task *task, void *calldata);
+extern int nfs_initiate_commit(struct rpc_clnt *clnt,
+                              struct nfs_commit_data *data,
                               const struct rpc_call_ops *call_ops,
-                              int how);
-extern void nfs_init_commit(struct nfs_write_data *data,
+                              int how, int flags);
+extern void nfs_init_commit(struct nfs_commit_data *data,
                            struct list_head *head,
-                           struct pnfs_layout_segment *lseg);
+                           struct pnfs_layout_segment *lseg,
+                           struct nfs_commit_info *cinfo);
+int nfs_scan_commit_list(struct list_head *src, struct list_head *dst,
+                        struct nfs_commit_info *cinfo, int max);
+int nfs_scan_commit(struct inode *inode, struct list_head *dst,
+                   struct nfs_commit_info *cinfo);
+void nfs_mark_request_commit(struct nfs_page *req,
+                            struct pnfs_layout_segment *lseg,
+                            struct nfs_commit_info *cinfo);
+int nfs_generic_commit_list(struct inode *inode, struct list_head *head,
+                           int how, struct nfs_commit_info *cinfo);
 void nfs_retry_commit(struct list_head *page_list,
-                     struct pnfs_layout_segment *lseg);
-void nfs_commit_clear_lock(struct nfs_inode *nfsi);
-void nfs_commitdata_release(void *data);
-void nfs_commit_release_pages(struct nfs_write_data *data);
-void nfs_request_add_commit_list(struct nfs_page *req, struct list_head *head);
-void nfs_request_remove_commit_list(struct nfs_page *req);
+                     struct pnfs_layout_segment *lseg,
+                     struct nfs_commit_info *cinfo);
+void nfs_commitdata_release(struct nfs_commit_data *data);
+void nfs_request_add_commit_list(struct nfs_page *req, struct list_head *dst,
+                                struct nfs_commit_info *cinfo);
+void nfs_request_remove_commit_list(struct nfs_page *req,
+                                   struct nfs_commit_info *cinfo);
+void nfs_init_cinfo(struct nfs_commit_info *cinfo,
+                   struct inode *inode,
+                   struct nfs_direct_req *dreq);
 
 #ifdef CONFIG_MIGRATION
 extern int nfs_migrate_page(struct address_space *,
@@ -342,15 +366,20 @@ extern int nfs_migrate_page(struct address_space *,
 #define nfs_migrate_page NULL
 #endif
 
+/* direct.c */
+void nfs_init_cinfo_from_dreq(struct nfs_commit_info *cinfo,
+                             struct nfs_direct_req *dreq);
+static inline void nfs_inode_dio_wait(struct inode *inode)
+{
+       inode_dio_wait(inode);
+}
+
 /* nfs4proc.c */
 extern void __nfs4_read_done_cb(struct nfs_read_data *);
-extern void nfs4_reset_read(struct rpc_task *task, struct nfs_read_data *data);
-extern int nfs4_init_client(struct nfs_client *clp,
+extern struct nfs_client *nfs4_init_client(struct nfs_client *clp,
                            const struct rpc_timeout *timeparms,
                            const char *ip_addr,
-                           rpc_authflavor_t authflavour,
-                           int noresvport);
-extern void nfs4_reset_write(struct rpc_task *task, struct nfs_write_data *data);
+                           rpc_authflavor_t authflavour);
 extern int _nfs4_call_sync(struct rpc_clnt *clnt,
                           struct nfs_server *server,
                           struct rpc_message *msg,
@@ -466,3 +495,15 @@ unsigned int nfs_page_array_len(unsigned int base, size_t len)
                PAGE_SIZE - 1) >> PAGE_SHIFT;
 }
 
+/*
+ * Convert a struct timespec into a 64-bit change attribute
+ *
+ * This does approximately the same thing as timespec_to_ns(),
+ * but for calculation efficiency, we multiply the seconds by
+ * 1024*1024*1024.
+ */
+static inline
+u64 nfs_timespec_to_change_attr(const struct timespec *ts)
+{
+       return ((u64)ts->tv_sec << 30) + ts->tv_nsec;
+}
index d51868e5683c0b34530c9db38a0cd4c26277c0b8..08b9c93675da512e0ef8174a25e1e048f282b527 100644 (file)
@@ -26,11 +26,6 @@ static LIST_HEAD(nfs_automount_list);
 static DECLARE_DELAYED_WORK(nfs_automount_task, nfs_expire_automounts);
 int nfs_mountpoint_expiry_timeout = 500 * HZ;
 
-static struct vfsmount *nfs_do_submount(struct dentry *dentry,
-                                       struct nfs_fh *fh,
-                                       struct nfs_fattr *fattr,
-                                       rpc_authflavor_t authflavor);
-
 /*
  * nfs_path - reconstruct the path given an arbitrary dentry
  * @base - used to return pointer to the end of devname part of path
@@ -118,64 +113,6 @@ Elong:
        return ERR_PTR(-ENAMETOOLONG);
 }
 
-#ifdef CONFIG_NFS_V4
-rpc_authflavor_t nfs_find_best_sec(struct nfs4_secinfo_flavors *flavors)
-{
-       struct gss_api_mech *mech;
-       struct xdr_netobj oid;
-       int i;
-       rpc_authflavor_t pseudoflavor = RPC_AUTH_UNIX;
-
-       for (i = 0; i < flavors->num_flavors; i++) {
-               struct nfs4_secinfo_flavor *flavor;
-               flavor = &flavors->flavors[i];
-
-               if (flavor->flavor == RPC_AUTH_NULL || flavor->flavor == RPC_AUTH_UNIX) {
-                       pseudoflavor = flavor->flavor;
-                       break;
-               } else if (flavor->flavor == RPC_AUTH_GSS) {
-                       oid.len  = flavor->gss.sec_oid4.len;
-                       oid.data = flavor->gss.sec_oid4.data;
-                       mech = gss_mech_get_by_OID(&oid);
-                       if (!mech)
-                               continue;
-                       pseudoflavor = gss_svc_to_pseudoflavor(mech, flavor->gss.service);
-                       gss_mech_put(mech);
-                       break;
-               }
-       }
-
-       return pseudoflavor;
-}
-
-static struct rpc_clnt *nfs_lookup_mountpoint(struct inode *dir,
-                                             struct qstr *name,
-                                             struct nfs_fh *fh,
-                                             struct nfs_fattr *fattr)
-{
-       int err;
-
-       if (NFS_PROTO(dir)->version == 4)
-               return nfs4_proc_lookup_mountpoint(dir, name, fh, fattr);
-
-       err = NFS_PROTO(dir)->lookup(NFS_SERVER(dir)->client, dir, name, fh, fattr);
-       if (err)
-               return ERR_PTR(err);
-       return rpc_clone_client(NFS_SERVER(dir)->client);
-}
-#else /* CONFIG_NFS_V4 */
-static inline struct rpc_clnt *nfs_lookup_mountpoint(struct inode *dir,
-                                                    struct qstr *name,
-                                                    struct nfs_fh *fh,
-                                                    struct nfs_fattr *fattr)
-{
-       int err = NFS_PROTO(dir)->lookup(NFS_SERVER(dir)->client, dir, name, fh, fattr);
-       if (err)
-               return ERR_PTR(err);
-       return rpc_clone_client(NFS_SERVER(dir)->client);
-}
-#endif /* CONFIG_NFS_V4 */
-
 /*
  * nfs_d_automount - Handle crossing a mountpoint on the server
  * @path - The mountpoint
@@ -191,10 +128,9 @@ static inline struct rpc_clnt *nfs_lookup_mountpoint(struct inode *dir,
 struct vfsmount *nfs_d_automount(struct path *path)
 {
        struct vfsmount *mnt;
-       struct dentry *parent;
+       struct nfs_server *server = NFS_SERVER(path->dentry->d_inode);
        struct nfs_fh *fh = NULL;
        struct nfs_fattr *fattr = NULL;
-       struct rpc_clnt *client;
 
        dprintk("--> nfs_d_automount()\n");
 
@@ -210,21 +146,7 @@ struct vfsmount *nfs_d_automount(struct path *path)
 
        dprintk("%s: enter\n", __func__);
 
-       /* Look it up again to get its attributes */
-       parent = dget_parent(path->dentry);
-       client = nfs_lookup_mountpoint(parent->d_inode, &path->dentry->d_name, fh, fattr);
-       dput(parent);
-       if (IS_ERR(client)) {
-               mnt = ERR_CAST(client);
-               goto out;
-       }
-
-       if (fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL)
-               mnt = nfs_do_refmount(client, path->dentry);
-       else
-               mnt = nfs_do_submount(path->dentry, fh, fattr, client->cl_auth->au_flavor);
-       rpc_shutdown_client(client);
-
+       mnt = server->nfs_client->rpc_ops->submount(server, path->dentry, fh, fattr);
        if (IS_ERR(mnt))
                goto out;
 
@@ -297,10 +219,8 @@ static struct vfsmount *nfs_do_clone_mount(struct nfs_server *server,
  * @authflavor - security flavor to use when performing the mount
  *
  */
-static struct vfsmount *nfs_do_submount(struct dentry *dentry,
-                                       struct nfs_fh *fh,
-                                       struct nfs_fattr *fattr,
-                                       rpc_authflavor_t authflavor)
+struct vfsmount *nfs_do_submount(struct dentry *dentry, struct nfs_fh *fh,
+                                struct nfs_fattr *fattr, rpc_authflavor_t authflavor)
 {
        struct nfs_clone_mount mountdata = {
                .sb = dentry->d_sb,
@@ -333,3 +253,18 @@ out:
        dprintk("<-- nfs_do_submount() = %p\n", mnt);
        return mnt;
 }
+
+struct vfsmount *nfs_submount(struct nfs_server *server, struct dentry *dentry,
+                             struct nfs_fh *fh, struct nfs_fattr *fattr)
+{
+       int err;
+       struct dentry *parent = dget_parent(dentry);
+
+       /* Look it up again to get its attributes */
+       err = server->nfs_client->rpc_ops->lookup(parent->d_inode, &dentry->d_name, fh, fattr);
+       dput(parent);
+       if (err != 0)
+               return ERR_PTR(err);
+
+       return nfs_do_submount(dentry, fh, fattr, server->client->cl_auth->au_flavor);
+}
index aa14ec303e9408a7111bf2cf5ceffeda7703d17e..8a6394edb8b015375eb26016594a09e7b275261f 100644 (file)
@@ -1,3 +1,7 @@
+/*
+ * NFS-private data for each "struct net".  Accessed with net_generic().
+ */
+
 #ifndef __NFS_NETNS_H__
 #define __NFS_NETNS_H__
 
@@ -20,6 +24,7 @@ struct nfs_net {
        struct idr cb_ident_idr; /* Protected by nfs_client_lock */
 #endif
        spinlock_t nfs_client_lock;
+       struct timespec boot_time;
 };
 
 extern int nfs_net_id;
index 1f56000fabbdc1b1283961e340661e20e986e3bf..baf759bccd054d562d24d9adf43af18915da8267 100644 (file)
@@ -61,6 +61,7 @@
 #define NFS_readdirres_sz      (1)
 #define NFS_statfsres_sz       (1+NFS_info_sz)
 
+static int nfs_stat_to_errno(enum nfs_stat);
 
 /*
  * While encoding arguments, set up the reply buffer in advance to
@@ -313,6 +314,8 @@ static int decode_fattr(struct xdr_stream *xdr, struct nfs_fattr *fattr)
        p = xdr_decode_time(p, &fattr->atime);
        p = xdr_decode_time(p, &fattr->mtime);
        xdr_decode_time(p, &fattr->ctime);
+       fattr->change_attr = nfs_timespec_to_change_attr(&fattr->ctime);
+
        return 0;
 out_overflow:
        print_overflow_msg(__func__, xdr);
@@ -1109,7 +1112,7 @@ static const struct {
  * Returns a local errno value, or -EIO if the NFS status code is
  * not recognized.  This function is used jointly by NFSv2 and NFSv3.
  */
-int nfs_stat_to_errno(enum nfs_stat status)
+static int nfs_stat_to_errno(enum nfs_stat status)
 {
        int i;
 
index 75c68299358e226e805e861e4ac8ffc81d96ab4f..2292a0fd2bffd3b042b43e9dc4b607acbd0eb6ad 100644 (file)
@@ -142,7 +142,7 @@ nfs3_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
 }
 
 static int
-nfs3_proc_lookup(struct rpc_clnt *clnt, struct inode *dir, struct qstr *name,
+nfs3_proc_lookup(struct inode *dir, struct qstr *name,
                 struct nfs_fh *fhandle, struct nfs_fattr *fattr)
 {
        struct nfs3_diropargs   arg = {
@@ -810,11 +810,13 @@ nfs3_proc_pathconf(struct nfs_server *server, struct nfs_fh *fhandle,
 
 static int nfs3_read_done(struct rpc_task *task, struct nfs_read_data *data)
 {
-       if (nfs3_async_handle_jukebox(task, data->inode))
+       struct inode *inode = data->header->inode;
+
+       if (nfs3_async_handle_jukebox(task, inode))
                return -EAGAIN;
 
-       nfs_invalidate_atime(data->inode);
-       nfs_refresh_inode(data->inode, &data->fattr);
+       nfs_invalidate_atime(inode);
+       nfs_refresh_inode(inode, &data->fattr);
        return 0;
 }
 
@@ -830,10 +832,12 @@ static void nfs3_proc_read_rpc_prepare(struct rpc_task *task, struct nfs_read_da
 
 static int nfs3_write_done(struct rpc_task *task, struct nfs_write_data *data)
 {
-       if (nfs3_async_handle_jukebox(task, data->inode))
+       struct inode *inode = data->header->inode;
+
+       if (nfs3_async_handle_jukebox(task, inode))
                return -EAGAIN;
        if (task->tk_status >= 0)
-               nfs_post_op_update_inode_force_wcc(data->inode, data->res.fattr);
+               nfs_post_op_update_inode_force_wcc(inode, data->res.fattr);
        return 0;
 }
 
@@ -847,7 +851,12 @@ static void nfs3_proc_write_rpc_prepare(struct rpc_task *task, struct nfs_write_
        rpc_call_start(task);
 }
 
-static int nfs3_commit_done(struct rpc_task *task, struct nfs_write_data *data)
+static void nfs3_proc_commit_rpc_prepare(struct rpc_task *task, struct nfs_commit_data *data)
+{
+       rpc_call_start(task);
+}
+
+static int nfs3_commit_done(struct rpc_task *task, struct nfs_commit_data *data)
 {
        if (nfs3_async_handle_jukebox(task, data->inode))
                return -EAGAIN;
@@ -855,7 +864,7 @@ static int nfs3_commit_done(struct rpc_task *task, struct nfs_write_data *data)
        return 0;
 }
 
-static void nfs3_proc_commit_setup(struct nfs_write_data *data, struct rpc_message *msg)
+static void nfs3_proc_commit_setup(struct nfs_commit_data *data, struct rpc_message *msg)
 {
        msg->rpc_proc = &nfs3_procedures[NFS3PROC_COMMIT];
 }
@@ -875,6 +884,7 @@ const struct nfs_rpc_ops nfs_v3_clientops = {
        .file_inode_ops = &nfs3_file_inode_operations,
        .file_ops       = &nfs_file_operations,
        .getroot        = nfs3_proc_get_root,
+       .submount       = nfs_submount,
        .getattr        = nfs3_proc_getattr,
        .setattr        = nfs3_proc_setattr,
        .lookup         = nfs3_proc_lookup,
@@ -906,6 +916,7 @@ const struct nfs_rpc_ops nfs_v3_clientops = {
        .write_rpc_prepare = nfs3_proc_write_rpc_prepare,
        .write_done     = nfs3_write_done,
        .commit_setup   = nfs3_proc_commit_setup,
+       .commit_rpc_prepare = nfs3_proc_commit_rpc_prepare,
        .commit_done    = nfs3_commit_done,
        .lock           = nfs3_proc_lock,
        .clear_acl_cache = nfs3_forget_cached_acls,
index a77cc9a3ce5561f1d8b23e78bb16ac49fcbf14b4..902de489ec9bac793dd2e3fa65b663262879b271 100644 (file)
@@ -86,6 +86,8 @@
                                XDR_QUADLEN(NFS_ACL_INLINE_BUFSIZE))
 #define ACL3_setaclres_sz      (1+NFS3_post_op_attr_sz)
 
+static int nfs3_stat_to_errno(enum nfs_stat);
+
 /*
  * Map file type to S_IFMT bits
  */
@@ -675,6 +677,7 @@ static int decode_fattr3(struct xdr_stream *xdr, struct nfs_fattr *fattr)
        p = xdr_decode_nfstime3(p, &fattr->atime);
        p = xdr_decode_nfstime3(p, &fattr->mtime);
        xdr_decode_nfstime3(p, &fattr->ctime);
+       fattr->change_attr = nfs_timespec_to_change_attr(&fattr->ctime);
 
        fattr->valid |= NFS_ATTR_FATTR_V3;
        return 0;
@@ -725,12 +728,14 @@ static int decode_wcc_attr(struct xdr_stream *xdr, struct nfs_fattr *fattr)
                goto out_overflow;
 
        fattr->valid |= NFS_ATTR_FATTR_PRESIZE
+               | NFS_ATTR_FATTR_PRECHANGE
                | NFS_ATTR_FATTR_PREMTIME
                | NFS_ATTR_FATTR_PRECTIME;
 
        p = xdr_decode_size3(p, &fattr->pre_size);
        p = xdr_decode_nfstime3(p, &fattr->pre_mtime);
        xdr_decode_nfstime3(p, &fattr->pre_ctime);
+       fattr->pre_change_attr = nfs_timespec_to_change_attr(&fattr->pre_ctime);
 
        return 0;
 out_overflow:
@@ -1287,7 +1292,7 @@ static void nfs3_xdr_enc_readdirplus3args(struct rpc_rqst *req,
  *     };
  */
 static void encode_commit3args(struct xdr_stream *xdr,
-                              const struct nfs_writeargs *args)
+                              const struct nfs_commitargs *args)
 {
        __be32 *p;
 
@@ -1300,7 +1305,7 @@ static void encode_commit3args(struct xdr_stream *xdr,
 
 static void nfs3_xdr_enc_commit3args(struct rpc_rqst *req,
                                     struct xdr_stream *xdr,
-                                    const struct nfs_writeargs *args)
+                                    const struct nfs_commitargs *args)
 {
        encode_commit3args(xdr, args);
 }
@@ -1385,7 +1390,7 @@ static int nfs3_xdr_dec_getattr3res(struct rpc_rqst *req,
 out:
        return error;
 out_default:
-       return nfs_stat_to_errno(status);
+       return nfs3_stat_to_errno(status);
 }
 
 /*
@@ -1424,7 +1429,7 @@ static int nfs3_xdr_dec_setattr3res(struct rpc_rqst *req,
 out:
        return error;
 out_status:
-       return nfs_stat_to_errno(status);
+       return nfs3_stat_to_errno(status);
 }
 
 /*
@@ -1472,7 +1477,7 @@ out_default:
        error = decode_post_op_attr(xdr, result->dir_attr);
        if (unlikely(error))
                goto out;
-       return nfs_stat_to_errno(status);
+       return nfs3_stat_to_errno(status);
 }
 
 /*
@@ -1513,7 +1518,7 @@ static int nfs3_xdr_dec_access3res(struct rpc_rqst *req,
 out:
        return error;
 out_default:
-       return nfs_stat_to_errno(status);
+       return nfs3_stat_to_errno(status);
 }
 
 /*
@@ -1554,7 +1559,7 @@ static int nfs3_xdr_dec_readlink3res(struct rpc_rqst *req,
 out:
        return error;
 out_default:
-       return nfs_stat_to_errno(status);
+       return nfs3_stat_to_errno(status);
 }
 
 /*
@@ -1636,7 +1641,7 @@ static int nfs3_xdr_dec_read3res(struct rpc_rqst *req, struct xdr_stream *xdr,
 out:
        return error;
 out_status:
-       return nfs_stat_to_errno(status);
+       return nfs3_stat_to_errno(status);
 }
 
 /*
@@ -1706,7 +1711,7 @@ static int nfs3_xdr_dec_write3res(struct rpc_rqst *req, struct xdr_stream *xdr,
 out:
        return error;
 out_status:
-       return nfs_stat_to_errno(status);
+       return nfs3_stat_to_errno(status);
 }
 
 /*
@@ -1770,7 +1775,7 @@ out_default:
        error = decode_wcc_data(xdr, result->dir_attr);
        if (unlikely(error))
                goto out;
-       return nfs_stat_to_errno(status);
+       return nfs3_stat_to_errno(status);
 }
 
 /*
@@ -1809,7 +1814,7 @@ static int nfs3_xdr_dec_remove3res(struct rpc_rqst *req,
 out:
        return error;
 out_status:
-       return nfs_stat_to_errno(status);
+       return nfs3_stat_to_errno(status);
 }
 
 /*
@@ -1853,7 +1858,7 @@ static int nfs3_xdr_dec_rename3res(struct rpc_rqst *req,
 out:
        return error;
 out_status:
-       return nfs_stat_to_errno(status);
+       return nfs3_stat_to_errno(status);
 }
 
 /*
@@ -1896,7 +1901,7 @@ static int nfs3_xdr_dec_link3res(struct rpc_rqst *req, struct xdr_stream *xdr,
 out:
        return error;
 out_status:
-       return nfs_stat_to_errno(status);
+       return nfs3_stat_to_errno(status);
 }
 
 /**
@@ -2088,7 +2093,7 @@ out_default:
        error = decode_post_op_attr(xdr, result->dir_attr);
        if (unlikely(error))
                goto out;
-       return nfs_stat_to_errno(status);
+       return nfs3_stat_to_errno(status);
 }
 
 /*
@@ -2156,7 +2161,7 @@ static int nfs3_xdr_dec_fsstat3res(struct rpc_rqst *req,
 out:
        return error;
 out_status:
-       return nfs_stat_to_errno(status);
+       return nfs3_stat_to_errno(status);
 }
 
 /*
@@ -2232,7 +2237,7 @@ static int nfs3_xdr_dec_fsinfo3res(struct rpc_rqst *req,
 out:
        return error;
 out_status:
-       return nfs_stat_to_errno(status);
+       return nfs3_stat_to_errno(status);
 }
 
 /*
@@ -2295,7 +2300,7 @@ static int nfs3_xdr_dec_pathconf3res(struct rpc_rqst *req,
 out:
        return error;
 out_status:
-       return nfs_stat_to_errno(status);
+       return nfs3_stat_to_errno(status);
 }
 
 /*
@@ -2319,7 +2324,7 @@ out_status:
  */
 static int nfs3_xdr_dec_commit3res(struct rpc_rqst *req,
                                   struct xdr_stream *xdr,
-                                  struct nfs_writeres *result)
+                                  struct nfs_commitres *result)
 {
        enum nfs_stat status;
        int error;
@@ -2336,7 +2341,7 @@ static int nfs3_xdr_dec_commit3res(struct rpc_rqst *req,
 out:
        return error;
 out_status:
-       return nfs_stat_to_errno(status);
+       return nfs3_stat_to_errno(status);
 }
 
 #ifdef CONFIG_NFS_V3_ACL
@@ -2401,7 +2406,7 @@ static int nfs3_xdr_dec_getacl3res(struct rpc_rqst *req,
 out:
        return error;
 out_default:
-       return nfs_stat_to_errno(status);
+       return nfs3_stat_to_errno(status);
 }
 
 static int nfs3_xdr_dec_setacl3res(struct rpc_rqst *req,
@@ -2420,11 +2425,76 @@ static int nfs3_xdr_dec_setacl3res(struct rpc_rqst *req,
 out:
        return error;
 out_default:
-       return nfs_stat_to_errno(status);
+       return nfs3_stat_to_errno(status);
 }
 
 #endif  /* CONFIG_NFS_V3_ACL */
 
+
+/*
+ * We need to translate between nfs status return values and
+ * the local errno values which may not be the same.
+ */
+static const struct {
+       int stat;
+       int errno;
+} nfs_errtbl[] = {
+       { NFS_OK,               0               },
+       { NFSERR_PERM,          -EPERM          },
+       { NFSERR_NOENT,         -ENOENT         },
+       { NFSERR_IO,            -errno_NFSERR_IO},
+       { NFSERR_NXIO,          -ENXIO          },
+/*     { NFSERR_EAGAIN,        -EAGAIN         }, */
+       { NFSERR_ACCES,         -EACCES         },
+       { NFSERR_EXIST,         -EEXIST         },
+       { NFSERR_XDEV,          -EXDEV          },
+       { NFSERR_NODEV,         -ENODEV         },
+       { NFSERR_NOTDIR,        -ENOTDIR        },
+       { NFSERR_ISDIR,         -EISDIR         },
+       { NFSERR_INVAL,         -EINVAL         },
+       { NFSERR_FBIG,          -EFBIG          },
+       { NFSERR_NOSPC,         -ENOSPC         },
+       { NFSERR_ROFS,          -EROFS          },
+       { NFSERR_MLINK,         -EMLINK         },
+       { NFSERR_NAMETOOLONG,   -ENAMETOOLONG   },
+       { NFSERR_NOTEMPTY,      -ENOTEMPTY      },
+       { NFSERR_DQUOT,         -EDQUOT         },
+       { NFSERR_STALE,         -ESTALE         },
+       { NFSERR_REMOTE,        -EREMOTE        },
+#ifdef EWFLUSH
+       { NFSERR_WFLUSH,        -EWFLUSH        },
+#endif
+       { NFSERR_BADHANDLE,     -EBADHANDLE     },
+       { NFSERR_NOT_SYNC,      -ENOTSYNC       },
+       { NFSERR_BAD_COOKIE,    -EBADCOOKIE     },
+       { NFSERR_NOTSUPP,       -ENOTSUPP       },
+       { NFSERR_TOOSMALL,      -ETOOSMALL      },
+       { NFSERR_SERVERFAULT,   -EREMOTEIO      },
+       { NFSERR_BADTYPE,       -EBADTYPE       },
+       { NFSERR_JUKEBOX,       -EJUKEBOX       },
+       { -1,                   -EIO            }
+};
+
+/**
+ * nfs3_stat_to_errno - convert an NFS status code to a local errno
+ * @status: NFS status code to convert
+ *
+ * Returns a local errno value, or -EIO if the NFS status code is
+ * not recognized.  This function is used jointly by NFSv2 and NFSv3.
+ */
+static int nfs3_stat_to_errno(enum nfs_stat status)
+{
+       int i;
+
+       for (i = 0; nfs_errtbl[i].stat != -1; i++) {
+               if (nfs_errtbl[i].stat == (int)status)
+                       return nfs_errtbl[i].errno;
+       }
+       dprintk("NFS: Unrecognized nfs status value: %u\n", status);
+       return nfs_errtbl[i].errno;
+}
+
+
 #define PROC(proc, argtype, restype, timer)                            \
 [NFS3PROC_##proc] = {                                                  \
        .p_proc      = NFS3PROC_##proc,                                 \
index 8d75021020b31f44f0fcb9ec6f1ff05a8b39b313..c6827f93ab57caeab4e613e97919c04a51aa5d64 100644 (file)
@@ -24,6 +24,8 @@ enum nfs4_client_state {
        NFS4CLNT_RECALL_SLOT,
        NFS4CLNT_LEASE_CONFIRM,
        NFS4CLNT_SERVER_SCOPE_MISMATCH,
+       NFS4CLNT_PURGE_STATE,
+       NFS4CLNT_BIND_CONN_TO_SESSION,
 };
 
 enum nfs4_session_state {
@@ -52,11 +54,6 @@ struct nfs4_minor_version_ops {
        const struct nfs4_state_maintenance_ops *state_renewal_ops;
 };
 
-struct nfs_unique_id {
-       struct rb_node rb_node;
-       __u64 id;
-};
-
 #define NFS_SEQID_CONFIRMED 1
 struct nfs_seqid_counter {
        ktime_t create_time;
@@ -206,12 +203,18 @@ extern const struct dentry_operations nfs4_dentry_operations;
 extern const struct inode_operations nfs4_dir_inode_operations;
 
 /* nfs4namespace.c */
+rpc_authflavor_t nfs_find_best_sec(struct nfs4_secinfo_flavors *);
 struct rpc_clnt *nfs4_create_sec_client(struct rpc_clnt *, struct inode *, struct qstr *);
+struct vfsmount *nfs4_submount(struct nfs_server *, struct dentry *,
+                              struct nfs_fh *, struct nfs_fattr *);
 
 /* nfs4proc.c */
 extern int nfs4_proc_setclientid(struct nfs_client *, u32, unsigned short, struct rpc_cred *, struct nfs4_setclientid_res *);
 extern int nfs4_proc_setclientid_confirm(struct nfs_client *, struct nfs4_setclientid_res *arg, struct rpc_cred *);
+extern int nfs4_proc_get_rootfh(struct nfs_server *, struct nfs_fh *, struct nfs_fsinfo *);
+extern int nfs4_proc_bind_conn_to_session(struct nfs_client *, struct rpc_cred *cred);
 extern int nfs4_proc_exchange_id(struct nfs_client *clp, struct rpc_cred *cred);
+extern int nfs4_destroy_clientid(struct nfs_client *clp);
 extern int nfs4_init_clientid(struct nfs_client *, struct rpc_cred *);
 extern int nfs41_init_clientid(struct nfs_client *, struct rpc_cred *);
 extern int nfs4_do_close(struct nfs4_state *state, gfp_t gfp_mask, int wait, bool roc);
@@ -239,8 +242,8 @@ extern int nfs41_setup_sequence(struct nfs4_session *session,
                struct rpc_task *task);
 extern void nfs4_destroy_session(struct nfs4_session *session);
 extern struct nfs4_session *nfs4_alloc_session(struct nfs_client *clp);
-extern int nfs4_proc_create_session(struct nfs_client *);
-extern int nfs4_proc_destroy_session(struct nfs4_session *);
+extern int nfs4_proc_create_session(struct nfs_client *, struct rpc_cred *);
+extern int nfs4_proc_destroy_session(struct nfs4_session *, struct rpc_cred *);
 extern int nfs4_init_session(struct nfs_server *server);
 extern int nfs4_proc_get_lease_time(struct nfs_client *clp,
                struct nfs_fsinfo *fsinfo);
@@ -310,9 +313,9 @@ struct rpc_cred *nfs4_get_renew_cred_locked(struct nfs_client *clp);
 #if defined(CONFIG_NFS_V4_1)
 struct rpc_cred *nfs4_get_machine_cred_locked(struct nfs_client *clp);
 struct rpc_cred *nfs4_get_exchange_id_cred(struct nfs_client *clp);
-extern void nfs4_schedule_session_recovery(struct nfs4_session *);
+extern void nfs4_schedule_session_recovery(struct nfs4_session *, int);
 #else
-static inline void nfs4_schedule_session_recovery(struct nfs4_session *session)
+static inline void nfs4_schedule_session_recovery(struct nfs4_session *session, int err)
 {
 }
 #endif /* CONFIG_NFS_V4_1 */
@@ -334,7 +337,7 @@ extern void nfs4_schedule_stateid_recovery(const struct nfs_server *, struct nfs
 extern void nfs41_handle_sequence_flag_errors(struct nfs_client *clp, u32 flags);
 extern void nfs41_handle_recall_slot(struct nfs_client *clp);
 extern void nfs41_handle_server_scope(struct nfs_client *,
-                                     struct server_scope **);
+                                     struct nfs41_server_scope **);
 extern void nfs4_put_lock_state(struct nfs4_lock_state *lsp);
 extern int nfs4_set_lock_state(struct nfs4_state *state, struct file_lock *fl);
 extern void nfs4_select_rw_stateid(nfs4_stateid *, struct nfs4_state *,
index 5acfd9ea8a31390eb0f6b3ab6872f8efc334bc2b..e1340293872c7a70e747d051888e5ab603db905e 100644 (file)
@@ -82,29 +82,76 @@ filelayout_get_dserver_offset(struct pnfs_layout_segment *lseg, loff_t offset)
        BUG();
 }
 
+static void filelayout_reset_write(struct nfs_write_data *data)
+{
+       struct nfs_pgio_header *hdr = data->header;
+       struct rpc_task *task = &data->task;
+
+       if (!test_and_set_bit(NFS_IOHDR_REDO, &hdr->flags)) {
+               dprintk("%s Reset task %5u for i/o through MDS "
+                       "(req %s/%lld, %u bytes @ offset %llu)\n", __func__,
+                       data->task.tk_pid,
+                       hdr->inode->i_sb->s_id,
+                       (long long)NFS_FILEID(hdr->inode),
+                       data->args.count,
+                       (unsigned long long)data->args.offset);
+
+               task->tk_status = pnfs_write_done_resend_to_mds(hdr->inode,
+                                                       &hdr->pages,
+                                                       hdr->completion_ops);
+       }
+}
+
+static void filelayout_reset_read(struct nfs_read_data *data)
+{
+       struct nfs_pgio_header *hdr = data->header;
+       struct rpc_task *task = &data->task;
+
+       if (!test_and_set_bit(NFS_IOHDR_REDO, &hdr->flags)) {
+               dprintk("%s Reset task %5u for i/o through MDS "
+                       "(req %s/%lld, %u bytes @ offset %llu)\n", __func__,
+                       data->task.tk_pid,
+                       hdr->inode->i_sb->s_id,
+                       (long long)NFS_FILEID(hdr->inode),
+                       data->args.count,
+                       (unsigned long long)data->args.offset);
+
+               task->tk_status = pnfs_read_done_resend_to_mds(hdr->inode,
+                                                       &hdr->pages,
+                                                       hdr->completion_ops);
+       }
+}
+
 static int filelayout_async_handle_error(struct rpc_task *task,
                                         struct nfs4_state *state,
                                         struct nfs_client *clp,
-                                        int *reset)
+                                        struct pnfs_layout_segment *lseg)
 {
-       struct nfs_server *mds_server = NFS_SERVER(state->inode);
+       struct inode *inode = lseg->pls_layout->plh_inode;
+       struct nfs_server *mds_server = NFS_SERVER(inode);
+       struct nfs4_deviceid_node *devid = FILELAYOUT_DEVID_NODE(lseg);
        struct nfs_client *mds_client = mds_server->nfs_client;
+       struct nfs4_slot_table *tbl = &clp->cl_session->fc_slot_table;
 
        if (task->tk_status >= 0)
                return 0;
-       *reset = 0;
 
        switch (task->tk_status) {
        /* MDS state errors */
        case -NFS4ERR_DELEG_REVOKED:
        case -NFS4ERR_ADMIN_REVOKED:
        case -NFS4ERR_BAD_STATEID:
+               if (state == NULL)
+                       break;
                nfs_remove_bad_delegation(state->inode);
        case -NFS4ERR_OPENMODE:
+               if (state == NULL)
+                       break;
                nfs4_schedule_stateid_recovery(mds_server, state);
                goto wait_on_recovery;
        case -NFS4ERR_EXPIRED:
-               nfs4_schedule_stateid_recovery(mds_server, state);
+               if (state != NULL)
+                       nfs4_schedule_stateid_recovery(mds_server, state);
                nfs4_schedule_lease_recovery(mds_client);
                goto wait_on_recovery;
        /* DS session errors */
@@ -118,7 +165,7 @@ static int filelayout_async_handle_error(struct rpc_task *task,
                dprintk("%s ERROR %d, Reset session. Exchangeid "
                        "flags 0x%x\n", __func__, task->tk_status,
                        clp->cl_exchange_flags);
-               nfs4_schedule_session_recovery(clp->cl_session);
+               nfs4_schedule_session_recovery(clp->cl_session, task->tk_status);
                break;
        case -NFS4ERR_DELAY:
        case -NFS4ERR_GRACE:
@@ -127,11 +174,48 @@ static int filelayout_async_handle_error(struct rpc_task *task,
                break;
        case -NFS4ERR_RETRY_UNCACHED_REP:
                break;
+       /* Invalidate Layout errors */
+       case -NFS4ERR_PNFS_NO_LAYOUT:
+       case -ESTALE:           /* mapped NFS4ERR_STALE */
+       case -EBADHANDLE:       /* mapped NFS4ERR_BADHANDLE */
+       case -EISDIR:           /* mapped NFS4ERR_ISDIR */
+       case -NFS4ERR_FHEXPIRED:
+       case -NFS4ERR_WRONG_TYPE:
+               dprintk("%s Invalid layout error %d\n", __func__,
+                       task->tk_status);
+               /*
+                * Destroy layout so new i/o will get a new layout.
+                * Layout will not be destroyed until all current lseg
+                * references are put. Mark layout as invalid to resend failed
+                * i/o and all i/o waiting on the slot table to the MDS until
+                * layout is destroyed and a new valid layout is obtained.
+                */
+               set_bit(NFS_LAYOUT_INVALID,
+                               &NFS_I(inode)->layout->plh_flags);
+               pnfs_destroy_layout(NFS_I(inode));
+               rpc_wake_up(&tbl->slot_tbl_waitq);
+               goto reset;
+       /* RPC connection errors */
+       case -ECONNREFUSED:
+       case -EHOSTDOWN:
+       case -EHOSTUNREACH:
+       case -ENETUNREACH:
+       case -EIO:
+       case -ETIMEDOUT:
+       case -EPIPE:
+               dprintk("%s DS connection error %d\n", __func__,
+                       task->tk_status);
+               if (!filelayout_test_devid_invalid(devid))
+                       _pnfs_return_layout(inode);
+               filelayout_mark_devid_invalid(devid);
+               rpc_wake_up(&tbl->slot_tbl_waitq);
+               nfs4_ds_disconnect(clp);
+               /* fall through */
        default:
-               dprintk("%s DS error. Retry through MDS %d\n", __func__,
+reset:
+               dprintk("%s Retry through MDS. Error %d\n", __func__,
                        task->tk_status);
-               *reset = 1;
-               break;
+               return -NFS4ERR_RESET_TO_MDS;
        }
 out:
        task->tk_status = 0;
@@ -148,18 +232,17 @@ wait_on_recovery:
 static int filelayout_read_done_cb(struct rpc_task *task,
                                struct nfs_read_data *data)
 {
-       int reset = 0;
+       struct nfs_pgio_header *hdr = data->header;
+       int err;
 
-       dprintk("%s DS read\n", __func__);
+       err = filelayout_async_handle_error(task, data->args.context->state,
+                                           data->ds_clp, hdr->lseg);
 
-       if (filelayout_async_handle_error(task, data->args.context->state,
-                                         data->ds_clp, &reset) == -EAGAIN) {
-               dprintk("%s calling restart ds_clp %p ds_clp->cl_session %p\n",
-                       __func__, data->ds_clp, data->ds_clp->cl_session);
-               if (reset) {
-                       pnfs_set_lo_fail(data->lseg);
-                       nfs4_reset_read(task, data);
-               }
+       switch (err) {
+       case -NFS4ERR_RESET_TO_MDS:
+               filelayout_reset_read(data);
+               return task->tk_status;
+       case -EAGAIN:
                rpc_restart_call_prepare(task);
                return -EAGAIN;
        }
@@ -175,13 +258,15 @@ static int filelayout_read_done_cb(struct rpc_task *task,
 static void
 filelayout_set_layoutcommit(struct nfs_write_data *wdata)
 {
-       if (FILELAYOUT_LSEG(wdata->lseg)->commit_through_mds ||
+       struct nfs_pgio_header *hdr = wdata->header;
+
+       if (FILELAYOUT_LSEG(hdr->lseg)->commit_through_mds ||
            wdata->res.verf->committed == NFS_FILE_SYNC)
                return;
 
        pnfs_set_layoutcommit(wdata);
-       dprintk("%s ionde %lu pls_end_pos %lu\n", __func__, wdata->inode->i_ino,
-               (unsigned long) NFS_I(wdata->inode)->layout->plh_lwb);
+       dprintk("%s ionde %lu pls_end_pos %lu\n", __func__, hdr->inode->i_ino,
+               (unsigned long) NFS_I(hdr->inode)->layout->plh_lwb);
 }
 
 /*
@@ -191,8 +276,14 @@ filelayout_set_layoutcommit(struct nfs_write_data *wdata)
  */
 static void filelayout_read_prepare(struct rpc_task *task, void *data)
 {
-       struct nfs_read_data *rdata = (struct nfs_read_data *)data;
+       struct nfs_read_data *rdata = data;
 
+       if (filelayout_reset_to_mds(rdata->header->lseg)) {
+               dprintk("%s task %u reset io to MDS\n", __func__, task->tk_pid);
+               filelayout_reset_read(rdata);
+               rpc_exit(task, 0);
+               return;
+       }
        rdata->read_done_cb = filelayout_read_done_cb;
 
        if (nfs41_setup_sequence(rdata->ds_clp->cl_session,
@@ -205,42 +296,47 @@ static void filelayout_read_prepare(struct rpc_task *task, void *data)
 
 static void filelayout_read_call_done(struct rpc_task *task, void *data)
 {
-       struct nfs_read_data *rdata = (struct nfs_read_data *)data;
+       struct nfs_read_data *rdata = data;
 
        dprintk("--> %s task->tk_status %d\n", __func__, task->tk_status);
 
+       if (test_bit(NFS_IOHDR_REDO, &rdata->header->flags) &&
+           task->tk_status == 0)
+               return;
+
        /* Note this may cause RPC to be resent */
-       rdata->mds_ops->rpc_call_done(task, data);
+       rdata->header->mds_ops->rpc_call_done(task, data);
 }
 
 static void filelayout_read_count_stats(struct rpc_task *task, void *data)
 {
-       struct nfs_read_data *rdata = (struct nfs_read_data *)data;
+       struct nfs_read_data *rdata = data;
 
-       rpc_count_iostats(task, NFS_SERVER(rdata->inode)->client->cl_metrics);
+       rpc_count_iostats(task, NFS_SERVER(rdata->header->inode)->client->cl_metrics);
 }
 
 static void filelayout_read_release(void *data)
 {
-       struct nfs_read_data *rdata = (struct nfs_read_data *)data;
+       struct nfs_read_data *rdata = data;
 
-       put_lseg(rdata->lseg);
-       rdata->mds_ops->rpc_release(data);
+       nfs_put_client(rdata->ds_clp);
+       rdata->header->mds_ops->rpc_release(data);
 }
 
 static int filelayout_write_done_cb(struct rpc_task *task,
                                struct nfs_write_data *data)
 {
-       int reset = 0;
-
-       if (filelayout_async_handle_error(task, data->args.context->state,
-                                         data->ds_clp, &reset) == -EAGAIN) {
-               dprintk("%s calling restart ds_clp %p ds_clp->cl_session %p\n",
-                       __func__, data->ds_clp, data->ds_clp->cl_session);
-               if (reset) {
-                       pnfs_set_lo_fail(data->lseg);
-                       nfs4_reset_write(task, data);
-               }
+       struct nfs_pgio_header *hdr = data->header;
+       int err;
+
+       err = filelayout_async_handle_error(task, data->args.context->state,
+                                           data->ds_clp, hdr->lseg);
+
+       switch (err) {
+       case -NFS4ERR_RESET_TO_MDS:
+               filelayout_reset_write(data);
+               return task->tk_status;
+       case -EAGAIN:
                rpc_restart_call_prepare(task);
                return -EAGAIN;
        }
@@ -250,7 +346,7 @@ static int filelayout_write_done_cb(struct rpc_task *task,
 }
 
 /* Fake up some data that will cause nfs_commit_release to retry the writes. */
-static void prepare_to_resend_writes(struct nfs_write_data *data)
+static void prepare_to_resend_writes(struct nfs_commit_data *data)
 {
        struct nfs_page *first = nfs_list_entry(data->pages.next);
 
@@ -261,19 +357,19 @@ static void prepare_to_resend_writes(struct nfs_write_data *data)
 }
 
 static int filelayout_commit_done_cb(struct rpc_task *task,
-                                    struct nfs_write_data *data)
+                                    struct nfs_commit_data *data)
 {
-       int reset = 0;
-
-       if (filelayout_async_handle_error(task, data->args.context->state,
-                                         data->ds_clp, &reset) == -EAGAIN) {
-               dprintk("%s calling restart ds_clp %p ds_clp->cl_session %p\n",
-                       __func__, data->ds_clp, data->ds_clp->cl_session);
-               if (reset) {
-                       prepare_to_resend_writes(data);
-                       pnfs_set_lo_fail(data->lseg);
-               } else
-                       rpc_restart_call_prepare(task);
+       int err;
+
+       err = filelayout_async_handle_error(task, NULL, data->ds_clp,
+                                           data->lseg);
+
+       switch (err) {
+       case -NFS4ERR_RESET_TO_MDS:
+               prepare_to_resend_writes(data);
+               return -EAGAIN;
+       case -EAGAIN:
+               rpc_restart_call_prepare(task);
                return -EAGAIN;
        }
 
@@ -282,8 +378,14 @@ static int filelayout_commit_done_cb(struct rpc_task *task,
 
 static void filelayout_write_prepare(struct rpc_task *task, void *data)
 {
-       struct nfs_write_data *wdata = (struct nfs_write_data *)data;
+       struct nfs_write_data *wdata = data;
 
+       if (filelayout_reset_to_mds(wdata->header->lseg)) {
+               dprintk("%s task %u reset io to MDS\n", __func__, task->tk_pid);
+               filelayout_reset_write(wdata);
+               rpc_exit(task, 0);
+               return;
+       }
        if (nfs41_setup_sequence(wdata->ds_clp->cl_session,
                                &wdata->args.seq_args, &wdata->res.seq_res,
                                task))
@@ -294,36 +396,66 @@ static void filelayout_write_prepare(struct rpc_task *task, void *data)
 
 static void filelayout_write_call_done(struct rpc_task *task, void *data)
 {
-       struct nfs_write_data *wdata = (struct nfs_write_data *)data;
+       struct nfs_write_data *wdata = data;
+
+       if (test_bit(NFS_IOHDR_REDO, &wdata->header->flags) &&
+           task->tk_status == 0)
+               return;
 
        /* Note this may cause RPC to be resent */
-       wdata->mds_ops->rpc_call_done(task, data);
+       wdata->header->mds_ops->rpc_call_done(task, data);
 }
 
 static void filelayout_write_count_stats(struct rpc_task *task, void *data)
 {
-       struct nfs_write_data *wdata = (struct nfs_write_data *)data;
+       struct nfs_write_data *wdata = data;
 
-       rpc_count_iostats(task, NFS_SERVER(wdata->inode)->client->cl_metrics);
+       rpc_count_iostats(task, NFS_SERVER(wdata->header->inode)->client->cl_metrics);
 }
 
 static void filelayout_write_release(void *data)
 {
-       struct nfs_write_data *wdata = (struct nfs_write_data *)data;
+       struct nfs_write_data *wdata = data;
+
+       nfs_put_client(wdata->ds_clp);
+       wdata->header->mds_ops->rpc_release(data);
+}
+
+static void filelayout_commit_prepare(struct rpc_task *task, void *data)
+{
+       struct nfs_commit_data *wdata = data;
 
-       put_lseg(wdata->lseg);
-       wdata->mds_ops->rpc_release(data);
+       if (nfs41_setup_sequence(wdata->ds_clp->cl_session,
+                               &wdata->args.seq_args, &wdata->res.seq_res,
+                               task))
+               return;
+
+       rpc_call_start(task);
+}
+
+static void filelayout_write_commit_done(struct rpc_task *task, void *data)
+{
+       struct nfs_commit_data *wdata = data;
+
+       /* Note this may cause RPC to be resent */
+       wdata->mds_ops->rpc_call_done(task, data);
+}
+
+static void filelayout_commit_count_stats(struct rpc_task *task, void *data)
+{
+       struct nfs_commit_data *cdata = data;
+
+       rpc_count_iostats(task, NFS_SERVER(cdata->inode)->client->cl_metrics);
 }
 
-static void filelayout_commit_release(void *data)
+static void filelayout_commit_release(void *calldata)
 {
-       struct nfs_write_data *wdata = (struct nfs_write_data *)data;
+       struct nfs_commit_data *data = calldata;
 
-       nfs_commit_release_pages(wdata);
-       if (atomic_dec_and_test(&NFS_I(wdata->inode)->commits_outstanding))
-               nfs_commit_clear_lock(NFS_I(wdata->inode));
-       put_lseg(wdata->lseg);
-       nfs_commitdata_release(wdata);
+       data->completion_ops->completion(data);
+       put_lseg(data->lseg);
+       nfs_put_client(data->ds_clp);
+       nfs_commitdata_release(data);
 }
 
 static const struct rpc_call_ops filelayout_read_call_ops = {
@@ -341,16 +473,17 @@ static const struct rpc_call_ops filelayout_write_call_ops = {
 };
 
 static const struct rpc_call_ops filelayout_commit_call_ops = {
-       .rpc_call_prepare = filelayout_write_prepare,
-       .rpc_call_done = filelayout_write_call_done,
-       .rpc_count_stats = filelayout_write_count_stats,
+       .rpc_call_prepare = filelayout_commit_prepare,
+       .rpc_call_done = filelayout_write_commit_done,
+       .rpc_count_stats = filelayout_commit_count_stats,
        .rpc_release = filelayout_commit_release,
 };
 
 static enum pnfs_try_status
 filelayout_read_pagelist(struct nfs_read_data *data)
 {
-       struct pnfs_layout_segment *lseg = data->lseg;
+       struct nfs_pgio_header *hdr = data->header;
+       struct pnfs_layout_segment *lseg = hdr->lseg;
        struct nfs4_pnfs_ds *ds;
        loff_t offset = data->args.offset;
        u32 j, idx;
@@ -358,25 +491,20 @@ filelayout_read_pagelist(struct nfs_read_data *data)
        int status;
 
        dprintk("--> %s ino %lu pgbase %u req %Zu@%llu\n",
-               __func__, data->inode->i_ino,
+               __func__, hdr->inode->i_ino,
                data->args.pgbase, (size_t)data->args.count, offset);
 
-       if (test_bit(NFS_DEVICEID_INVALID, &FILELAYOUT_DEVID_NODE(lseg)->flags))
-               return PNFS_NOT_ATTEMPTED;
-
        /* Retrieve the correct rpc_client for the byte range */
        j = nfs4_fl_calc_j_index(lseg, offset);
        idx = nfs4_fl_calc_ds_index(lseg, j);
        ds = nfs4_fl_prepare_ds(lseg, idx);
-       if (!ds) {
-               /* Either layout fh index faulty, or ds connect failed */
-               set_bit(lo_fail_bit(IOMODE_RW), &lseg->pls_layout->plh_flags);
-               set_bit(lo_fail_bit(IOMODE_READ), &lseg->pls_layout->plh_flags);
+       if (!ds)
                return PNFS_NOT_ATTEMPTED;
-       }
-       dprintk("%s USE DS: %s\n", __func__, ds->ds_remotestr);
+       dprintk("%s USE DS: %s cl_count %d\n", __func__,
+               ds->ds_remotestr, atomic_read(&ds->ds_clp->cl_count));
 
        /* No multipath support. Use first DS */
+       atomic_inc(&ds->ds_clp->cl_count);
        data->ds_clp = ds->ds_clp;
        fh = nfs4_fl_select_ds_fh(lseg, j);
        if (fh)
@@ -386,8 +514,8 @@ filelayout_read_pagelist(struct nfs_read_data *data)
        data->mds_offset = offset;
 
        /* Perform an asynchronous read to ds */
-       status = nfs_initiate_read(data, ds->ds_clp->cl_rpcclient,
-                                  &filelayout_read_call_ops);
+       status = nfs_initiate_read(ds->ds_clp->cl_rpcclient, data,
+                                 &filelayout_read_call_ops, RPC_TASK_SOFTCONN);
        BUG_ON(status != 0);
        return PNFS_ATTEMPTED;
 }
@@ -396,32 +524,26 @@ filelayout_read_pagelist(struct nfs_read_data *data)
 static enum pnfs_try_status
 filelayout_write_pagelist(struct nfs_write_data *data, int sync)
 {
-       struct pnfs_layout_segment *lseg = data->lseg;
+       struct nfs_pgio_header *hdr = data->header;
+       struct pnfs_layout_segment *lseg = hdr->lseg;
        struct nfs4_pnfs_ds *ds;
        loff_t offset = data->args.offset;
        u32 j, idx;
        struct nfs_fh *fh;
        int status;
 
-       if (test_bit(NFS_DEVICEID_INVALID, &FILELAYOUT_DEVID_NODE(lseg)->flags))
-               return PNFS_NOT_ATTEMPTED;
-
        /* Retrieve the correct rpc_client for the byte range */
        j = nfs4_fl_calc_j_index(lseg, offset);
        idx = nfs4_fl_calc_ds_index(lseg, j);
        ds = nfs4_fl_prepare_ds(lseg, idx);
-       if (!ds) {
-               printk(KERN_ERR "NFS: %s: prepare_ds failed, use MDS\n",
-                       __func__);
-               set_bit(lo_fail_bit(IOMODE_RW), &lseg->pls_layout->plh_flags);
-               set_bit(lo_fail_bit(IOMODE_READ), &lseg->pls_layout->plh_flags);
+       if (!ds)
                return PNFS_NOT_ATTEMPTED;
-       }
-       dprintk("%s ino %lu sync %d req %Zu@%llu DS: %s\n", __func__,
-               data->inode->i_ino, sync, (size_t) data->args.count, offset,
-               ds->ds_remotestr);
+       dprintk("%s ino %lu sync %d req %Zu@%llu DS: %s cl_count %d\n",
+               __func__, hdr->inode->i_ino, sync, (size_t) data->args.count,
+               offset, ds->ds_remotestr, atomic_read(&ds->ds_clp->cl_count));
 
        data->write_done_cb = filelayout_write_done_cb;
+       atomic_inc(&ds->ds_clp->cl_count);
        data->ds_clp = ds->ds_clp;
        fh = nfs4_fl_select_ds_fh(lseg, j);
        if (fh)
@@ -433,8 +555,9 @@ filelayout_write_pagelist(struct nfs_write_data *data, int sync)
        data->args.offset = filelayout_get_dserver_offset(lseg, offset);
 
        /* Perform an asynchronous write */
-       status = nfs_initiate_write(data, ds->ds_clp->cl_rpcclient,
-                                   &filelayout_write_call_ops, sync);
+       status = nfs_initiate_write(ds->ds_clp->cl_rpcclient, data,
+                                   &filelayout_write_call_ops, sync,
+                                   RPC_TASK_SOFTCONN);
        BUG_ON(status != 0);
        return PNFS_ATTEMPTED;
 }
@@ -650,10 +773,65 @@ filelayout_free_lseg(struct pnfs_layout_segment *lseg)
 
        dprintk("--> %s\n", __func__);
        nfs4_fl_put_deviceid(fl->dsaddr);
-       kfree(fl->commit_buckets);
+       /* This assumes a single RW lseg */
+       if (lseg->pls_range.iomode == IOMODE_RW) {
+               struct nfs4_filelayout *flo;
+
+               flo = FILELAYOUT_FROM_HDR(lseg->pls_layout);
+               flo->commit_info.nbuckets = 0;
+               kfree(flo->commit_info.buckets);
+               flo->commit_info.buckets = NULL;
+       }
        _filelayout_free_lseg(fl);
 }
 
+static int
+filelayout_alloc_commit_info(struct pnfs_layout_segment *lseg,
+                            struct nfs_commit_info *cinfo,
+                            gfp_t gfp_flags)
+{
+       struct nfs4_filelayout_segment *fl = FILELAYOUT_LSEG(lseg);
+       struct pnfs_commit_bucket *buckets;
+       int size;
+
+       if (fl->commit_through_mds)
+               return 0;
+       if (cinfo->ds->nbuckets != 0) {
+               /* This assumes there is only one IOMODE_RW lseg.  What
+                * we really want to do is have a layout_hdr level
+                * dictionary of <multipath_list4, fh> keys, each
+                * associated with a struct list_head, populated by calls
+                * to filelayout_write_pagelist().
+                * */
+               return 0;
+       }
+
+       size = (fl->stripe_type == STRIPE_SPARSE) ?
+               fl->dsaddr->ds_num : fl->dsaddr->stripe_count;
+
+       buckets = kcalloc(size, sizeof(struct pnfs_commit_bucket),
+                         gfp_flags);
+       if (!buckets)
+               return -ENOMEM;
+       else {
+               int i;
+
+               spin_lock(cinfo->lock);
+               if (cinfo->ds->nbuckets != 0)
+                       kfree(buckets);
+               else {
+                       cinfo->ds->buckets = buckets;
+                       cinfo->ds->nbuckets = size;
+                       for (i = 0; i < size; i++) {
+                               INIT_LIST_HEAD(&buckets[i].written);
+                               INIT_LIST_HEAD(&buckets[i].committing);
+                       }
+               }
+               spin_unlock(cinfo->lock);
+               return 0;
+       }
+}
+
 static struct pnfs_layout_segment *
 filelayout_alloc_lseg(struct pnfs_layout_hdr *layoutid,
                      struct nfs4_layoutget_res *lgr,
@@ -673,29 +851,6 @@ filelayout_alloc_lseg(struct pnfs_layout_hdr *layoutid,
                _filelayout_free_lseg(fl);
                return NULL;
        }
-
-       /* This assumes there is only one IOMODE_RW lseg.  What
-        * we really want to do is have a layout_hdr level
-        * dictionary of <multipath_list4, fh> keys, each
-        * associated with a struct list_head, populated by calls
-        * to filelayout_write_pagelist().
-        * */
-       if ((!fl->commit_through_mds) && (lgr->range.iomode == IOMODE_RW)) {
-               int i;
-               int size = (fl->stripe_type == STRIPE_SPARSE) ?
-                       fl->dsaddr->ds_num : fl->dsaddr->stripe_count;
-
-               fl->commit_buckets = kcalloc(size, sizeof(struct nfs4_fl_commit_bucket), gfp_flags);
-               if (!fl->commit_buckets) {
-                       filelayout_free_lseg(&fl->generic_hdr);
-                       return NULL;
-               }
-               fl->number_of_buckets = size;
-               for (i = 0; i < size; i++) {
-                       INIT_LIST_HEAD(&fl->commit_buckets[i].written);
-                       INIT_LIST_HEAD(&fl->commit_buckets[i].committing);
-               }
-       }
        return &fl->generic_hdr;
 }
 
@@ -716,8 +871,8 @@ filelayout_pg_test(struct nfs_pageio_descriptor *pgio, struct nfs_page *prev,
            !nfs_generic_pg_test(pgio, prev, req))
                return false;
 
-       p_stripe = (u64)prev->wb_index << PAGE_CACHE_SHIFT;
-       r_stripe = (u64)req->wb_index << PAGE_CACHE_SHIFT;
+       p_stripe = (u64)req_offset(prev);
+       r_stripe = (u64)req_offset(req);
        stripe_unit = FILELAYOUT_LSEG(pgio->pg_lseg)->stripe_unit;
 
        do_div(p_stripe, stripe_unit);
@@ -732,6 +887,16 @@ filelayout_pg_init_read(struct nfs_pageio_descriptor *pgio,
 {
        BUG_ON(pgio->pg_lseg != NULL);
 
+       if (req->wb_offset != req->wb_pgbase) {
+               /*
+                * Handling unaligned pages is difficult, because have to
+                * somehow split a req in two in certain cases in the
+                * pg.test code.  Avoid this by just not using pnfs
+                * in this case.
+                */
+               nfs_pageio_reset_read_mds(pgio);
+               return;
+       }
        pgio->pg_lseg = pnfs_update_layout(pgio->pg_inode,
                                           req->wb_context,
                                           0,
@@ -747,8 +912,13 @@ static void
 filelayout_pg_init_write(struct nfs_pageio_descriptor *pgio,
                         struct nfs_page *req)
 {
+       struct nfs_commit_info cinfo;
+       int status;
+
        BUG_ON(pgio->pg_lseg != NULL);
 
+       if (req->wb_offset != req->wb_pgbase)
+               goto out_mds;
        pgio->pg_lseg = pnfs_update_layout(pgio->pg_inode,
                                           req->wb_context,
                                           0,
@@ -757,7 +927,17 @@ filelayout_pg_init_write(struct nfs_pageio_descriptor *pgio,
                                           GFP_NOFS);
        /* If no lseg, fall back to write through mds */
        if (pgio->pg_lseg == NULL)
-               nfs_pageio_reset_write_mds(pgio);
+               goto out_mds;
+       nfs_init_cinfo(&cinfo, pgio->pg_inode, pgio->pg_dreq);
+       status = filelayout_alloc_commit_info(pgio->pg_lseg, &cinfo, GFP_NOFS);
+       if (status < 0) {
+               put_lseg(pgio->pg_lseg);
+               pgio->pg_lseg = NULL;
+               goto out_mds;
+       }
+       return;
+out_mds:
+       nfs_pageio_reset_write_mds(pgio);
 }
 
 static const struct nfs_pageio_ops filelayout_pg_read_ops = {
@@ -784,43 +964,42 @@ static u32 select_bucket_index(struct nfs4_filelayout_segment *fl, u32 j)
  * If this will make the bucket empty, it will need to put the lseg reference.
  */
 static void
-filelayout_clear_request_commit(struct nfs_page *req)
+filelayout_clear_request_commit(struct nfs_page *req,
+                               struct nfs_commit_info *cinfo)
 {
        struct pnfs_layout_segment *freeme = NULL;
-       struct inode *inode = req->wb_context->dentry->d_inode;
 
-       spin_lock(&inode->i_lock);
+       spin_lock(cinfo->lock);
        if (!test_and_clear_bit(PG_COMMIT_TO_DS, &req->wb_flags))
                goto out;
+       cinfo->ds->nwritten--;
        if (list_is_singular(&req->wb_list)) {
-               struct pnfs_layout_segment *lseg;
+               struct pnfs_commit_bucket *bucket;
 
-               /* From here we can find the bucket, but for the moment,
-                * since there is only one relevant lseg...
-                */
-               list_for_each_entry(lseg, &NFS_I(inode)->layout->plh_segs, pls_list) {
-                       if (lseg->pls_range.iomode == IOMODE_RW) {
-                               freeme = lseg;
-                               break;
-                       }
-               }
+               bucket = list_first_entry(&req->wb_list,
+                                         struct pnfs_commit_bucket,
+                                         written);
+               freeme = bucket->wlseg;
+               bucket->wlseg = NULL;
        }
 out:
-       nfs_request_remove_commit_list(req);
-       spin_unlock(&inode->i_lock);
+       nfs_request_remove_commit_list(req, cinfo);
+       spin_unlock(cinfo->lock);
        put_lseg(freeme);
 }
 
 static struct list_head *
 filelayout_choose_commit_list(struct nfs_page *req,
-                             struct pnfs_layout_segment *lseg)
+                             struct pnfs_layout_segment *lseg,
+                             struct nfs_commit_info *cinfo)
 {
        struct nfs4_filelayout_segment *fl = FILELAYOUT_LSEG(lseg);
        u32 i, j;
        struct list_head *list;
+       struct pnfs_commit_bucket *buckets;
 
        if (fl->commit_through_mds)
-               return &NFS_I(req->wb_context->dentry->d_inode)->commit_list;
+               return &cinfo->mds->list;
 
        /* Note that we are calling nfs4_fl_calc_j_index on each page
         * that ends up being committed to a data server.  An attractive
@@ -828,31 +1007,33 @@ filelayout_choose_commit_list(struct nfs_page *req,
         * to store the value calculated in filelayout_write_pagelist
         * and just use that here.
         */
-       j = nfs4_fl_calc_j_index(lseg,
-                                (loff_t)req->wb_index << PAGE_CACHE_SHIFT);
+       j = nfs4_fl_calc_j_index(lseg, req_offset(req));
        i = select_bucket_index(fl, j);
-       list = &fl->commit_buckets[i].written;
+       buckets = cinfo->ds->buckets;
+       list = &buckets[i].written;
        if (list_empty(list)) {
                /* Non-empty buckets hold a reference on the lseg.  That ref
                 * is normally transferred to the COMMIT call and released
                 * there.  It could also be released if the last req is pulled
                 * off due to a rewrite, in which case it will be done in
-                * filelayout_remove_commit_req
+                * filelayout_clear_request_commit
                 */
-               get_lseg(lseg);
+               buckets[i].wlseg = get_lseg(lseg);
        }
        set_bit(PG_COMMIT_TO_DS, &req->wb_flags);
+       cinfo->ds->nwritten++;
        return list;
 }
 
 static void
 filelayout_mark_request_commit(struct nfs_page *req,
-               struct pnfs_layout_segment *lseg)
+                              struct pnfs_layout_segment *lseg,
+                              struct nfs_commit_info *cinfo)
 {
        struct list_head *list;
 
-       list = filelayout_choose_commit_list(req, lseg);
-       nfs_request_add_commit_list(req, list);
+       list = filelayout_choose_commit_list(req, lseg, cinfo);
+       nfs_request_add_commit_list(req, list, cinfo);
 }
 
 static u32 calc_ds_index_from_commit(struct pnfs_layout_segment *lseg, u32 i)
@@ -880,7 +1061,7 @@ select_ds_fh_from_commit(struct pnfs_layout_segment *lseg, u32 i)
        return flseg->fh_array[i];
 }
 
-static int filelayout_initiate_commit(struct nfs_write_data *data, int how)
+static int filelayout_initiate_commit(struct nfs_commit_data *data, int how)
 {
        struct pnfs_layout_segment *lseg = data->lseg;
        struct nfs4_pnfs_ds *ds;
@@ -890,135 +1071,138 @@ static int filelayout_initiate_commit(struct nfs_write_data *data, int how)
        idx = calc_ds_index_from_commit(lseg, data->ds_commit_index);
        ds = nfs4_fl_prepare_ds(lseg, idx);
        if (!ds) {
-               printk(KERN_ERR "NFS: %s: prepare_ds failed, use MDS\n",
-                       __func__);
-               set_bit(lo_fail_bit(IOMODE_RW), &lseg->pls_layout->plh_flags);
-               set_bit(lo_fail_bit(IOMODE_READ), &lseg->pls_layout->plh_flags);
                prepare_to_resend_writes(data);
                filelayout_commit_release(data);
                return -EAGAIN;
        }
-       dprintk("%s ino %lu, how %d\n", __func__, data->inode->i_ino, how);
-       data->write_done_cb = filelayout_commit_done_cb;
+       dprintk("%s ino %lu, how %d cl_count %d\n", __func__,
+               data->inode->i_ino, how, atomic_read(&ds->ds_clp->cl_count));
+       data->commit_done_cb = filelayout_commit_done_cb;
+       atomic_inc(&ds->ds_clp->cl_count);
        data->ds_clp = ds->ds_clp;
        fh = select_ds_fh_from_commit(lseg, data->ds_commit_index);
        if (fh)
                data->args.fh = fh;
-       return nfs_initiate_commit(data, ds->ds_clp->cl_rpcclient,
-                                  &filelayout_commit_call_ops, how);
-}
-
-/*
- * This is only useful while we are using whole file layouts.
- */
-static struct pnfs_layout_segment *
-find_only_write_lseg_locked(struct inode *inode)
-{
-       struct pnfs_layout_segment *lseg;
-
-       list_for_each_entry(lseg, &NFS_I(inode)->layout->plh_segs, pls_list)
-               if (lseg->pls_range.iomode == IOMODE_RW)
-                       return lseg;
-       return NULL;
-}
-
-static struct pnfs_layout_segment *find_only_write_lseg(struct inode *inode)
-{
-       struct pnfs_layout_segment *rv;
-
-       spin_lock(&inode->i_lock);
-       rv = find_only_write_lseg_locked(inode);
-       if (rv)
-               get_lseg(rv);
-       spin_unlock(&inode->i_lock);
-       return rv;
+       return nfs_initiate_commit(ds->ds_clp->cl_rpcclient, data,
+                                  &filelayout_commit_call_ops, how,
+                                  RPC_TASK_SOFTCONN);
 }
 
 static int
-filelayout_scan_ds_commit_list(struct nfs4_fl_commit_bucket *bucket, int max,
-               spinlock_t *lock)
+transfer_commit_list(struct list_head *src, struct list_head *dst,
+                    struct nfs_commit_info *cinfo, int max)
 {
-       struct list_head *src = &bucket->written;
-       struct list_head *dst = &bucket->committing;
        struct nfs_page *req, *tmp;
        int ret = 0;
 
        list_for_each_entry_safe(req, tmp, src, wb_list) {
                if (!nfs_lock_request(req))
                        continue;
-               if (cond_resched_lock(lock))
+               kref_get(&req->wb_kref);
+               if (cond_resched_lock(cinfo->lock))
                        list_safe_reset_next(req, tmp, wb_list);
-               nfs_request_remove_commit_list(req);
+               nfs_request_remove_commit_list(req, cinfo);
                clear_bit(PG_COMMIT_TO_DS, &req->wb_flags);
                nfs_list_add_request(req, dst);
                ret++;
-               if (ret == max)
+               if ((ret == max) && !cinfo->dreq)
                        break;
        }
        return ret;
 }
 
+static int
+filelayout_scan_ds_commit_list(struct pnfs_commit_bucket *bucket,
+                              struct nfs_commit_info *cinfo,
+                              int max)
+{
+       struct list_head *src = &bucket->written;
+       struct list_head *dst = &bucket->committing;
+       int ret;
+
+       ret = transfer_commit_list(src, dst, cinfo, max);
+       if (ret) {
+               cinfo->ds->nwritten -= ret;
+               cinfo->ds->ncommitting += ret;
+               bucket->clseg = bucket->wlseg;
+               if (list_empty(src))
+                       bucket->wlseg = NULL;
+               else
+                       get_lseg(bucket->clseg);
+       }
+       return ret;
+}
+
 /* Move reqs from written to committing lists, returning count of number moved.
- * Note called with i_lock held.
+ * Note called with cinfo->lock held.
  */
-static int filelayout_scan_commit_lists(struct inode *inode, int max,
-               spinlock_t *lock)
+static int filelayout_scan_commit_lists(struct nfs_commit_info *cinfo,
+                                       int max)
 {
-       struct pnfs_layout_segment *lseg;
-       struct nfs4_filelayout_segment *fl;
        int i, rv = 0, cnt;
 
-       lseg = find_only_write_lseg_locked(inode);
-       if (!lseg)
-               goto out_done;
-       fl = FILELAYOUT_LSEG(lseg);
-       if (fl->commit_through_mds)
-               goto out_done;
-       for (i = 0; i < fl->number_of_buckets && max != 0; i++) {
-               cnt = filelayout_scan_ds_commit_list(&fl->commit_buckets[i],
-                               max, lock);
+       for (i = 0; i < cinfo->ds->nbuckets && max != 0; i++) {
+               cnt = filelayout_scan_ds_commit_list(&cinfo->ds->buckets[i],
+                                                    cinfo, max);
                max -= cnt;
                rv += cnt;
        }
-out_done:
        return rv;
 }
 
+/* Pull everything off the committing lists and dump into @dst */
+static void filelayout_recover_commit_reqs(struct list_head *dst,
+                                          struct nfs_commit_info *cinfo)
+{
+       struct pnfs_commit_bucket *b;
+       int i;
+
+       /* NOTE cinfo->lock is NOT held, relying on fact that this is
+        * only called on single thread per dreq.
+        * Can't take the lock because need to do put_lseg
+        */
+       for (i = 0, b = cinfo->ds->buckets; i < cinfo->ds->nbuckets; i++, b++) {
+               if (transfer_commit_list(&b->written, dst, cinfo, 0)) {
+                       BUG_ON(!list_empty(&b->written));
+                       put_lseg(b->wlseg);
+                       b->wlseg = NULL;
+               }
+       }
+       cinfo->ds->nwritten = 0;
+}
+
 static unsigned int
-alloc_ds_commits(struct inode *inode, struct list_head *list)
+alloc_ds_commits(struct nfs_commit_info *cinfo, struct list_head *list)
 {
-       struct pnfs_layout_segment *lseg;
-       struct nfs4_filelayout_segment *fl;
-       struct nfs_write_data *data;
+       struct pnfs_ds_commit_info *fl_cinfo;
+       struct pnfs_commit_bucket *bucket;
+       struct nfs_commit_data *data;
        int i, j;
        unsigned int nreq = 0;
 
-       /* Won't need this when non-whole file layout segments are supported
-        * instead we will use a pnfs_layout_hdr structure */
-       lseg = find_only_write_lseg(inode);
-       if (!lseg)
-               return 0;
-       fl = FILELAYOUT_LSEG(lseg);
-       for (i = 0; i < fl->number_of_buckets; i++) {
-               if (list_empty(&fl->commit_buckets[i].committing))
+       fl_cinfo = cinfo->ds;
+       bucket = fl_cinfo->buckets;
+       for (i = 0; i < fl_cinfo->nbuckets; i++, bucket++) {
+               if (list_empty(&bucket->committing))
                        continue;
                data = nfs_commitdata_alloc();
                if (!data)
                        break;
                data->ds_commit_index = i;
-               data->lseg = lseg;
+               data->lseg = bucket->clseg;
+               bucket->clseg = NULL;
                list_add(&data->pages, list);
                nreq++;
        }
 
        /* Clean up on error */
-       for (j = i; j < fl->number_of_buckets; j++) {
-               if (list_empty(&fl->commit_buckets[i].committing))
+       for (j = i; j < fl_cinfo->nbuckets; j++, bucket++) {
+               if (list_empty(&bucket->committing))
                        continue;
-               nfs_retry_commit(&fl->commit_buckets[i].committing, lseg);
-               put_lseg(lseg);  /* associated with emptying bucket */
+               nfs_retry_commit(&bucket->committing, bucket->clseg, cinfo);
+               put_lseg(bucket->clseg);
+               bucket->clseg = NULL;
        }
-       put_lseg(lseg);
        /* Caller will clean up entries put on list */
        return nreq;
 }
@@ -1026,9 +1210,9 @@ alloc_ds_commits(struct inode *inode, struct list_head *list)
 /* This follows nfs_commit_list pretty closely */
 static int
 filelayout_commit_pagelist(struct inode *inode, struct list_head *mds_pages,
-                          int how)
+                          int how, struct nfs_commit_info *cinfo)
 {
-       struct nfs_write_data   *data, *tmp;
+       struct nfs_commit_data *data, *tmp;
        LIST_HEAD(list);
        unsigned int nreq = 0;
 
@@ -1039,30 +1223,34 @@ filelayout_commit_pagelist(struct inode *inode, struct list_head *mds_pages,
                        list_add(&data->pages, &list);
                        nreq++;
                } else
-                       nfs_retry_commit(mds_pages, NULL);
+                       nfs_retry_commit(mds_pages, NULL, cinfo);
        }
 
-       nreq += alloc_ds_commits(inode, &list);
+       nreq += alloc_ds_commits(cinfo, &list);
 
        if (nreq == 0) {
-               nfs_commit_clear_lock(NFS_I(inode));
+               cinfo->completion_ops->error_cleanup(NFS_I(inode));
                goto out;
        }
 
-       atomic_add(nreq, &NFS_I(inode)->commits_outstanding);
+       atomic_add(nreq, &cinfo->mds->rpcs_out);
 
        list_for_each_entry_safe(data, tmp, &list, pages) {
                list_del_init(&data->pages);
                if (!data->lseg) {
-                       nfs_init_commit(data, mds_pages, NULL);
-                       nfs_initiate_commit(data, NFS_CLIENT(inode),
-                                           data->mds_ops, how);
+                       nfs_init_commit(data, mds_pages, NULL, cinfo);
+                       nfs_initiate_commit(NFS_CLIENT(inode), data,
+                                           data->mds_ops, how, 0);
                } else {
-                       nfs_init_commit(data, &FILELAYOUT_LSEG(data->lseg)->commit_buckets[data->ds_commit_index].committing, data->lseg);
+                       struct pnfs_commit_bucket *buckets;
+
+                       buckets = cinfo->ds->buckets;
+                       nfs_init_commit(data, &buckets[data->ds_commit_index].committing, data->lseg, cinfo);
                        filelayout_initiate_commit(data, how);
                }
        }
 out:
+       cinfo->ds->ncommitting = 0;
        return PNFS_ATTEMPTED;
 }
 
@@ -1072,17 +1260,47 @@ filelayout_free_deveiceid_node(struct nfs4_deviceid_node *d)
        nfs4_fl_free_deviceid(container_of(d, struct nfs4_file_layout_dsaddr, id_node));
 }
 
+static struct pnfs_layout_hdr *
+filelayout_alloc_layout_hdr(struct inode *inode, gfp_t gfp_flags)
+{
+       struct nfs4_filelayout *flo;
+
+       flo = kzalloc(sizeof(*flo), gfp_flags);
+       return &flo->generic_hdr;
+}
+
+static void
+filelayout_free_layout_hdr(struct pnfs_layout_hdr *lo)
+{
+       kfree(FILELAYOUT_FROM_HDR(lo));
+}
+
+static struct pnfs_ds_commit_info *
+filelayout_get_ds_info(struct inode *inode)
+{
+       struct pnfs_layout_hdr *layout = NFS_I(inode)->layout;
+
+       if (layout == NULL)
+               return NULL;
+       else
+               return &FILELAYOUT_FROM_HDR(layout)->commit_info;
+}
+
 static struct pnfs_layoutdriver_type filelayout_type = {
        .id                     = LAYOUT_NFSV4_1_FILES,
        .name                   = "LAYOUT_NFSV4_1_FILES",
        .owner                  = THIS_MODULE,
+       .alloc_layout_hdr       = filelayout_alloc_layout_hdr,
+       .free_layout_hdr        = filelayout_free_layout_hdr,
        .alloc_lseg             = filelayout_alloc_lseg,
        .free_lseg              = filelayout_free_lseg,
        .pg_read_ops            = &filelayout_pg_read_ops,
        .pg_write_ops           = &filelayout_pg_write_ops,
+       .get_ds_info            = &filelayout_get_ds_info,
        .mark_request_commit    = filelayout_mark_request_commit,
        .clear_request_commit   = filelayout_clear_request_commit,
        .scan_commit_lists      = filelayout_scan_commit_lists,
+       .recover_commit_reqs    = filelayout_recover_commit_reqs,
        .commit_pagelist        = filelayout_commit_pagelist,
        .read_pagelist          = filelayout_read_pagelist,
        .write_pagelist         = filelayout_write_pagelist,
index 21190bb1f5e348c5549e5985afb8cdf896aa72dd..43fe802dd67855fbfe521825d0c24ef3acca7c38 100644 (file)
 
 #include "pnfs.h"
 
+/*
+ * Default data server connection timeout and retrans vaules.
+ * Set by module paramters dataserver_timeo and dataserver_retrans.
+ */
+#define NFS4_DEF_DS_TIMEO   60
+#define NFS4_DEF_DS_RETRANS 5
+
 /*
  * Field testing shows we need to support up to 4096 stripe indices.
  * We store each index as a u8 (u32 on the wire) to keep the memory footprint
@@ -41,6 +48,9 @@
 #define NFS4_PNFS_MAX_STRIPE_CNT 4096
 #define NFS4_PNFS_MAX_MULTI_CNT  256 /* 256 fit into a u8 stripe_index */
 
+/* error codes for internal use */
+#define NFS4ERR_RESET_TO_MDS   12001
+
 enum stripetype4 {
        STRIPE_SPARSE = 1,
        STRIPE_DENSE = 2
@@ -62,23 +72,14 @@ struct nfs4_pnfs_ds {
        atomic_t                ds_count;
 };
 
-/* nfs4_file_layout_dsaddr flags */
-#define NFS4_DEVICE_ID_NEG_ENTRY       0x00000001
-
 struct nfs4_file_layout_dsaddr {
        struct nfs4_deviceid_node       id_node;
-       unsigned long                   flags;
        u32                             stripe_count;
        u8                              *stripe_indices;
        u32                             ds_num;
        struct nfs4_pnfs_ds             *ds_list[1];
 };
 
-struct nfs4_fl_commit_bucket {
-       struct list_head written;
-       struct list_head committing;
-};
-
 struct nfs4_filelayout_segment {
        struct pnfs_layout_segment generic_hdr;
        u32 stripe_type;
@@ -89,10 +90,19 @@ struct nfs4_filelayout_segment {
        struct nfs4_file_layout_dsaddr *dsaddr; /* Point to GETDEVINFO data */
        unsigned int num_fh;
        struct nfs_fh **fh_array;
-       struct nfs4_fl_commit_bucket *commit_buckets; /* Sort commits to ds */
-       int number_of_buckets;
 };
 
+struct nfs4_filelayout {
+       struct pnfs_layout_hdr generic_hdr;
+       struct pnfs_ds_commit_info commit_info;
+};
+
+static inline struct nfs4_filelayout *
+FILELAYOUT_FROM_HDR(struct pnfs_layout_hdr *lo)
+{
+       return container_of(lo, struct nfs4_filelayout, generic_hdr);
+}
+
 static inline struct nfs4_filelayout_segment *
 FILELAYOUT_LSEG(struct pnfs_layout_segment *lseg)
 {
@@ -107,6 +117,36 @@ FILELAYOUT_DEVID_NODE(struct pnfs_layout_segment *lseg)
        return &FILELAYOUT_LSEG(lseg)->dsaddr->id_node;
 }
 
+static inline void
+filelayout_mark_devid_invalid(struct nfs4_deviceid_node *node)
+{
+       u32 *p = (u32 *)&node->deviceid;
+
+       printk(KERN_WARNING "NFS: Deviceid [%x%x%x%x] marked out of use.\n",
+               p[0], p[1], p[2], p[3]);
+
+       set_bit(NFS_DEVICEID_INVALID, &node->flags);
+}
+
+static inline bool
+filelayout_test_layout_invalid(struct pnfs_layout_hdr *lo)
+{
+       return test_bit(NFS_LAYOUT_INVALID, &lo->plh_flags);
+}
+
+static inline bool
+filelayout_test_devid_invalid(struct nfs4_deviceid_node *node)
+{
+       return test_bit(NFS_DEVICEID_INVALID, &node->flags);
+}
+
+static inline bool
+filelayout_reset_to_mds(struct pnfs_layout_segment *lseg)
+{
+       return filelayout_test_devid_invalid(FILELAYOUT_DEVID_NODE(lseg)) ||
+               filelayout_test_layout_invalid(lseg->pls_layout);
+}
+
 extern struct nfs_fh *
 nfs4_fl_select_ds_fh(struct pnfs_layout_segment *lseg, u32 j);
 
@@ -119,5 +159,6 @@ extern void nfs4_fl_put_deviceid(struct nfs4_file_layout_dsaddr *dsaddr);
 extern void nfs4_fl_free_deviceid(struct nfs4_file_layout_dsaddr *dsaddr);
 struct nfs4_file_layout_dsaddr *
 get_device_info(struct inode *inode, struct nfs4_deviceid *dev_id, gfp_t gfp_flags);
+void nfs4_ds_disconnect(struct nfs_client *clp);
 
 #endif /* FS_NFS_NFS4FILELAYOUT_H */
index c9cff9adb2d3f7c832f3e3bc7e6d76ec45a6e692..a1fab8da7f03c8819951af81d95e6268a71dc80f 100644 (file)
 
 #include <linux/nfs_fs.h>
 #include <linux/vmalloc.h>
+#include <linux/module.h>
 
 #include "internal.h"
 #include "nfs4filelayout.h"
 
 #define NFSDBG_FACILITY                NFSDBG_PNFS_LD
 
+static unsigned int dataserver_timeo = NFS4_DEF_DS_TIMEO;
+static unsigned int dataserver_retrans = NFS4_DEF_DS_RETRANS;
+
 /*
  * Data server cache
  *
@@ -144,6 +148,28 @@ _data_server_lookup_locked(const struct list_head *dsaddrs)
        return NULL;
 }
 
+/*
+ * Lookup DS by nfs_client pointer. Zero data server client pointer
+ */
+void nfs4_ds_disconnect(struct nfs_client *clp)
+{
+       struct nfs4_pnfs_ds *ds;
+       struct nfs_client *found = NULL;
+
+       dprintk("%s clp %p\n", __func__, clp);
+       spin_lock(&nfs4_ds_cache_lock);
+       list_for_each_entry(ds, &nfs4_data_server_cache, ds_node)
+               if (ds->ds_clp && ds->ds_clp == clp) {
+                       found = ds->ds_clp;
+                       ds->ds_clp = NULL;
+               }
+       spin_unlock(&nfs4_ds_cache_lock);
+       if (found) {
+               set_bit(NFS_CS_STOP_RENEW, &clp->cl_res_state);
+               nfs_put_client(clp);
+       }
+}
+
 /*
  * Create an rpc connection to the nfs4_pnfs_ds data server
  * Currently only supports IPv4 and IPv6 addresses
@@ -165,8 +191,9 @@ nfs4_ds_connect(struct nfs_server *mds_srv, struct nfs4_pnfs_ds *ds)
                        __func__, ds->ds_remotestr, da->da_remotestr);
 
                clp = nfs4_set_ds_client(mds_srv->nfs_client,
-                                (struct sockaddr *)&da->da_addr,
-                                da->da_addrlen, IPPROTO_TCP);
+                                       (struct sockaddr *)&da->da_addr,
+                                       da->da_addrlen, IPPROTO_TCP,
+                                       dataserver_timeo, dataserver_retrans);
                if (!IS_ERR(clp))
                        break;
        }
@@ -176,28 +203,7 @@ nfs4_ds_connect(struct nfs_server *mds_srv, struct nfs4_pnfs_ds *ds)
                goto out;
        }
 
-       if ((clp->cl_exchange_flags & EXCHGID4_FLAG_MASK_PNFS) != 0) {
-               if (!is_ds_client(clp)) {
-                       status = -ENODEV;
-                       goto out_put;
-               }
-               ds->ds_clp = clp;
-               dprintk("%s [existing] server=%s\n", __func__,
-                       ds->ds_remotestr);
-               goto out;
-       }
-
-       /*
-        * Do not set NFS_CS_CHECK_LEASE_TIME instead set the DS lease to
-        * be equal to the MDS lease. Renewal is scheduled in create_session.
-        */
-       spin_lock(&mds_srv->nfs_client->cl_lock);
-       clp->cl_lease_time = mds_srv->nfs_client->cl_lease_time;
-       spin_unlock(&mds_srv->nfs_client->cl_lock);
-       clp->cl_last_renewal = jiffies;
-
-       /* New nfs_client */
-       status = nfs4_init_ds_session(clp);
+       status = nfs4_init_ds_session(clp, mds_srv->nfs_client->cl_lease_time);
        if (status)
                goto out_put;
 
@@ -602,7 +608,7 @@ decode_device(struct inode *ino, struct pnfs_device *pdev, gfp_t gfp_flags)
 
                mp_count = be32_to_cpup(p); /* multipath count */
                for (j = 0; j < mp_count; j++) {
-                       da = decode_ds_addr(NFS_SERVER(ino)->nfs_client->net,
+                       da = decode_ds_addr(NFS_SERVER(ino)->nfs_client->cl_net,
                                            &stream, gfp_flags);
                        if (da)
                                list_add_tail(&da->da_node, &dsaddrs);
@@ -791,48 +797,42 @@ nfs4_fl_select_ds_fh(struct pnfs_layout_segment *lseg, u32 j)
        return flseg->fh_array[i];
 }
 
-static void
-filelayout_mark_devid_negative(struct nfs4_file_layout_dsaddr *dsaddr,
-                              int err, const char *ds_remotestr)
-{
-       u32 *p = (u32 *)&dsaddr->id_node.deviceid;
-
-       printk(KERN_ERR "NFS: data server %s connection error %d."
-               " Deviceid [%x%x%x%x] marked out of use.\n",
-               ds_remotestr, err, p[0], p[1], p[2], p[3]);
-
-       spin_lock(&nfs4_ds_cache_lock);
-       dsaddr->flags |= NFS4_DEVICE_ID_NEG_ENTRY;
-       spin_unlock(&nfs4_ds_cache_lock);
-}
-
 struct nfs4_pnfs_ds *
 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_invalid(devid))
+               return NULL;
 
        if (ds == NULL) {
                printk(KERN_ERR "NFS: %s: No data server for offset index %d\n",
                        __func__, ds_idx);
-               return NULL;
+               goto mark_dev_invalid;
        }
 
        if (!ds->ds_clp) {
                struct nfs_server *s = NFS_SERVER(lseg->pls_layout->plh_inode);
                int err;
 
-               if (dsaddr->flags & NFS4_DEVICE_ID_NEG_ENTRY) {
-                       /* Already tried to connect, don't try again */
-                       dprintk("%s Deviceid marked out of use\n", __func__);
-                       return NULL;
-               }
                err = nfs4_ds_connect(s, ds);
-               if (err) {
-                       filelayout_mark_devid_negative(dsaddr, err,
-                                                      ds->ds_remotestr);
-                       return NULL;
-               }
+               if (err)
+                       goto mark_dev_invalid;
        }
        return ds;
+
+mark_dev_invalid:
+       filelayout_mark_devid_invalid(devid);
+       return NULL;
 }
+
+module_param(dataserver_retrans, uint, 0644);
+MODULE_PARM_DESC(dataserver_retrans, "The  number of times the NFSv4.1 client "
+                       "retries a request before it attempts further "
+                       " recovery  action.");
+module_param(dataserver_timeo, uint, 0644);
+MODULE_PARM_DESC(dataserver_timeo, "The time (in tenths of a second) the "
+                       "NFSv4.1  client  waits for a response from a "
+                       " data server before it retries an NFS request.");
index a7f3dedc4ec7bade9df84ed6f0fc7524507b9c21..017b4b01a69c7a747b4afab8c7591b5f2ac59a20 100644 (file)
@@ -132,6 +132,35 @@ static size_t nfs_parse_server_name(char *string, size_t len,
        return ret;
 }
 
+rpc_authflavor_t nfs_find_best_sec(struct nfs4_secinfo_flavors *flavors)
+{
+       struct gss_api_mech *mech;
+       struct xdr_netobj oid;
+       int i;
+       rpc_authflavor_t pseudoflavor = RPC_AUTH_UNIX;
+
+       for (i = 0; i < flavors->num_flavors; i++) {
+               struct nfs4_secinfo_flavor *flavor;
+               flavor = &flavors->flavors[i];
+
+               if (flavor->flavor == RPC_AUTH_NULL || flavor->flavor == RPC_AUTH_UNIX) {
+                       pseudoflavor = flavor->flavor;
+                       break;
+               } else if (flavor->flavor == RPC_AUTH_GSS) {
+                       oid.len  = flavor->gss.sec_oid4.len;
+                       oid.data = flavor->gss.sec_oid4.data;
+                       mech = gss_mech_get_by_OID(&oid);
+                       if (!mech)
+                               continue;
+                       pseudoflavor = gss_svc_to_pseudoflavor(mech, flavor->gss.service);
+                       gss_mech_put(mech);
+                       break;
+               }
+       }
+
+       return pseudoflavor;
+}
+
 static rpc_authflavor_t nfs4_negotiate_security(struct inode *inode, struct qstr *name)
 {
        struct page *page;
@@ -168,7 +197,7 @@ struct rpc_clnt *nfs4_create_sec_client(struct rpc_clnt *clnt, struct inode *ino
        rpc_authflavor_t flavor;
 
        flavor = nfs4_negotiate_security(inode, name);
-       if (flavor < 0)
+       if ((int)flavor < 0)
                return ERR_PTR(flavor);
 
        clone = rpc_clone_client(clnt);
@@ -300,7 +329,7 @@ out:
  * @dentry - dentry of referral
  *
  */
-struct vfsmount *nfs_do_refmount(struct rpc_clnt *client, struct dentry *dentry)
+static struct vfsmount *nfs_do_refmount(struct rpc_clnt *client, struct dentry *dentry)
 {
        struct vfsmount *mnt = ERR_PTR(-ENOMEM);
        struct dentry *parent;
@@ -341,3 +370,25 @@ out:
        dprintk("%s: done\n", __func__);
        return mnt;
 }
+
+struct vfsmount *nfs4_submount(struct nfs_server *server, struct dentry *dentry,
+                              struct nfs_fh *fh, struct nfs_fattr *fattr)
+{
+       struct dentry *parent = dget_parent(dentry);
+       struct rpc_clnt *client;
+       struct vfsmount *mnt;
+
+       /* Look it up again to get its attributes and sec flavor */
+       client = nfs4_proc_lookup_mountpoint(parent->d_inode, &dentry->d_name, fh, fattr);
+       dput(parent);
+       if (IS_ERR(client))
+               return ERR_CAST(client);
+
+       if (fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL)
+               mnt = nfs_do_refmount(client, dentry);
+       else
+               mnt = nfs_do_submount(dentry, fh, fattr, client->cl_auth->au_flavor);
+
+       rpc_shutdown_client(client);
+       return mnt;
+}
index ab985f6f0da8d93da67f625ba8027ff851678c55..d48dbefa0e71ebf6d9ac90edbb893364afd2d0a3 100644 (file)
@@ -64,6 +64,7 @@
 #include "iostat.h"
 #include "callback.h"
 #include "pnfs.h"
+#include "netns.h"
 
 #define NFSDBG_FACILITY                NFSDBG_PROC
 
@@ -80,6 +81,7 @@ static int _nfs4_recover_proc_open(struct nfs4_opendata *data);
 static int nfs4_do_fsinfo(struct nfs_server *, struct nfs_fh *, struct nfs_fsinfo *);
 static int nfs4_async_handle_error(struct rpc_task *, const struct nfs_server *, struct nfs4_state *);
 static void nfs_fixup_referral_attributes(struct nfs_fattr *fattr);
+static int nfs4_proc_getattr(struct nfs_server *, struct nfs_fh *, struct nfs_fattr *);
 static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr);
 static int nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred,
                            struct nfs_fattr *fattr, struct iattr *sattr,
@@ -101,6 +103,8 @@ static int nfs4_map_errors(int err)
        case -NFS4ERR_BADOWNER:
        case -NFS4ERR_BADNAME:
                return -EINVAL;
+       case -NFS4ERR_SHARE_DENIED:
+               return -EACCES;
        default:
                dprintk("%s could not handle NFSv4 error %d\n",
                                __func__, -err);
@@ -304,7 +308,7 @@ static int nfs4_handle_exception(struct nfs_server *server, int errorcode, struc
                case -NFS4ERR_SEQ_MISORDERED:
                        dprintk("%s ERROR: %d Reset session\n", __func__,
                                errorcode);
-                       nfs4_schedule_session_recovery(clp->cl_session);
+                       nfs4_schedule_session_recovery(clp->cl_session, errorcode);
                        exception->retry = 1;
                        break;
 #endif /* defined(CONFIG_NFS_V4_1) */
@@ -772,7 +776,7 @@ static void update_changeattr(struct inode *dir, struct nfs4_change_info *cinfo)
        struct nfs_inode *nfsi = NFS_I(dir);
 
        spin_lock(&dir->i_lock);
-       nfsi->cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE|NFS_INO_INVALID_DATA;
+       nfsi->cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA;
        if (!cinfo->atomic || cinfo->before != dir->i_version)
                nfs_force_lookup_revalidate(dir);
        dir->i_version = cinfo->after;
@@ -788,7 +792,6 @@ struct nfs4_opendata {
        struct nfs4_string owner_name;
        struct nfs4_string group_name;
        struct nfs_fattr f_attr;
-       struct nfs_fattr dir_attr;
        struct dentry *dir;
        struct dentry *dentry;
        struct nfs4_state_owner *owner;
@@ -804,12 +807,10 @@ struct nfs4_opendata {
 static void nfs4_init_opendata_res(struct nfs4_opendata *p)
 {
        p->o_res.f_attr = &p->f_attr;
-       p->o_res.dir_attr = &p->dir_attr;
        p->o_res.seqid = p->o_arg.seqid;
        p->c_res.seqid = p->c_arg.seqid;
        p->o_res.server = p->o_arg.server;
        nfs_fattr_init(&p->f_attr);
-       nfs_fattr_init(&p->dir_attr);
        nfs_fattr_init_names(&p->f_attr, &p->owner_name, &p->group_name);
 }
 
@@ -843,7 +844,6 @@ static struct nfs4_opendata *nfs4_opendata_alloc(struct dentry *dentry,
        p->o_arg.name = &dentry->d_name;
        p->o_arg.server = server;
        p->o_arg.bitmask = server->attr_bitmask;
-       p->o_arg.dir_bitmask = server->cache_consistency_bitmask;
        p->o_arg.claim = NFS4_OPEN_CLAIM_NULL;
        if (attrs != NULL && attrs->ia_valid != 0) {
                __be32 verf[2];
@@ -1332,7 +1332,7 @@ int nfs4_open_delegation_recall(struct nfs_open_context *ctx, struct nfs4_state
                        case -NFS4ERR_BAD_HIGH_SLOT:
                        case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION:
                        case -NFS4ERR_DEADSESSION:
-                               nfs4_schedule_session_recovery(server->nfs_client->cl_session);
+                               nfs4_schedule_session_recovery(server->nfs_client->cl_session, err);
                                goto out;
                        case -NFS4ERR_STALE_CLIENTID:
                        case -NFS4ERR_STALE_STATEID:
@@ -1611,8 +1611,6 @@ static int _nfs4_recover_proc_open(struct nfs4_opendata *data)
 
        nfs_fattr_map_and_free_names(NFS_SERVER(dir), &data->f_attr);
 
-       nfs_refresh_inode(dir, o_res->dir_attr);
-
        if (o_res->rflags & NFS4_OPEN_RESULT_CONFIRM) {
                status = _nfs4_proc_open_confirm(data);
                if (status != 0)
@@ -1645,11 +1643,8 @@ static int _nfs4_proc_open(struct nfs4_opendata *data)
 
        nfs_fattr_map_and_free_names(server, &data->f_attr);
 
-       if (o_arg->open_flags & O_CREAT) {
+       if (o_arg->open_flags & O_CREAT)
                update_changeattr(dir, &o_res->cinfo);
-               nfs_post_op_update_inode(dir, o_res->dir_attr);
-       } else
-               nfs_refresh_inode(dir, o_res->dir_attr);
        if ((o_res->rflags & NFS4_OPEN_RESULT_LOCKTYPE_POSIX) == 0)
                server->caps &= ~NFS_CAP_POSIX_LOCK;
        if(o_res->rflags & NFS4_OPEN_RESULT_CONFIRM) {
@@ -1789,7 +1784,14 @@ static inline void nfs4_exclusive_attrset(struct nfs4_opendata *opendata, struct
 /*
  * Returns a referenced nfs4_state
  */
-static int _nfs4_do_open(struct inode *dir, struct dentry *dentry, fmode_t fmode, int flags, struct iattr *sattr, struct rpc_cred *cred, struct nfs4_state **res)
+static int _nfs4_do_open(struct inode *dir,
+                       struct dentry *dentry,
+                       fmode_t fmode,
+                       int flags,
+                       struct iattr *sattr,
+                       struct rpc_cred *cred,
+                       struct nfs4_state **res,
+                       struct nfs4_threshold **ctx_th)
 {
        struct nfs4_state_owner  *sp;
        struct nfs4_state     *state = NULL;
@@ -1814,6 +1816,11 @@ static int _nfs4_do_open(struct inode *dir, struct dentry *dentry, fmode_t fmode
        if (opendata == NULL)
                goto err_put_state_owner;
 
+       if (ctx_th && server->attr_bitmask[2] & FATTR4_WORD2_MDSTHRESHOLD) {
+               opendata->f_attr.mdsthreshold = pnfs_mdsthreshold_alloc();
+               if (!opendata->f_attr.mdsthreshold)
+                       goto err_opendata_put;
+       }
        if (dentry->d_inode != NULL)
                opendata->state = nfs4_get_open_state(dentry->d_inode, sp);
 
@@ -1839,11 +1846,19 @@ static int _nfs4_do_open(struct inode *dir, struct dentry *dentry, fmode_t fmode
                        nfs_setattr_update_inode(state->inode, sattr);
                nfs_post_op_update_inode(state->inode, opendata->o_res.f_attr);
        }
+
+       if (pnfs_use_threshold(ctx_th, opendata->f_attr.mdsthreshold, server))
+               *ctx_th = opendata->f_attr.mdsthreshold;
+       else
+               kfree(opendata->f_attr.mdsthreshold);
+       opendata->f_attr.mdsthreshold = NULL;
+
        nfs4_opendata_put(opendata);
        nfs4_put_state_owner(sp);
        *res = state;
        return 0;
 err_opendata_put:
+       kfree(opendata->f_attr.mdsthreshold);
        nfs4_opendata_put(opendata);
 err_put_state_owner:
        nfs4_put_state_owner(sp);
@@ -1853,14 +1868,21 @@ out_err:
 }
 
 
-static struct nfs4_state *nfs4_do_open(struct inode *dir, struct dentry *dentry, fmode_t fmode, int flags, struct iattr *sattr, struct rpc_cred *cred)
+static struct nfs4_state *nfs4_do_open(struct inode *dir,
+                                       struct dentry *dentry,
+                                       fmode_t fmode,
+                                       int flags,
+                                       struct iattr *sattr,
+                                       struct rpc_cred *cred,
+                                       struct nfs4_threshold **ctx_th)
 {
        struct nfs4_exception exception = { };
        struct nfs4_state *res;
        int status;
 
        do {
-               status = _nfs4_do_open(dir, dentry, fmode, flags, sattr, cred, &res);
+               status = _nfs4_do_open(dir, dentry, fmode, flags, sattr, cred,
+                                      &res, ctx_th);
                if (status == 0)
                        break;
                /* NOTE: BAD_SEQID means the server and client disagree about the
@@ -2184,7 +2206,8 @@ nfs4_atomic_open(struct inode *dir, struct nfs_open_context *ctx, int open_flags
        struct nfs4_state *state;
 
        /* Protect against concurrent sillydeletes */
-       state = nfs4_do_open(dir, ctx->dentry, ctx->mode, open_flags, attr, ctx->cred);
+       state = nfs4_do_open(dir, ctx->dentry, ctx->mode, open_flags, attr,
+                            ctx->cred, &ctx->mdsthreshold);
        if (IS_ERR(state))
                return ERR_CAST(state);
        ctx->state = state;
@@ -2354,8 +2377,8 @@ static int nfs4_find_root_sec(struct nfs_server *server, struct nfs_fh *fhandle,
 /*
  * get the file handle for the "/" directory on the server
  */
-static int nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle,
-                             struct nfs_fsinfo *info)
+int nfs4_proc_get_rootfh(struct nfs_server *server, struct nfs_fh *fhandle,
+                        struct nfs_fsinfo *info)
 {
        int minor_version = server->nfs_client->cl_minorversion;
        int status = nfs4_lookup_root(server, fhandle, info);
@@ -2372,6 +2395,31 @@ static int nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle,
        return nfs4_map_errors(status);
 }
 
+static int nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *mntfh,
+                             struct nfs_fsinfo *info)
+{
+       int error;
+       struct nfs_fattr *fattr = info->fattr;
+
+       error = nfs4_server_capabilities(server, mntfh);
+       if (error < 0) {
+               dprintk("nfs4_get_root: getcaps error = %d\n", -error);
+               return error;
+       }
+
+       error = nfs4_proc_getattr(server, mntfh, fattr);
+       if (error < 0) {
+               dprintk("nfs4_get_root: getattr error = %d\n", -error);
+               return error;
+       }
+
+       if (fattr->valid & NFS_ATTR_FATTR_FSID &&
+           !nfs_fsid_equal(&server->fsid, &fattr->fsid))
+               memcpy(&server->fsid, &fattr->fsid, sizeof(server->fsid));
+
+       return error;
+}
+
 /*
  * Get locations and (maybe) other attributes of a referral.
  * Note that we'll actually follow the referral later when
@@ -2578,7 +2626,7 @@ out:
        return err;
 }
 
-static int nfs4_proc_lookup(struct rpc_clnt *clnt, struct inode *dir, struct qstr *name,
+static int nfs4_proc_lookup(struct inode *dir, struct qstr *name,
                            struct nfs_fh *fhandle, struct nfs_fattr *fattr)
 {
        int status;
@@ -2761,7 +2809,7 @@ nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
                fmode = ctx->mode;
        }
        sattr->ia_mode &= ~current_umask();
-       state = nfs4_do_open(dir, de, fmode, flags, sattr, cred);
+       state = nfs4_do_open(dir, de, fmode, flags, sattr, cred, NULL);
        d_drop(dentry);
        if (IS_ERR(state)) {
                status = PTR_ERR(state);
@@ -2783,7 +2831,6 @@ static int _nfs4_proc_remove(struct inode *dir, struct qstr *name)
        struct nfs_removeargs args = {
                .fh = NFS_FH(dir),
                .name = *name,
-               .bitmask = server->attr_bitmask,
        };
        struct nfs_removeres res = {
                .server = server,
@@ -2793,19 +2840,11 @@ static int _nfs4_proc_remove(struct inode *dir, struct qstr *name)
                .rpc_argp = &args,
                .rpc_resp = &res,
        };
-       int status = -ENOMEM;
-
-       res.dir_attr = nfs_alloc_fattr();
-       if (res.dir_attr == NULL)
-               goto out;
+       int status;
 
        status = nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 1);
-       if (status == 0) {
+       if (status == 0)
                update_changeattr(dir, &res.cinfo);
-               nfs_post_op_update_inode(dir, res.dir_attr);
-       }
-       nfs_free_fattr(res.dir_attr);
-out:
        return status;
 }
 
@@ -2827,7 +2866,6 @@ static void nfs4_proc_unlink_setup(struct rpc_message *msg, struct inode *dir)
        struct nfs_removeargs *args = msg->rpc_argp;
        struct nfs_removeres *res = msg->rpc_resp;
 
-       args->bitmask = server->cache_consistency_bitmask;
        res->server = server;
        msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_REMOVE];
        nfs41_init_sequence(&args->seq_args, &res->seq_res, 1);
@@ -2852,7 +2890,6 @@ static int nfs4_proc_unlink_done(struct rpc_task *task, struct inode *dir)
        if (nfs4_async_handle_error(task, res->server, NULL) == -EAGAIN)
                return 0;
        update_changeattr(dir, &res->cinfo);
-       nfs_post_op_update_inode(dir, res->dir_attr);
        return 1;
 }
 
@@ -2863,7 +2900,6 @@ static void nfs4_proc_rename_setup(struct rpc_message *msg, struct inode *dir)
        struct nfs_renameres *res = msg->rpc_resp;
 
        msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RENAME];
-       arg->bitmask = server->attr_bitmask;
        res->server = server;
        nfs41_init_sequence(&arg->seq_args, &res->seq_res, 1);
 }
@@ -2889,9 +2925,7 @@ static int nfs4_proc_rename_done(struct rpc_task *task, struct inode *old_dir,
                return 0;
 
        update_changeattr(old_dir, &res->old_cinfo);
-       nfs_post_op_update_inode(old_dir, res->old_fattr);
        update_changeattr(new_dir, &res->new_cinfo);
-       nfs_post_op_update_inode(new_dir, res->new_fattr);
        return 1;
 }
 
@@ -2904,7 +2938,6 @@ static int _nfs4_proc_rename(struct inode *old_dir, struct qstr *old_name,
                .new_dir = NFS_FH(new_dir),
                .old_name = old_name,
                .new_name = new_name,
-               .bitmask = server->attr_bitmask,
        };
        struct nfs_renameres res = {
                .server = server,
@@ -2916,21 +2949,11 @@ static int _nfs4_proc_rename(struct inode *old_dir, struct qstr *old_name,
        };
        int status = -ENOMEM;
        
-       res.old_fattr = nfs_alloc_fattr();
-       res.new_fattr = nfs_alloc_fattr();
-       if (res.old_fattr == NULL || res.new_fattr == NULL)
-               goto out;
-
        status = nfs4_call_sync(server->client, server, &msg, &arg.seq_args, &res.seq_res, 1);
        if (!status) {
                update_changeattr(old_dir, &res.old_cinfo);
-               nfs_post_op_update_inode(old_dir, res.old_fattr);
                update_changeattr(new_dir, &res.new_cinfo);
-               nfs_post_op_update_inode(new_dir, res.new_fattr);
        }
-out:
-       nfs_free_fattr(res.new_fattr);
-       nfs_free_fattr(res.old_fattr);
        return status;
 }
 
@@ -2968,18 +2991,15 @@ static int _nfs4_proc_link(struct inode *inode, struct inode *dir, struct qstr *
        int status = -ENOMEM;
 
        res.fattr = nfs_alloc_fattr();
-       res.dir_attr = nfs_alloc_fattr();
-       if (res.fattr == NULL || res.dir_attr == NULL)
+       if (res.fattr == NULL)
                goto out;
 
        status = nfs4_call_sync(server->client, server, &msg, &arg.seq_args, &res.seq_res, 1);
        if (!status) {
                update_changeattr(dir, &res.cinfo);
-               nfs_post_op_update_inode(dir, res.dir_attr);
                nfs_post_op_update_inode(inode, res.fattr);
        }
 out:
-       nfs_free_fattr(res.dir_attr);
        nfs_free_fattr(res.fattr);
        return status;
 }
@@ -3002,7 +3022,6 @@ struct nfs4_createdata {
        struct nfs4_create_res res;
        struct nfs_fh fh;
        struct nfs_fattr fattr;
-       struct nfs_fattr dir_fattr;
 };
 
 static struct nfs4_createdata *nfs4_alloc_createdata(struct inode *dir,
@@ -3026,9 +3045,7 @@ static struct nfs4_createdata *nfs4_alloc_createdata(struct inode *dir,
                data->res.server = server;
                data->res.fh = &data->fh;
                data->res.fattr = &data->fattr;
-               data->res.dir_fattr = &data->dir_fattr;
                nfs_fattr_init(data->res.fattr);
-               nfs_fattr_init(data->res.dir_fattr);
        }
        return data;
 }
@@ -3039,7 +3056,6 @@ static int nfs4_do_create(struct inode *dir, struct dentry *dentry, struct nfs4_
                                    &data->arg.seq_args, &data->res.seq_res, 1);
        if (status == 0) {
                update_changeattr(dir, &data->res.dir_cinfo);
-               nfs_post_op_update_inode(dir, data->res.dir_fattr);
                status = nfs_instantiate(dentry, data->res.fh, data->res.fattr);
        }
        return status;
@@ -3335,12 +3351,12 @@ static int nfs4_proc_pathconf(struct nfs_server *server, struct nfs_fh *fhandle,
 
 void __nfs4_read_done_cb(struct nfs_read_data *data)
 {
-       nfs_invalidate_atime(data->inode);
+       nfs_invalidate_atime(data->header->inode);
 }
 
 static int nfs4_read_done_cb(struct rpc_task *task, struct nfs_read_data *data)
 {
-       struct nfs_server *server = NFS_SERVER(data->inode);
+       struct nfs_server *server = NFS_SERVER(data->header->inode);
 
        if (nfs4_async_handle_error(task, server, data->args.context->state) == -EAGAIN) {
                rpc_restart_call_prepare(task);
@@ -3375,7 +3391,7 @@ static void nfs4_proc_read_setup(struct nfs_read_data *data, struct rpc_message
 
 static void nfs4_proc_read_rpc_prepare(struct rpc_task *task, struct nfs_read_data *data)
 {
-       if (nfs4_setup_sequence(NFS_SERVER(data->inode),
+       if (nfs4_setup_sequence(NFS_SERVER(data->header->inode),
                                &data->args.seq_args,
                                &data->res.seq_res,
                                task))
@@ -3383,25 +3399,9 @@ static void nfs4_proc_read_rpc_prepare(struct rpc_task *task, struct nfs_read_da
        rpc_call_start(task);
 }
 
-/* Reset the the nfs_read_data to send the read to the MDS. */
-void nfs4_reset_read(struct rpc_task *task, struct nfs_read_data *data)
-{
-       dprintk("%s Reset task for i/o through\n", __func__);
-       put_lseg(data->lseg);
-       data->lseg = NULL;
-       /* offsets will differ in the dense stripe case */
-       data->args.offset = data->mds_offset;
-       data->ds_clp = NULL;
-       data->args.fh     = NFS_FH(data->inode);
-       data->read_done_cb = nfs4_read_done_cb;
-       task->tk_ops = data->mds_ops;
-       rpc_task_reset_client(task, NFS_CLIENT(data->inode));
-}
-EXPORT_SYMBOL_GPL(nfs4_reset_read);
-
 static int nfs4_write_done_cb(struct rpc_task *task, struct nfs_write_data *data)
 {
-       struct inode *inode = data->inode;
+       struct inode *inode = data->header->inode;
        
        if (nfs4_async_handle_error(task, NFS_SERVER(inode), data->args.context->state) == -EAGAIN) {
                rpc_restart_call_prepare(task);
@@ -3409,7 +3409,7 @@ static int nfs4_write_done_cb(struct rpc_task *task, struct nfs_write_data *data
        }
        if (task->tk_status >= 0) {
                renew_lease(NFS_SERVER(inode), data->timestamp);
-               nfs_post_op_update_inode_force_wcc(inode, data->res.fattr);
+               nfs_post_op_update_inode_force_wcc(inode, &data->fattr);
        }
        return 0;
 }
@@ -3422,32 +3422,30 @@ static int nfs4_write_done(struct rpc_task *task, struct nfs_write_data *data)
                nfs4_write_done_cb(task, data);
 }
 
-/* Reset the the nfs_write_data to send the write to the MDS. */
-void nfs4_reset_write(struct rpc_task *task, struct nfs_write_data *data)
+static
+bool nfs4_write_need_cache_consistency_data(const struct nfs_write_data *data)
 {
-       dprintk("%s Reset task for i/o through\n", __func__);
-       put_lseg(data->lseg);
-       data->lseg          = NULL;
-       data->ds_clp        = NULL;
-       data->write_done_cb = nfs4_write_done_cb;
-       data->args.fh       = NFS_FH(data->inode);
-       data->args.bitmask  = data->res.server->cache_consistency_bitmask;
-       data->args.offset   = data->mds_offset;
-       data->res.fattr     = &data->fattr;
-       task->tk_ops        = data->mds_ops;
-       rpc_task_reset_client(task, NFS_CLIENT(data->inode));
+       const struct nfs_pgio_header *hdr = data->header;
+
+       /* Don't request attributes for pNFS or O_DIRECT writes */
+       if (data->ds_clp != NULL || hdr->dreq != NULL)
+               return false;
+       /* Otherwise, request attributes if and only if we don't hold
+        * a delegation
+        */
+       return nfs_have_delegation(hdr->inode, FMODE_READ) == 0;
 }
-EXPORT_SYMBOL_GPL(nfs4_reset_write);
 
 static void nfs4_proc_write_setup(struct nfs_write_data *data, struct rpc_message *msg)
 {
-       struct nfs_server *server = NFS_SERVER(data->inode);
+       struct nfs_server *server = NFS_SERVER(data->header->inode);
 
-       if (data->lseg) {
+       if (!nfs4_write_need_cache_consistency_data(data)) {
                data->args.bitmask = NULL;
                data->res.fattr = NULL;
        } else
                data->args.bitmask = server->cache_consistency_bitmask;
+
        if (!data->write_done_cb)
                data->write_done_cb = nfs4_write_done_cb;
        data->res.server = server;
@@ -3458,6 +3456,16 @@ static void nfs4_proc_write_setup(struct nfs_write_data *data, struct rpc_messag
 }
 
 static void nfs4_proc_write_rpc_prepare(struct rpc_task *task, struct nfs_write_data *data)
+{
+       if (nfs4_setup_sequence(NFS_SERVER(data->header->inode),
+                               &data->args.seq_args,
+                               &data->res.seq_res,
+                               task))
+               return;
+       rpc_call_start(task);
+}
+
+static void nfs4_proc_commit_rpc_prepare(struct rpc_task *task, struct nfs_commit_data *data)
 {
        if (nfs4_setup_sequence(NFS_SERVER(data->inode),
                                &data->args.seq_args,
@@ -3467,7 +3475,7 @@ static void nfs4_proc_write_rpc_prepare(struct rpc_task *task, struct nfs_write_
        rpc_call_start(task);
 }
 
-static int nfs4_commit_done_cb(struct rpc_task *task, struct nfs_write_data *data)
+static int nfs4_commit_done_cb(struct rpc_task *task, struct nfs_commit_data *data)
 {
        struct inode *inode = data->inode;
 
@@ -3475,28 +3483,22 @@ static int nfs4_commit_done_cb(struct rpc_task *task, struct nfs_write_data *dat
                rpc_restart_call_prepare(task);
                return -EAGAIN;
        }
-       nfs_refresh_inode(inode, data->res.fattr);
        return 0;
 }
 
-static int nfs4_commit_done(struct rpc_task *task, struct nfs_write_data *data)
+static int nfs4_commit_done(struct rpc_task *task, struct nfs_commit_data *data)
 {
        if (!nfs4_sequence_done(task, &data->res.seq_res))
                return -EAGAIN;
-       return data->write_done_cb(task, data);
+       return data->commit_done_cb(task, data);
 }
 
-static void nfs4_proc_commit_setup(struct nfs_write_data *data, struct rpc_message *msg)
+static void nfs4_proc_commit_setup(struct nfs_commit_data *data, struct rpc_message *msg)
 {
        struct nfs_server *server = NFS_SERVER(data->inode);
 
-       if (data->lseg) {
-               data->args.bitmask = NULL;
-               data->res.fattr = NULL;
-       } else
-               data->args.bitmask = server->cache_consistency_bitmask;
-       if (!data->write_done_cb)
-               data->write_done_cb = nfs4_commit_done_cb;
+       if (data->commit_done_cb == NULL)
+               data->commit_done_cb = nfs4_commit_done_cb;
        data->res.server = server;
        msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_COMMIT];
        nfs41_init_sequence(&data->args.seq_args, &data->res.seq_res, 1);
@@ -3905,7 +3907,7 @@ nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server,
                case -NFS4ERR_SEQ_MISORDERED:
                        dprintk("%s ERROR %d, Reset session\n", __func__,
                                task->tk_status);
-                       nfs4_schedule_session_recovery(clp->cl_session);
+                       nfs4_schedule_session_recovery(clp->cl_session, task->tk_status);
                        task->tk_status = 0;
                        return -EAGAIN;
 #endif /* CONFIG_NFS_V4_1 */
@@ -3931,13 +3933,21 @@ wait_on_recovery:
        return -EAGAIN;
 }
 
-static void nfs4_construct_boot_verifier(struct nfs_client *clp,
-                                        nfs4_verifier *bootverf)
+static void nfs4_init_boot_verifier(const struct nfs_client *clp,
+                                   nfs4_verifier *bootverf)
 {
        __be32 verf[2];
 
-       verf[0] = htonl((u32)clp->cl_boot_time.tv_sec);
-       verf[1] = htonl((u32)clp->cl_boot_time.tv_nsec);
+       if (test_bit(NFS4CLNT_PURGE_STATE, &clp->cl_state)) {
+               /* An impossible timestamp guarantees this value
+                * will never match a generated boot time. */
+               verf[0] = 0;
+               verf[1] = (__be32)(NSEC_PER_SEC + 1);
+       } else {
+               struct nfs_net *nn = net_generic(clp->cl_net, nfs_net_id);
+               verf[0] = (__be32)nn->boot_time.tv_sec;
+               verf[1] = (__be32)nn->boot_time.tv_nsec;
+       }
        memcpy(bootverf->data, verf, sizeof(bootverf->data));
 }
 
@@ -3960,7 +3970,7 @@ int nfs4_proc_setclientid(struct nfs_client *clp, u32 program,
        int loop = 0;
        int status;
 
-       nfs4_construct_boot_verifier(clp, &sc_verifier);
+       nfs4_init_boot_verifier(clp, &sc_verifier);
 
        for(;;) {
                rcu_read_lock();
@@ -4104,7 +4114,7 @@ static int _nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, co
        nfs41_init_sequence(&data->args.seq_args, &data->res.seq_res, 1);
        data->args.fhandle = &data->fh;
        data->args.stateid = &data->stateid;
-       data->args.bitmask = server->attr_bitmask;
+       data->args.bitmask = server->cache_consistency_bitmask;
        nfs_copy_fh(&data->fh, NFS_FH(inode));
        nfs4_stateid_copy(&data->stateid, stateid);
        data->res.fattr = &data->fattr;
@@ -4125,9 +4135,10 @@ static int _nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, co
        if (status != 0)
                goto out;
        status = data->rpc_status;
-       if (status != 0)
-               goto out;
-       nfs_refresh_inode(inode, &data->fattr);
+       if (status == 0)
+               nfs_post_op_update_inode_force_wcc(inode, &data->fattr);
+       else
+               nfs_refresh_inode(inode, &data->fattr);
 out:
        rpc_put_task(task);
        return status;
@@ -4837,7 +4848,7 @@ int nfs4_lock_delegation_recall(struct nfs4_state *state, struct file_lock *fl)
                        case -NFS4ERR_BAD_HIGH_SLOT:
                        case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION:
                        case -NFS4ERR_DEADSESSION:
-                               nfs4_schedule_session_recovery(server->nfs_client->cl_session);
+                               nfs4_schedule_session_recovery(server->nfs_client->cl_session, err);
                                goto out;
                        case -ERESTARTSYS:
                                /*
@@ -5079,7 +5090,8 @@ out_inval:
 }
 
 static bool
-nfs41_same_server_scope(struct server_scope *a, struct server_scope *b)
+nfs41_same_server_scope(struct nfs41_server_scope *a,
+                       struct nfs41_server_scope *b)
 {
        if (a->server_scope_sz == b->server_scope_sz &&
            memcmp(a->server_scope, b->server_scope, a->server_scope_sz) == 0)
@@ -5088,6 +5100,61 @@ nfs41_same_server_scope(struct server_scope *a, struct server_scope *b)
        return false;
 }
 
+/*
+ * nfs4_proc_bind_conn_to_session()
+ *
+ * The 4.1 client currently uses the same TCP connection for the
+ * fore and backchannel.
+ */
+int nfs4_proc_bind_conn_to_session(struct nfs_client *clp, struct rpc_cred *cred)
+{
+       int status;
+       struct nfs41_bind_conn_to_session_res res;
+       struct rpc_message msg = {
+               .rpc_proc =
+                       &nfs4_procedures[NFSPROC4_CLNT_BIND_CONN_TO_SESSION],
+               .rpc_argp = clp,
+               .rpc_resp = &res,
+               .rpc_cred = cred,
+       };
+
+       dprintk("--> %s\n", __func__);
+       BUG_ON(clp == NULL);
+
+       res.session = kzalloc(sizeof(struct nfs4_session), GFP_NOFS);
+       if (unlikely(res.session == NULL)) {
+               status = -ENOMEM;
+               goto out;
+       }
+
+       status = rpc_call_sync(clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT);
+       if (status == 0) {
+               if (memcmp(res.session->sess_id.data,
+                   clp->cl_session->sess_id.data, NFS4_MAX_SESSIONID_LEN)) {
+                       dprintk("NFS: %s: Session ID mismatch\n", __func__);
+                       status = -EIO;
+                       goto out_session;
+               }
+               if (res.dir != NFS4_CDFS4_BOTH) {
+                       dprintk("NFS: %s: Unexpected direction from server\n",
+                               __func__);
+                       status = -EIO;
+                       goto out_session;
+               }
+               if (res.use_conn_in_rdma_mode) {
+                       dprintk("NFS: %s: Server returned RDMA mode = true\n",
+                               __func__);
+                       status = -EIO;
+                       goto out_session;
+               }
+       }
+out_session:
+       kfree(res.session);
+out:
+       dprintk("<-- %s status= %d\n", __func__, status);
+       return status;
+}
+
 /*
  * nfs4_proc_exchange_id()
  *
@@ -5105,7 +5172,7 @@ int nfs4_proc_exchange_id(struct nfs_client *clp, struct rpc_cred *cred)
                .flags = EXCHGID4_FLAG_SUPP_MOVED_REFER,
        };
        struct nfs41_exchange_id_res res = {
-               .client = clp,
+               0
        };
        int status;
        struct rpc_message msg = {
@@ -5118,7 +5185,7 @@ int nfs4_proc_exchange_id(struct nfs_client *clp, struct rpc_cred *cred)
        dprintk("--> %s\n", __func__);
        BUG_ON(clp == NULL);
 
-       nfs4_construct_boot_verifier(clp, &verifier);
+       nfs4_init_boot_verifier(clp, &verifier);
 
        args.id_len = scnprintf(args.id, sizeof(args.id),
                                "%s/%s/%u",
@@ -5126,59 +5193,135 @@ int nfs4_proc_exchange_id(struct nfs_client *clp, struct rpc_cred *cred)
                                clp->cl_rpcclient->cl_nodename,
                                clp->cl_rpcclient->cl_auth->au_flavor);
 
-       res.server_scope = kzalloc(sizeof(struct server_scope), GFP_KERNEL);
-       if (unlikely(!res.server_scope)) {
+       res.server_owner = kzalloc(sizeof(struct nfs41_server_owner),
+                                       GFP_NOFS);
+       if (unlikely(res.server_owner == NULL)) {
                status = -ENOMEM;
                goto out;
        }
 
-       res.impl_id = kzalloc(sizeof(struct nfs41_impl_id), GFP_KERNEL);
-       if (unlikely(!res.impl_id)) {
+       res.server_scope = kzalloc(sizeof(struct nfs41_server_scope),
+                                       GFP_NOFS);
+       if (unlikely(res.server_scope == NULL)) {
+               status = -ENOMEM;
+               goto out_server_owner;
+       }
+
+       res.impl_id = kzalloc(sizeof(struct nfs41_impl_id), GFP_NOFS);
+       if (unlikely(res.impl_id == NULL)) {
                status = -ENOMEM;
                goto out_server_scope;
        }
 
        status = rpc_call_sync(clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT);
-       if (!status)
-               status = nfs4_check_cl_exchange_flags(clp->cl_exchange_flags);
+       if (status == 0)
+               status = nfs4_check_cl_exchange_flags(res.flags);
+
+       if (status == 0) {
+               clp->cl_clientid = res.clientid;
+               clp->cl_exchange_flags = (res.flags & ~EXCHGID4_FLAG_CONFIRMED_R);
+               if (!(res.flags & EXCHGID4_FLAG_CONFIRMED_R))
+                       clp->cl_seqid = res.seqid;
+
+               kfree(clp->cl_serverowner);
+               clp->cl_serverowner = res.server_owner;
+               res.server_owner = NULL;
 
-       if (!status) {
                /* use the most recent implementation id */
-               kfree(clp->impl_id);
-               clp->impl_id = res.impl_id;
-       } else
-               kfree(res.impl_id);
+               kfree(clp->cl_implid);
+               clp->cl_implid = res.impl_id;
 
-       if (!status) {
-               if (clp->server_scope &&
-                   !nfs41_same_server_scope(clp->server_scope,
+               if (clp->cl_serverscope != NULL &&
+                   !nfs41_same_server_scope(clp->cl_serverscope,
                                             res.server_scope)) {
                        dprintk("%s: server_scope mismatch detected\n",
                                __func__);
                        set_bit(NFS4CLNT_SERVER_SCOPE_MISMATCH, &clp->cl_state);
-                       kfree(clp->server_scope);
-                       clp->server_scope = NULL;
+                       kfree(clp->cl_serverscope);
+                       clp->cl_serverscope = NULL;
                }
 
-               if (!clp->server_scope) {
-                       clp->server_scope = res.server_scope;
+               if (clp->cl_serverscope == NULL) {
+                       clp->cl_serverscope = res.server_scope;
                        goto out;
                }
-       }
+       } else
+               kfree(res.impl_id);
 
+out_server_owner:
+       kfree(res.server_owner);
 out_server_scope:
        kfree(res.server_scope);
 out:
-       if (clp->impl_id)
+       if (clp->cl_implid != NULL)
                dprintk("%s: Server Implementation ID: "
                        "domain: %s, name: %s, date: %llu,%u\n",
-                       __func__, clp->impl_id->domain, clp->impl_id->name,
-                       clp->impl_id->date.seconds,
-                       clp->impl_id->date.nseconds);
+                       __func__, clp->cl_implid->domain, clp->cl_implid->name,
+                       clp->cl_implid->date.seconds,
+                       clp->cl_implid->date.nseconds);
        dprintk("<-- %s status= %d\n", __func__, status);
        return status;
 }
 
+static int _nfs4_proc_destroy_clientid(struct nfs_client *clp,
+               struct rpc_cred *cred)
+{
+       struct rpc_message msg = {
+               .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_DESTROY_CLIENTID],
+               .rpc_argp = clp,
+               .rpc_cred = cred,
+       };
+       int status;
+
+       status = rpc_call_sync(clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT);
+       if (status)
+               pr_warn("NFS: Got error %d from the server %s on "
+                       "DESTROY_CLIENTID.", status, clp->cl_hostname);
+       return status;
+}
+
+static int nfs4_proc_destroy_clientid(struct nfs_client *clp,
+               struct rpc_cred *cred)
+{
+       unsigned int loop;
+       int ret;
+
+       for (loop = NFS4_MAX_LOOP_ON_RECOVER; loop != 0; loop--) {
+               ret = _nfs4_proc_destroy_clientid(clp, cred);
+               switch (ret) {
+               case -NFS4ERR_DELAY:
+               case -NFS4ERR_CLIENTID_BUSY:
+                       ssleep(1);
+                       break;
+               default:
+                       return ret;
+               }
+       }
+       return 0;
+}
+
+int nfs4_destroy_clientid(struct nfs_client *clp)
+{
+       struct rpc_cred *cred;
+       int ret = 0;
+
+       if (clp->cl_mvops->minor_version < 1)
+               goto out;
+       if (clp->cl_exchange_flags == 0)
+               goto out;
+       cred = nfs4_get_exchange_id_cred(clp);
+       ret = nfs4_proc_destroy_clientid(clp, cred);
+       if (cred)
+               put_rpccred(cred);
+       switch (ret) {
+       case 0:
+       case -NFS4ERR_STALE_CLIENTID:
+               clp->cl_exchange_flags = 0;
+       }
+out:
+       return ret;
+}
+
 struct nfs4_get_lease_time_data {
        struct nfs4_get_lease_time_args *args;
        struct nfs4_get_lease_time_res *res;
@@ -5399,8 +5542,12 @@ struct nfs4_session *nfs4_alloc_session(struct nfs_client *clp)
 void nfs4_destroy_session(struct nfs4_session *session)
 {
        struct rpc_xprt *xprt;
+       struct rpc_cred *cred;
 
-       nfs4_proc_destroy_session(session);
+       cred = nfs4_get_exchange_id_cred(session->clp);
+       nfs4_proc_destroy_session(session, cred);
+       if (cred)
+               put_rpccred(cred);
 
        rcu_read_lock();
        xprt = rcu_dereference(session->clp->cl_rpcclient->cl_xprt);
@@ -5510,7 +5657,8 @@ static int nfs4_verify_channel_attrs(struct nfs41_create_session_args *args,
        return nfs4_verify_back_channel_attrs(args, session);
 }
 
-static int _nfs4_proc_create_session(struct nfs_client *clp)
+static int _nfs4_proc_create_session(struct nfs_client *clp,
+               struct rpc_cred *cred)
 {
        struct nfs4_session *session = clp->cl_session;
        struct nfs41_create_session_args args = {
@@ -5524,6 +5672,7 @@ static int _nfs4_proc_create_session(struct nfs_client *clp)
                .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CREATE_SESSION],
                .rpc_argp = &args,
                .rpc_resp = &res,
+               .rpc_cred = cred,
        };
        int status;
 
@@ -5548,7 +5697,7 @@ static int _nfs4_proc_create_session(struct nfs_client *clp)
  * It is the responsibility of the caller to verify the session is
  * expired before calling this routine.
  */
-int nfs4_proc_create_session(struct nfs_client *clp)
+int nfs4_proc_create_session(struct nfs_client *clp, struct rpc_cred *cred)
 {
        int status;
        unsigned *ptr;
@@ -5556,7 +5705,7 @@ int nfs4_proc_create_session(struct nfs_client *clp)
 
        dprintk("--> %s clp=%p session=%p\n", __func__, clp, session);
 
-       status = _nfs4_proc_create_session(clp);
+       status = _nfs4_proc_create_session(clp, cred);
        if (status)
                goto out;
 
@@ -5578,10 +5727,15 @@ out:
  * Issue the over-the-wire RPC DESTROY_SESSION.
  * The caller must serialize access to this routine.
  */
-int nfs4_proc_destroy_session(struct nfs4_session *session)
+int nfs4_proc_destroy_session(struct nfs4_session *session,
+               struct rpc_cred *cred)
 {
+       struct rpc_message msg = {
+               .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_DESTROY_SESSION],
+               .rpc_argp = session,
+               .rpc_cred = cred,
+       };
        int status = 0;
-       struct rpc_message msg;
 
        dprintk("--> nfs4_proc_destroy_session\n");
 
@@ -5589,10 +5743,6 @@ int nfs4_proc_destroy_session(struct nfs4_session *session)
        if (session->clp->cl_cons_state != NFS_CS_READY)
                return status;
 
-       msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_DESTROY_SESSION];
-       msg.rpc_argp = session;
-       msg.rpc_resp = NULL;
-       msg.rpc_cred = NULL;
        status = rpc_call_sync(session->clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT);
 
        if (status)
@@ -5604,53 +5754,79 @@ int nfs4_proc_destroy_session(struct nfs4_session *session)
        return status;
 }
 
+/*
+ * With sessions, the client is not marked ready until after a
+ * successful EXCHANGE_ID and CREATE_SESSION.
+ *
+ * Map errors cl_cons_state errors to EPROTONOSUPPORT to indicate
+ * other versions of NFS can be tried.
+ */
+static int nfs41_check_session_ready(struct nfs_client *clp)
+{
+       int ret;
+       
+       if (clp->cl_cons_state == NFS_CS_SESSION_INITING) {
+               ret = nfs4_client_recover_expired_lease(clp);
+               if (ret)
+                       return ret;
+       }
+       if (clp->cl_cons_state < NFS_CS_READY)
+               return -EPROTONOSUPPORT;
+       smp_rmb();
+       return 0;
+}
+
 int nfs4_init_session(struct nfs_server *server)
 {
        struct nfs_client *clp = server->nfs_client;
        struct nfs4_session *session;
        unsigned int rsize, wsize;
-       int ret;
 
        if (!nfs4_has_session(clp))
                return 0;
 
        session = clp->cl_session;
-       if (!test_and_clear_bit(NFS4_SESSION_INITING, &session->session_state))
-               return 0;
+       spin_lock(&clp->cl_lock);
+       if (test_and_clear_bit(NFS4_SESSION_INITING, &session->session_state)) {
 
-       rsize = server->rsize;
-       if (rsize == 0)
-               rsize = NFS_MAX_FILE_IO_SIZE;
-       wsize = server->wsize;
-       if (wsize == 0)
-               wsize = NFS_MAX_FILE_IO_SIZE;
+               rsize = server->rsize;
+               if (rsize == 0)
+                       rsize = NFS_MAX_FILE_IO_SIZE;
+               wsize = server->wsize;
+               if (wsize == 0)
+                       wsize = NFS_MAX_FILE_IO_SIZE;
 
-       session->fc_attrs.max_rqst_sz = wsize + nfs41_maxwrite_overhead;
-       session->fc_attrs.max_resp_sz = rsize + nfs41_maxread_overhead;
+               session->fc_attrs.max_rqst_sz = wsize + nfs41_maxwrite_overhead;
+               session->fc_attrs.max_resp_sz = rsize + nfs41_maxread_overhead;
+       }
+       spin_unlock(&clp->cl_lock);
 
-       ret = nfs4_recover_expired_lease(server);
-       if (!ret)
-               ret = nfs4_check_client_ready(clp);
-       return ret;
+       return nfs41_check_session_ready(clp);
 }
 
-int nfs4_init_ds_session(struct nfs_client *clp)
+int nfs4_init_ds_session(struct nfs_client *clp, unsigned long lease_time)
 {
        struct nfs4_session *session = clp->cl_session;
        int ret;
 
-       if (!test_and_clear_bit(NFS4_SESSION_INITING, &session->session_state))
-               return 0;
-
-       ret = nfs4_client_recover_expired_lease(clp);
-       if (!ret)
-               /* Test for the DS role */
-               if (!is_ds_client(clp))
-                       ret = -ENODEV;
-       if (!ret)
-               ret = nfs4_check_client_ready(clp);
-       return ret;
+       spin_lock(&clp->cl_lock);
+       if (test_and_clear_bit(NFS4_SESSION_INITING, &session->session_state)) {
+               /*
+                * Do not set NFS_CS_CHECK_LEASE_TIME instead set the
+                * DS lease to be equal to the MDS lease.
+                */
+               clp->cl_lease_time = lease_time;
+               clp->cl_last_renewal = jiffies;
+       }
+       spin_unlock(&clp->cl_lock);
 
+       ret = nfs41_check_session_ready(clp);
+       if (ret)
+               return ret;
+       /* Test for the DS role */
+       if (!is_ds_client(clp))
+               return -ENODEV;
+       return 0;
 }
 EXPORT_SYMBOL_GPL(nfs4_init_ds_session);
 
@@ -6557,6 +6733,7 @@ const struct nfs_rpc_ops nfs_v4_clientops = {
        .file_inode_ops = &nfs4_file_inode_operations,
        .file_ops       = &nfs4_file_operations,
        .getroot        = nfs4_proc_get_root,
+       .submount       = nfs4_submount,
        .getattr        = nfs4_proc_getattr,
        .setattr        = nfs4_proc_setattr,
        .lookup         = nfs4_proc_lookup,
@@ -6589,13 +6766,13 @@ const struct nfs_rpc_ops nfs_v4_clientops = {
        .write_rpc_prepare = nfs4_proc_write_rpc_prepare,
        .write_done     = nfs4_write_done,
        .commit_setup   = nfs4_proc_commit_setup,
+       .commit_rpc_prepare = nfs4_proc_commit_rpc_prepare,
        .commit_done    = nfs4_commit_done,
        .lock           = nfs4_proc_lock,
        .clear_acl_cache = nfs4_zap_acl_attr,
        .close_context  = nfs4_close_context,
        .open_context   = nfs4_atomic_open,
        .init_client    = nfs4_init_client,
-       .secinfo        = nfs4_proc_secinfo,
 };
 
 static const struct xattr_handler nfs4_xattr_nfs4_acl_handler = {
index dc484c0eae7f9706716e4a73cabe03857dd3ae15..6930bec91bca22a8f8f7cf0548dcfe9cc6964762 100644 (file)
@@ -49,7 +49,7 @@
 #include "nfs4_fs.h"
 #include "delegation.h"
 
-#define NFSDBG_FACILITY        NFSDBG_PROC
+#define NFSDBG_FACILITY                NFSDBG_STATE
 
 void
 nfs4_renew_state(struct work_struct *work)
index 7f0fcfc1fe9db51e9bc3748f511163dfed7cdce7..c679b9ecef634c80d4738e3cc2a9624f51c327c2 100644 (file)
@@ -57,6 +57,8 @@
 #include "internal.h"
 #include "pnfs.h"
 
+#define NFSDBG_FACILITY                NFSDBG_STATE
+
 #define OPENOWNER_POOL_SIZE    8
 
 const nfs4_stateid zero_stateid;
@@ -254,7 +256,7 @@ int nfs41_init_clientid(struct nfs_client *clp, struct rpc_cred *cred)
                goto out;
        set_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state);
 do_confirm:
-       status = nfs4_proc_create_session(clp);
+       status = nfs4_proc_create_session(clp, cred);
        if (status != 0)
                goto out;
        clear_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state);
@@ -1106,6 +1108,8 @@ void nfs4_schedule_lease_recovery(struct nfs_client *clp)
                return;
        if (!test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state))
                set_bit(NFS4CLNT_CHECK_LEASE, &clp->cl_state);
+       dprintk("%s: scheduling lease recovery for server %s\n", __func__,
+                       clp->cl_hostname);
        nfs4_schedule_state_manager(clp);
 }
 EXPORT_SYMBOL_GPL(nfs4_schedule_lease_recovery);
@@ -1122,6 +1126,8 @@ static void nfs40_handle_cb_pathdown(struct nfs_client *clp)
 {
        set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
        nfs_expire_all_delegations(clp);
+       dprintk("%s: handling CB_PATHDOWN recovery for server %s\n", __func__,
+                       clp->cl_hostname);
 }
 
 void nfs4_schedule_path_down_recovery(struct nfs_client *clp)
@@ -1158,6 +1164,8 @@ void nfs4_schedule_stateid_recovery(const struct nfs_server *server, struct nfs4
        struct nfs_client *clp = server->nfs_client;
 
        nfs4_state_mark_reclaim_nograce(clp, state);
+       dprintk("%s: scheduling stateid recovery for server %s\n", __func__,
+                       clp->cl_hostname);
        nfs4_schedule_state_manager(clp);
 }
 EXPORT_SYMBOL_GPL(nfs4_schedule_stateid_recovery);
@@ -1491,19 +1499,25 @@ static int nfs4_recovery_handle_error(struct nfs_client *clp, int error)
                case -NFS4ERR_BADSLOT:
                case -NFS4ERR_BAD_HIGH_SLOT:
                case -NFS4ERR_DEADSESSION:
-               case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION:
                case -NFS4ERR_SEQ_FALSE_RETRY:
                case -NFS4ERR_SEQ_MISORDERED:
                        set_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state);
                        /* Zero session reset errors */
                        break;
+               case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION:
+                       set_bit(NFS4CLNT_BIND_CONN_TO_SESSION, &clp->cl_state);
+                       break;
                case -EKEYEXPIRED:
                        /* Nothing we can do */
                        nfs4_warn_keyexpired(clp->cl_hostname);
                        break;
                default:
+                       dprintk("%s: failed to handle error %d for server %s\n",
+                                       __func__, error, clp->cl_hostname);
                        return error;
        }
+       dprintk("%s: handled error %d for server %s\n", __func__, error,
+                       clp->cl_hostname);
        return 0;
 }
 
@@ -1572,34 +1586,82 @@ out:
        return nfs4_recovery_handle_error(clp, status);
 }
 
+/* Set NFS4CLNT_LEASE_EXPIRED for all v4.0 errors and for recoverable errors
+ * on EXCHANGE_ID for v4.1
+ */
+static int nfs4_handle_reclaim_lease_error(struct nfs_client *clp, int status)
+{
+       switch (status) {
+       case -NFS4ERR_SEQ_MISORDERED:
+               if (test_and_set_bit(NFS4CLNT_PURGE_STATE, &clp->cl_state))
+                       return -ESERVERFAULT;
+               /* Lease confirmation error: retry after purging the lease */
+               ssleep(1);
+       case -NFS4ERR_CLID_INUSE:
+       case -NFS4ERR_STALE_CLIENTID:
+               clear_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state);
+               break;
+       case -EACCES:
+               if (clp->cl_machine_cred == NULL)
+                       return -EACCES;
+               /* Handle case where the user hasn't set up machine creds */
+               nfs4_clear_machine_cred(clp);
+       case -NFS4ERR_DELAY:
+       case -ETIMEDOUT:
+       case -EAGAIN:
+               ssleep(1);
+               break;
+
+       case -NFS4ERR_MINOR_VERS_MISMATCH:
+               if (clp->cl_cons_state == NFS_CS_SESSION_INITING)
+                       nfs_mark_client_ready(clp, -EPROTONOSUPPORT);
+               dprintk("%s: exit with error %d for server %s\n",
+                               __func__, -EPROTONOSUPPORT, clp->cl_hostname);
+               return -EPROTONOSUPPORT;
+       case -EKEYEXPIRED:
+               nfs4_warn_keyexpired(clp->cl_hostname);
+       case -NFS4ERR_NOT_SAME: /* FixMe: implement recovery
+                                * in nfs4_exchange_id */
+       default:
+               dprintk("%s: exit with error %d for server %s\n", __func__,
+                               status, clp->cl_hostname);
+               return status;
+       }
+       set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
+       dprintk("%s: handled error %d for server %s\n", __func__, status,
+                       clp->cl_hostname);
+       return 0;
+}
+
 static int nfs4_reclaim_lease(struct nfs_client *clp)
 {
        struct rpc_cred *cred;
        const struct nfs4_state_recovery_ops *ops =
                clp->cl_mvops->reboot_recovery_ops;
-       int status = -ENOENT;
+       int status;
 
        cred = ops->get_clid_cred(clp);
-       if (cred != NULL) {
-               status = ops->establish_clid(clp, cred);
-               put_rpccred(cred);
-               /* Handle case where the user hasn't set up machine creds */
-               if (status == -EACCES && cred == clp->cl_machine_cred) {
-                       nfs4_clear_machine_cred(clp);
-                       status = -EAGAIN;
-               }
-               if (status == -NFS4ERR_MINOR_VERS_MISMATCH)
-                       status = -EPROTONOSUPPORT;
-       }
-       return status;
+       if (cred == NULL)
+               return -ENOENT;
+       status = ops->establish_clid(clp, cred);
+       put_rpccred(cred);
+       if (status != 0)
+               return nfs4_handle_reclaim_lease_error(clp, status);
+       return 0;
 }
 
 #ifdef CONFIG_NFS_V4_1
-void nfs4_schedule_session_recovery(struct nfs4_session *session)
+void nfs4_schedule_session_recovery(struct nfs4_session *session, int err)
 {
        struct nfs_client *clp = session->clp;
 
-       set_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state);
+       switch (err) {
+       default:
+               set_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state);
+               break;
+       case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION:
+               set_bit(NFS4CLNT_BIND_CONN_TO_SESSION, &clp->cl_state);
+       }
        nfs4_schedule_lease_recovery(clp);
 }
 EXPORT_SYMBOL_GPL(nfs4_schedule_session_recovery);
@@ -1607,14 +1669,19 @@ EXPORT_SYMBOL_GPL(nfs4_schedule_session_recovery);
 void nfs41_handle_recall_slot(struct nfs_client *clp)
 {
        set_bit(NFS4CLNT_RECALL_SLOT, &clp->cl_state);
+       dprintk("%s: scheduling slot recall for server %s\n", __func__,
+                       clp->cl_hostname);
        nfs4_schedule_state_manager(clp);
 }
 
 static void nfs4_reset_all_state(struct nfs_client *clp)
 {
        if (test_and_set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state) == 0) {
-               clp->cl_boot_time = CURRENT_TIME;
+               set_bit(NFS4CLNT_PURGE_STATE, &clp->cl_state);
+               clear_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state);
                nfs4_state_start_reclaim_nograce(clp);
+               dprintk("%s: scheduling reset of all state for server %s!\n",
+                               __func__, clp->cl_hostname);
                nfs4_schedule_state_manager(clp);
        }
 }
@@ -1623,33 +1690,50 @@ static void nfs41_handle_server_reboot(struct nfs_client *clp)
 {
        if (test_and_set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state) == 0) {
                nfs4_state_start_reclaim_reboot(clp);
+               dprintk("%s: server %s rebooted!\n", __func__,
+                               clp->cl_hostname);
                nfs4_schedule_state_manager(clp);
        }
 }
 
 static void nfs41_handle_state_revoked(struct nfs_client *clp)
 {
-       /* Temporary */
        nfs4_reset_all_state(clp);
+       dprintk("%s: state revoked on server %s\n", __func__, clp->cl_hostname);
 }
 
 static void nfs41_handle_recallable_state_revoked(struct nfs_client *clp)
 {
        /* This will need to handle layouts too */
        nfs_expire_all_delegations(clp);
+       dprintk("%s: Recallable state revoked on server %s!\n", __func__,
+                       clp->cl_hostname);
 }
 
-static void nfs41_handle_cb_path_down(struct nfs_client *clp)
+static void nfs41_handle_backchannel_fault(struct nfs_client *clp)
 {
        nfs_expire_all_delegations(clp);
        if (test_and_set_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state) == 0)
                nfs4_schedule_state_manager(clp);
+       dprintk("%s: server %s declared a backchannel fault\n", __func__,
+                       clp->cl_hostname);
+}
+
+static void nfs41_handle_cb_path_down(struct nfs_client *clp)
+{
+       if (test_and_set_bit(NFS4CLNT_BIND_CONN_TO_SESSION,
+               &clp->cl_state) == 0)
+               nfs4_schedule_state_manager(clp);
 }
 
 void nfs41_handle_sequence_flag_errors(struct nfs_client *clp, u32 flags)
 {
        if (!flags)
                return;
+
+       dprintk("%s: \"%s\" (client ID %llx) flags=0x%08x\n",
+               __func__, clp->cl_hostname, clp->cl_clientid, flags);
+
        if (flags & SEQ4_STATUS_RESTART_RECLAIM_NEEDED)
                nfs41_handle_server_reboot(clp);
        if (flags & (SEQ4_STATUS_EXPIRED_ALL_STATE_REVOKED |
@@ -1659,18 +1743,21 @@ void nfs41_handle_sequence_flag_errors(struct nfs_client *clp, u32 flags)
                nfs41_handle_state_revoked(clp);
        if (flags & SEQ4_STATUS_RECALLABLE_STATE_REVOKED)
                nfs41_handle_recallable_state_revoked(clp);
-       if (flags & (SEQ4_STATUS_CB_PATH_DOWN |
-                           SEQ4_STATUS_BACKCHANNEL_FAULT |
-                           SEQ4_STATUS_CB_PATH_DOWN_SESSION))
+       if (flags & SEQ4_STATUS_BACKCHANNEL_FAULT)
+               nfs41_handle_backchannel_fault(clp);
+       else if (flags & (SEQ4_STATUS_CB_PATH_DOWN |
+                               SEQ4_STATUS_CB_PATH_DOWN_SESSION))
                nfs41_handle_cb_path_down(clp);
 }
 
 static int nfs4_reset_session(struct nfs_client *clp)
 {
+       struct rpc_cred *cred;
        int status;
 
        nfs4_begin_drain_session(clp);
-       status = nfs4_proc_destroy_session(clp->cl_session);
+       cred = nfs4_get_exchange_id_cred(clp);
+       status = nfs4_proc_destroy_session(clp->cl_session, cred);
        if (status && status != -NFS4ERR_BADSESSION &&
            status != -NFS4ERR_DEADSESSION) {
                status = nfs4_recovery_handle_error(clp, status);
@@ -1678,19 +1765,26 @@ static int nfs4_reset_session(struct nfs_client *clp)
        }
 
        memset(clp->cl_session->sess_id.data, 0, NFS4_MAX_SESSIONID_LEN);
-       status = nfs4_proc_create_session(clp);
+       status = nfs4_proc_create_session(clp, cred);
        if (status) {
-               status = nfs4_recovery_handle_error(clp, status);
+               dprintk("%s: session reset failed with status %d for server %s!\n",
+                       __func__, status, clp->cl_hostname);
+               status = nfs4_handle_reclaim_lease_error(clp, status);
                goto out;
        }
        clear_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state);
        /* create_session negotiated new slot table */
        clear_bit(NFS4CLNT_RECALL_SLOT, &clp->cl_state);
+       clear_bit(NFS4CLNT_BIND_CONN_TO_SESSION, &clp->cl_state);
+       dprintk("%s: session reset was successful for server %s!\n",
+                       __func__, clp->cl_hostname);
 
         /* Let the state manager reestablish state */
        if (!test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state))
                nfs41_setup_state_renewal(clp);
 out:
+       if (cred)
+               put_rpccred(cred);
        return status;
 }
 
@@ -1722,37 +1816,41 @@ static int nfs4_recall_slot(struct nfs_client *clp)
        return 0;
 }
 
-#else /* CONFIG_NFS_V4_1 */
-static int nfs4_reset_session(struct nfs_client *clp) { return 0; }
-static int nfs4_end_drain_session(struct nfs_client *clp) { return 0; }
-static int nfs4_recall_slot(struct nfs_client *clp) { return 0; }
-#endif /* CONFIG_NFS_V4_1 */
-
-/* Set NFS4CLNT_LEASE_EXPIRED for all v4.0 errors and for recoverable errors
- * on EXCHANGE_ID for v4.1
- */
-static void nfs4_set_lease_expired(struct nfs_client *clp, int status)
+static int nfs4_bind_conn_to_session(struct nfs_client *clp)
 {
-       switch (status) {
-       case -NFS4ERR_CLID_INUSE:
-       case -NFS4ERR_STALE_CLIENTID:
-               clear_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state);
+       struct rpc_cred *cred;
+       int ret;
+
+       nfs4_begin_drain_session(clp);
+       cred = nfs4_get_exchange_id_cred(clp);
+       ret = nfs4_proc_bind_conn_to_session(clp, cred);
+       if (cred)
+               put_rpccred(cred);
+       clear_bit(NFS4CLNT_BIND_CONN_TO_SESSION, &clp->cl_state);
+       switch (ret) {
+       case 0:
+               dprintk("%s: bind_conn_to_session was successful for server %s!\n",
+                       __func__, clp->cl_hostname);
                break;
        case -NFS4ERR_DELAY:
-       case -ETIMEDOUT:
-       case -EAGAIN:
                ssleep(1);
+               set_bit(NFS4CLNT_BIND_CONN_TO_SESSION, &clp->cl_state);
                break;
-
-       case -EKEYEXPIRED:
-               nfs4_warn_keyexpired(clp->cl_hostname);
-       case -NFS4ERR_NOT_SAME: /* FixMe: implement recovery
-                                * in nfs4_exchange_id */
        default:
-               return;
+               return nfs4_recovery_handle_error(clp, ret);
        }
-       set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
+       return 0;
 }
+#else /* CONFIG_NFS_V4_1 */
+static int nfs4_reset_session(struct nfs_client *clp) { return 0; }
+static int nfs4_end_drain_session(struct nfs_client *clp) { return 0; }
+static int nfs4_recall_slot(struct nfs_client *clp) { return 0; }
+
+static int nfs4_bind_conn_to_session(struct nfs_client *clp)
+{
+       return 0;
+}
+#endif /* CONFIG_NFS_V4_1 */
 
 static void nfs4_state_manager(struct nfs_client *clp)
 {
@@ -1760,19 +1858,21 @@ static void nfs4_state_manager(struct nfs_client *clp)
 
        /* Ensure exclusive access to NFSv4 state */
        do {
+               if (test_bit(NFS4CLNT_PURGE_STATE, &clp->cl_state)) {
+                       status = nfs4_reclaim_lease(clp);
+                       if (status < 0)
+                               goto out_error;
+                       clear_bit(NFS4CLNT_PURGE_STATE, &clp->cl_state);
+                       set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
+               }
+
                if (test_and_clear_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state)) {
                        /* We're going to have to re-establish a clientid */
                        status = nfs4_reclaim_lease(clp);
-                       if (status) {
-                               nfs4_set_lease_expired(clp, status);
-                               if (test_bit(NFS4CLNT_LEASE_EXPIRED,
-                                                       &clp->cl_state))
-                                       continue;
-                               if (clp->cl_cons_state ==
-                                                       NFS_CS_SESSION_INITING)
-                                       nfs_mark_client_ready(clp, status);
+                       if (status < 0)
                                goto out_error;
-                       }
+                       if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state))
+                               continue;
                        clear_bit(NFS4CLNT_CHECK_LEASE, &clp->cl_state);
 
                        if (test_and_clear_bit(NFS4CLNT_SERVER_SCOPE_MISMATCH,
@@ -1803,6 +1903,15 @@ static void nfs4_state_manager(struct nfs_client *clp)
                                goto out_error;
                }
 
+               /* Send BIND_CONN_TO_SESSION */
+               if (test_and_clear_bit(NFS4CLNT_BIND_CONN_TO_SESSION,
+                               &clp->cl_state) && nfs4_has_session(clp)) {
+                       status = nfs4_bind_conn_to_session(clp);
+                       if (status < 0)
+                               goto out_error;
+                       continue;
+               }
+
                /* First recover reboot state... */
                if (test_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state)) {
                        status = nfs4_do_reclaim(clp,
index c54aae364beebd38833151f97328c3edfe7c2337..ee4a74db95d0b1b7ea49e8dd1263f0504f2fffa8 100644 (file)
 #include <linux/nfs4.h>
 #include <linux/nfs_fs.h>
 #include <linux/nfs_idmap.h>
+
 #include "nfs4_fs.h"
 #include "internal.h"
 #include "pnfs.h"
+#include "netns.h"
 
 #define NFSDBG_FACILITY                NFSDBG_XDR
 
@@ -99,9 +101,12 @@ static int nfs4_stat_to_errno(int);
 #define nfs4_path_maxsz                (1 + ((3 + NFS4_MAXPATHLEN) >> 2))
 #define nfs4_owner_maxsz       (1 + XDR_QUADLEN(IDMAP_NAMESZ))
 #define nfs4_group_maxsz       (1 + XDR_QUADLEN(IDMAP_NAMESZ))
+/* We support only one layout type per file system */
+#define decode_mdsthreshold_maxsz (1 + 1 + nfs4_fattr_bitmap_maxsz + 1 + 8)
 /* This is based on getfattr, which uses the most attributes: */
 #define nfs4_fattr_value_maxsz (1 + (1 + 2 + 2 + 4 + 2 + 1 + 1 + 2 + 2 + \
-                               3 + 3 + 3 + nfs4_owner_maxsz + nfs4_group_maxsz))
+                               3 + 3 + 3 + nfs4_owner_maxsz + \
+                               nfs4_group_maxsz + decode_mdsthreshold_maxsz))
 #define nfs4_fattr_maxsz       (nfs4_fattr_bitmap_maxsz + \
                                nfs4_fattr_value_maxsz)
 #define decode_getattr_maxsz    (op_decode_hdr_maxsz + nfs4_fattr_maxsz)
@@ -321,8 +326,20 @@ static int nfs4_stat_to_errno(int);
                                     1 /* csr_flags */ + \
                                     decode_channel_attrs_maxsz + \
                                     decode_channel_attrs_maxsz)
+#define encode_bind_conn_to_session_maxsz  (op_encode_hdr_maxsz + \
+                                    /* bctsa_sessid */ \
+                                    XDR_QUADLEN(NFS4_MAX_SESSIONID_LEN) + \
+                                    1 /* bctsa_dir */ + \
+                                    1 /* bctsa_use_conn_in_rdma_mode */)
+#define decode_bind_conn_to_session_maxsz  (op_decode_hdr_maxsz +      \
+                                    /* bctsr_sessid */ \
+                                    XDR_QUADLEN(NFS4_MAX_SESSIONID_LEN) + \
+                                    1 /* bctsr_dir */ + \
+                                    1 /* bctsr_use_conn_in_rdma_mode */)
 #define encode_destroy_session_maxsz    (op_encode_hdr_maxsz + 4)
 #define decode_destroy_session_maxsz    (op_decode_hdr_maxsz)
+#define encode_destroy_clientid_maxsz   (op_encode_hdr_maxsz + 2)
+#define decode_destroy_clientid_maxsz   (op_decode_hdr_maxsz)
 #define encode_sequence_maxsz  (op_encode_hdr_maxsz + \
                                XDR_QUADLEN(NFS4_MAX_SESSIONID_LEN) + 4)
 #define decode_sequence_maxsz  (op_decode_hdr_maxsz + \
@@ -421,30 +438,22 @@ static int nfs4_stat_to_errno(int);
 #define NFS4_enc_commit_sz     (compound_encode_hdr_maxsz + \
                                encode_sequence_maxsz + \
                                encode_putfh_maxsz + \
-                               encode_commit_maxsz + \
-                               encode_getattr_maxsz)
+                               encode_commit_maxsz)
 #define NFS4_dec_commit_sz     (compound_decode_hdr_maxsz + \
                                decode_sequence_maxsz + \
                                decode_putfh_maxsz + \
-                               decode_commit_maxsz + \
-                               decode_getattr_maxsz)
+                               decode_commit_maxsz)
 #define NFS4_enc_open_sz        (compound_encode_hdr_maxsz + \
                                encode_sequence_maxsz + \
                                encode_putfh_maxsz + \
-                               encode_savefh_maxsz + \
                                encode_open_maxsz + \
                                encode_getfh_maxsz + \
-                               encode_getattr_maxsz + \
-                               encode_restorefh_maxsz + \
                                encode_getattr_maxsz)
 #define NFS4_dec_open_sz        (compound_decode_hdr_maxsz + \
                                decode_sequence_maxsz + \
                                decode_putfh_maxsz + \
-                               decode_savefh_maxsz + \
                                decode_open_maxsz + \
                                decode_getfh_maxsz + \
-                               decode_getattr_maxsz + \
-                               decode_restorefh_maxsz + \
                                decode_getattr_maxsz)
 #define NFS4_enc_open_confirm_sz \
                                (compound_encode_hdr_maxsz + \
@@ -595,47 +604,37 @@ static int nfs4_stat_to_errno(int);
 #define NFS4_enc_remove_sz     (compound_encode_hdr_maxsz + \
                                encode_sequence_maxsz + \
                                encode_putfh_maxsz + \
-                               encode_remove_maxsz + \
-                               encode_getattr_maxsz)
+                               encode_remove_maxsz)
 #define NFS4_dec_remove_sz     (compound_decode_hdr_maxsz + \
                                decode_sequence_maxsz + \
                                decode_putfh_maxsz + \
-                               decode_remove_maxsz + \
-                               decode_getattr_maxsz)
+                               decode_remove_maxsz)
 #define NFS4_enc_rename_sz     (compound_encode_hdr_maxsz + \
                                encode_sequence_maxsz + \
                                encode_putfh_maxsz + \
                                encode_savefh_maxsz + \
                                encode_putfh_maxsz + \
-                               encode_rename_maxsz + \
-                               encode_getattr_maxsz + \
-                               encode_restorefh_maxsz + \
-                               encode_getattr_maxsz)
+                               encode_rename_maxsz)
 #define NFS4_dec_rename_sz     (compound_decode_hdr_maxsz + \
                                decode_sequence_maxsz + \
                                decode_putfh_maxsz + \
                                decode_savefh_maxsz + \
                                decode_putfh_maxsz + \
-                               decode_rename_maxsz + \
-                               decode_getattr_maxsz + \
-                               decode_restorefh_maxsz + \
-                               decode_getattr_maxsz)
+                               decode_rename_maxsz)
 #define NFS4_enc_link_sz       (compound_encode_hdr_maxsz + \
                                encode_sequence_maxsz + \
                                encode_putfh_maxsz + \
                                encode_savefh_maxsz + \
                                encode_putfh_maxsz + \
                                encode_link_maxsz + \
-                               decode_getattr_maxsz + \
                                encode_restorefh_maxsz + \
-                               decode_getattr_maxsz)
+                               encode_getattr_maxsz)
 #define NFS4_dec_link_sz       (compound_decode_hdr_maxsz + \
                                decode_sequence_maxsz + \
                                decode_putfh_maxsz + \
                                decode_savefh_maxsz + \
                                decode_putfh_maxsz + \
                                decode_link_maxsz + \
-                               decode_getattr_maxsz + \
                                decode_restorefh_maxsz + \
                                decode_getattr_maxsz)
 #define NFS4_enc_symlink_sz    (compound_encode_hdr_maxsz + \
@@ -653,20 +652,14 @@ static int nfs4_stat_to_errno(int);
 #define NFS4_enc_create_sz     (compound_encode_hdr_maxsz + \
                                encode_sequence_maxsz + \
                                encode_putfh_maxsz + \
-                               encode_savefh_maxsz + \
                                encode_create_maxsz + \
                                encode_getfh_maxsz + \
-                               encode_getattr_maxsz + \
-                               encode_restorefh_maxsz + \
                                encode_getattr_maxsz)
 #define NFS4_dec_create_sz     (compound_decode_hdr_maxsz + \
                                decode_sequence_maxsz + \
                                decode_putfh_maxsz + \
-                               decode_savefh_maxsz + \
                                decode_create_maxsz + \
                                decode_getfh_maxsz + \
-                               decode_getattr_maxsz + \
-                               decode_restorefh_maxsz + \
                                decode_getattr_maxsz)
 #define NFS4_enc_pathconf_sz   (compound_encode_hdr_maxsz + \
                                encode_sequence_maxsz + \
@@ -738,6 +731,12 @@ static int nfs4_stat_to_errno(int);
                                decode_putfh_maxsz + \
                                decode_secinfo_maxsz)
 #if defined(CONFIG_NFS_V4_1)
+#define NFS4_enc_bind_conn_to_session_sz \
+                               (compound_encode_hdr_maxsz + \
+                                encode_bind_conn_to_session_maxsz)
+#define NFS4_dec_bind_conn_to_session_sz \
+                               (compound_decode_hdr_maxsz + \
+                                decode_bind_conn_to_session_maxsz)
 #define NFS4_enc_exchange_id_sz \
                                (compound_encode_hdr_maxsz + \
                                 encode_exchange_id_maxsz)
@@ -754,6 +753,10 @@ static int nfs4_stat_to_errno(int);
                                         encode_destroy_session_maxsz)
 #define NFS4_dec_destroy_session_sz    (compound_decode_hdr_maxsz + \
                                         decode_destroy_session_maxsz)
+#define NFS4_enc_destroy_clientid_sz   (compound_encode_hdr_maxsz + \
+                                        encode_destroy_clientid_maxsz)
+#define NFS4_dec_destroy_clientid_sz   (compound_decode_hdr_maxsz + \
+                                        decode_destroy_clientid_maxsz)
 #define NFS4_enc_sequence_sz \
                                (compound_decode_hdr_maxsz + \
                                 encode_sequence_maxsz)
@@ -1103,7 +1106,7 @@ static void encode_close(struct xdr_stream *xdr, const struct nfs_closeargs *arg
        encode_nfs4_stateid(xdr, arg->stateid);
 }
 
-static void encode_commit(struct xdr_stream *xdr, const struct nfs_writeargs *args, struct compound_hdr *hdr)
+static void encode_commit(struct xdr_stream *xdr, const struct nfs_commitargs *args, struct compound_hdr *hdr)
 {
        __be32 *p;
 
@@ -1194,6 +1197,16 @@ static void encode_getfattr(struct xdr_stream *xdr, const u32* bitmask, struct c
                           bitmask[1] & nfs4_fattr_bitmap[1], hdr);
 }
 
+static void encode_getfattr_open(struct xdr_stream *xdr, const u32 *bitmask,
+                                struct compound_hdr *hdr)
+{
+       encode_getattr_three(xdr,
+                            bitmask[0] & nfs4_fattr_bitmap[0],
+                            bitmask[1] & nfs4_fattr_bitmap[1],
+                            bitmask[2] & FATTR4_WORD2_MDSTHRESHOLD,
+                            hdr);
+}
+
 static void encode_fsinfo(struct xdr_stream *xdr, const u32* bitmask, struct compound_hdr *hdr)
 {
        encode_getattr_three(xdr,
@@ -1678,6 +1691,20 @@ static void encode_secinfo(struct xdr_stream *xdr, const struct qstr *name, stru
 
 #if defined(CONFIG_NFS_V4_1)
 /* NFSv4.1 operations */
+static void encode_bind_conn_to_session(struct xdr_stream *xdr,
+                                  struct nfs4_session *session,
+                                  struct compound_hdr *hdr)
+{
+       __be32 *p;
+
+       encode_op_hdr(xdr, OP_BIND_CONN_TO_SESSION,
+               decode_bind_conn_to_session_maxsz, hdr);
+       encode_opaque_fixed(xdr, session->sess_id.data, NFS4_MAX_SESSIONID_LEN);
+       p = xdr_reserve_space(xdr, 8);
+       *p++ = cpu_to_be32(NFS4_CDFC4_BACK_OR_BOTH);
+       *p = 0; /* use_conn_in_rdma_mode = False */
+}
+
 static void encode_exchange_id(struct xdr_stream *xdr,
                               struct nfs41_exchange_id_args *args,
                               struct compound_hdr *hdr)
@@ -1726,6 +1753,7 @@ static void encode_create_session(struct xdr_stream *xdr,
        char machine_name[NFS4_MAX_MACHINE_NAME_LEN];
        uint32_t len;
        struct nfs_client *clp = args->client;
+       struct nfs_net *nn = net_generic(clp->cl_net, nfs_net_id);
        u32 max_resp_sz_cached;
 
        /*
@@ -1767,7 +1795,7 @@ static void encode_create_session(struct xdr_stream *xdr,
        *p++ = cpu_to_be32(RPC_AUTH_UNIX);                      /* auth_sys */
 
        /* authsys_parms rfc1831 */
-       *p++ = cpu_to_be32((u32)clp->cl_boot_time.tv_nsec);     /* stamp */
+       *p++ = (__be32)nn->boot_time.tv_nsec;           /* stamp */
        p = xdr_encode_opaque(p, machine_name, len);
        *p++ = cpu_to_be32(0);                          /* UID */
        *p++ = cpu_to_be32(0);                          /* GID */
@@ -1782,6 +1810,14 @@ static void encode_destroy_session(struct xdr_stream *xdr,
        encode_opaque_fixed(xdr, session->sess_id.data, NFS4_MAX_SESSIONID_LEN);
 }
 
+static void encode_destroy_clientid(struct xdr_stream *xdr,
+                                  uint64_t clientid,
+                                  struct compound_hdr *hdr)
+{
+       encode_op_hdr(xdr, OP_DESTROY_CLIENTID, decode_destroy_clientid_maxsz, hdr);
+       encode_uint64(xdr, clientid);
+}
+
 static void encode_reclaim_complete(struct xdr_stream *xdr,
                                    struct nfs41_reclaim_complete_args *args,
                                    struct compound_hdr *hdr)
@@ -2064,7 +2100,6 @@ static void nfs4_xdr_enc_remove(struct rpc_rqst *req, struct xdr_stream *xdr,
        encode_sequence(xdr, &args->seq_args, &hdr);
        encode_putfh(xdr, args->fh, &hdr);
        encode_remove(xdr, &args->name, &hdr);
-       encode_getfattr(xdr, args->bitmask, &hdr);
        encode_nops(&hdr);
 }
 
@@ -2084,9 +2119,6 @@ static void nfs4_xdr_enc_rename(struct rpc_rqst *req, struct xdr_stream *xdr,
        encode_savefh(xdr, &hdr);
        encode_putfh(xdr, args->new_dir, &hdr);
        encode_rename(xdr, args->old_name, args->new_name, &hdr);
-       encode_getfattr(xdr, args->bitmask, &hdr);
-       encode_restorefh(xdr, &hdr);
-       encode_getfattr(xdr, args->bitmask, &hdr);
        encode_nops(&hdr);
 }
 
@@ -2106,7 +2138,6 @@ static void nfs4_xdr_enc_link(struct rpc_rqst *req, struct xdr_stream *xdr,
        encode_savefh(xdr, &hdr);
        encode_putfh(xdr, args->dir_fh, &hdr);
        encode_link(xdr, args->name, &hdr);
-       encode_getfattr(xdr, args->bitmask, &hdr);
        encode_restorefh(xdr, &hdr);
        encode_getfattr(xdr, args->bitmask, &hdr);
        encode_nops(&hdr);
@@ -2125,12 +2156,9 @@ static void nfs4_xdr_enc_create(struct rpc_rqst *req, struct xdr_stream *xdr,
        encode_compound_hdr(xdr, req, &hdr);
        encode_sequence(xdr, &args->seq_args, &hdr);
        encode_putfh(xdr, args->dir_fh, &hdr);
-       encode_savefh(xdr, &hdr);
        encode_create(xdr, args, &hdr);
        encode_getfh(xdr, &hdr);
        encode_getfattr(xdr, args->bitmask, &hdr);
-       encode_restorefh(xdr, &hdr);
-       encode_getfattr(xdr, args->bitmask, &hdr);
        encode_nops(&hdr);
 }
 
@@ -2191,12 +2219,9 @@ static void nfs4_xdr_enc_open(struct rpc_rqst *req, struct xdr_stream *xdr,
        encode_compound_hdr(xdr, req, &hdr);
        encode_sequence(xdr, &args->seq_args, &hdr);
        encode_putfh(xdr, args->fh, &hdr);
-       encode_savefh(xdr, &hdr);
        encode_open(xdr, args, &hdr);
        encode_getfh(xdr, &hdr);
-       encode_getfattr(xdr, args->bitmask, &hdr);
-       encode_restorefh(xdr, &hdr);
-       encode_getfattr(xdr, args->dir_bitmask, &hdr);
+       encode_getfattr_open(xdr, args->bitmask, &hdr);
        encode_nops(&hdr);
 }
 
@@ -2448,7 +2473,7 @@ static void nfs4_xdr_enc_write(struct rpc_rqst *req, struct xdr_stream *xdr,
  *  a COMMIT request
  */
 static void nfs4_xdr_enc_commit(struct rpc_rqst *req, struct xdr_stream *xdr,
-                               struct nfs_writeargs *args)
+                               struct nfs_commitargs *args)
 {
        struct compound_hdr hdr = {
                .minorversion = nfs4_xdr_minorversion(&args->seq_args),
@@ -2458,8 +2483,6 @@ static void nfs4_xdr_enc_commit(struct rpc_rqst *req, struct xdr_stream *xdr,
        encode_sequence(xdr, &args->seq_args, &hdr);
        encode_putfh(xdr, args->fh, &hdr);
        encode_commit(xdr, args, &hdr);
-       if (args->bitmask)
-               encode_getfattr(xdr, args->bitmask, &hdr);
        encode_nops(&hdr);
 }
 
@@ -2602,8 +2625,8 @@ static void nfs4_xdr_enc_delegreturn(struct rpc_rqst *req,
        encode_compound_hdr(xdr, req, &hdr);
        encode_sequence(xdr, &args->seq_args, &hdr);
        encode_putfh(xdr, args->fhandle, &hdr);
-       encode_delegreturn(xdr, args->stateid, &hdr);
        encode_getfattr(xdr, args->bitmask, &hdr);
+       encode_delegreturn(xdr, args->stateid, &hdr);
        encode_nops(&hdr);
 }
 
@@ -2650,6 +2673,22 @@ static void nfs4_xdr_enc_secinfo(struct rpc_rqst *req,
 }
 
 #if defined(CONFIG_NFS_V4_1)
+/*
+ * BIND_CONN_TO_SESSION request
+ */
+static void nfs4_xdr_enc_bind_conn_to_session(struct rpc_rqst *req,
+                               struct xdr_stream *xdr,
+                               struct nfs_client *clp)
+{
+       struct compound_hdr hdr = {
+               .minorversion = clp->cl_mvops->minor_version,
+       };
+
+       encode_compound_hdr(xdr, req, &hdr);
+       encode_bind_conn_to_session(xdr, clp->cl_session, &hdr);
+       encode_nops(&hdr);
+}
+
 /*
  * EXCHANGE_ID request
  */
@@ -2698,6 +2737,22 @@ static void nfs4_xdr_enc_destroy_session(struct rpc_rqst *req,
        encode_nops(&hdr);
 }
 
+/*
+ * a DESTROY_CLIENTID request
+ */
+static void nfs4_xdr_enc_destroy_clientid(struct rpc_rqst *req,
+                                        struct xdr_stream *xdr,
+                                        struct nfs_client *clp)
+{
+       struct compound_hdr hdr = {
+               .minorversion = clp->cl_mvops->minor_version,
+       };
+
+       encode_compound_hdr(xdr, req, &hdr);
+       encode_destroy_clientid(xdr, clp->cl_clientid, &hdr);
+       encode_nops(&hdr);
+}
+
 /*
  * a SEQUENCE request
  */
@@ -4102,7 +4157,7 @@ static int decode_verifier(struct xdr_stream *xdr, void *verifier)
        return decode_opaque_fixed(xdr, verifier, NFS4_VERIFIER_SIZE);
 }
 
-static int decode_commit(struct xdr_stream *xdr, struct nfs_writeres *res)
+static int decode_commit(struct xdr_stream *xdr, struct nfs_commitres *res)
 {
        int status;
 
@@ -4220,6 +4275,110 @@ xdr_error:
        return status;
 }
 
+static int decode_threshold_hint(struct xdr_stream *xdr,
+                                 uint32_t *bitmap,
+                                 uint64_t *res,
+                                 uint32_t hint_bit)
+{
+       __be32 *p;
+
+       *res = 0;
+       if (likely(bitmap[0] & hint_bit)) {
+               p = xdr_inline_decode(xdr, 8);
+               if (unlikely(!p))
+                       goto out_overflow;
+               xdr_decode_hyper(p, res);
+       }
+       return 0;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
+}
+
+static int decode_first_threshold_item4(struct xdr_stream *xdr,
+                                       struct nfs4_threshold *res)
+{
+       __be32 *p, *savep;
+       uint32_t bitmap[3] = {0,}, attrlen;
+       int status;
+
+       /* layout type */
+       p = xdr_inline_decode(xdr, 4);
+       if (unlikely(!p)) {
+               print_overflow_msg(__func__, xdr);
+               return -EIO;
+       }
+       res->l_type = be32_to_cpup(p);
+
+       /* thi_hintset bitmap */
+       status = decode_attr_bitmap(xdr, bitmap);
+       if (status < 0)
+               goto xdr_error;
+
+       /* thi_hintlist length */
+       status = decode_attr_length(xdr, &attrlen, &savep);
+       if (status < 0)
+               goto xdr_error;
+       /* thi_hintlist */
+       status = decode_threshold_hint(xdr, bitmap, &res->rd_sz, THRESHOLD_RD);
+       if (status < 0)
+               goto xdr_error;
+       status = decode_threshold_hint(xdr, bitmap, &res->wr_sz, THRESHOLD_WR);
+       if (status < 0)
+               goto xdr_error;
+       status = decode_threshold_hint(xdr, bitmap, &res->rd_io_sz,
+                                      THRESHOLD_RD_IO);
+       if (status < 0)
+               goto xdr_error;
+       status = decode_threshold_hint(xdr, bitmap, &res->wr_io_sz,
+                                      THRESHOLD_WR_IO);
+       if (status < 0)
+               goto xdr_error;
+
+       status = verify_attr_len(xdr, savep, attrlen);
+       res->bm = bitmap[0];
+
+       dprintk("%s bm=0x%x rd_sz=%llu wr_sz=%llu rd_io=%llu wr_io=%llu\n",
+                __func__, res->bm, res->rd_sz, res->wr_sz, res->rd_io_sz,
+               res->wr_io_sz);
+xdr_error:
+       dprintk("%s ret=%d!\n", __func__, status);
+       return status;
+}
+
+/*
+ * Thresholds on pNFS direct I/O vrs MDS I/O
+ */
+static int decode_attr_mdsthreshold(struct xdr_stream *xdr,
+                                   uint32_t *bitmap,
+                                   struct nfs4_threshold *res)
+{
+       __be32 *p;
+       int status = 0;
+       uint32_t num;
+
+       if (unlikely(bitmap[2] & (FATTR4_WORD2_MDSTHRESHOLD - 1U)))
+               return -EIO;
+       if (likely(bitmap[2] & FATTR4_WORD2_MDSTHRESHOLD)) {
+               p = xdr_inline_decode(xdr, 4);
+               if (unlikely(!p))
+                       goto out_overflow;
+               num = be32_to_cpup(p);
+               if (num == 0)
+                       return 0;
+               if (num > 1)
+                       printk(KERN_INFO "%s: Warning: Multiple pNFS layout "
+                               "drivers per filesystem not supported\n",
+                               __func__);
+
+               status = decode_first_threshold_item4(xdr, res);
+       }
+       return status;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
+}
+
 static int decode_getfattr_attrs(struct xdr_stream *xdr, uint32_t *bitmap,
                struct nfs_fattr *fattr, struct nfs_fh *fh,
                struct nfs4_fs_locations *fs_loc,
@@ -4326,6 +4485,10 @@ static int decode_getfattr_attrs(struct xdr_stream *xdr, uint32_t *bitmap,
                goto xdr_error;
        fattr->valid |= status;
 
+       status = decode_attr_mdsthreshold(xdr, bitmap, fattr->mdsthreshold);
+       if (status < 0)
+               goto xdr_error;
+
 xdr_error:
        dprintk("%s: xdr returned %d\n", __func__, -status);
        return status;
@@ -5156,7 +5319,6 @@ static int decode_exchange_id(struct xdr_stream *xdr,
        uint32_t dummy;
        char *dummy_str;
        int status;
-       struct nfs_client *clp = res->client;
        uint32_t impl_id_count;
 
        status = decode_op_hdr(xdr, OP_EXCHANGE_ID);
@@ -5166,36 +5328,39 @@ static int decode_exchange_id(struct xdr_stream *xdr,
        p = xdr_inline_decode(xdr, 8);
        if (unlikely(!p))
                goto out_overflow;
-       xdr_decode_hyper(p, &clp->cl_clientid);
+       xdr_decode_hyper(p, &res->clientid);
        p = xdr_inline_decode(xdr, 12);
        if (unlikely(!p))
                goto out_overflow;
-       clp->cl_seqid = be32_to_cpup(p++);
-       clp->cl_exchange_flags = be32_to_cpup(p++);
+       res->seqid = be32_to_cpup(p++);
+       res->flags = be32_to_cpup(p++);
 
        /* We ask for SP4_NONE */
        dummy = be32_to_cpup(p);
        if (dummy != SP4_NONE)
                return -EIO;
 
-       /* Throw away minor_id */
+       /* server_owner4.so_minor_id */
        p = xdr_inline_decode(xdr, 8);
        if (unlikely(!p))
                goto out_overflow;
+       p = xdr_decode_hyper(p, &res->server_owner->minor_id);
 
-       /* Throw away Major id */
+       /* server_owner4.so_major_id */
        status = decode_opaque_inline(xdr, &dummy, &dummy_str);
        if (unlikely(status))
                return status;
+       if (unlikely(dummy > NFS4_OPAQUE_LIMIT))
+               return -EIO;
+       memcpy(res->server_owner->major_id, dummy_str, dummy);
+       res->server_owner->major_id_sz = dummy;
 
-       /* Save server_scope */
+       /* server_scope4 */
        status = decode_opaque_inline(xdr, &dummy, &dummy_str);
        if (unlikely(status))
                return status;
-
        if (unlikely(dummy > NFS4_OPAQUE_LIMIT))
                return -EIO;
-
        memcpy(res->server_scope->server_scope, dummy_str, dummy);
        res->server_scope->server_scope_sz = dummy;
 
@@ -5276,6 +5441,37 @@ static int decode_sessionid(struct xdr_stream *xdr, struct nfs4_sessionid *sid)
        return decode_opaque_fixed(xdr, sid->data, NFS4_MAX_SESSIONID_LEN);
 }
 
+static int decode_bind_conn_to_session(struct xdr_stream *xdr,
+                               struct nfs41_bind_conn_to_session_res *res)
+{
+       __be32 *p;
+       int status;
+
+       status = decode_op_hdr(xdr, OP_BIND_CONN_TO_SESSION);
+       if (!status)
+               status = decode_sessionid(xdr, &res->session->sess_id);
+       if (unlikely(status))
+               return status;
+
+       /* dir flags, rdma mode bool */
+       p = xdr_inline_decode(xdr, 8);
+       if (unlikely(!p))
+               goto out_overflow;
+
+       res->dir = be32_to_cpup(p++);
+       if (res->dir == 0 || res->dir > NFS4_CDFS4_BOTH)
+               return -EIO;
+       if (be32_to_cpup(p) == 0)
+               res->use_conn_in_rdma_mode = false;
+       else
+               res->use_conn_in_rdma_mode = true;
+
+       return 0;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
+}
+
 static int decode_create_session(struct xdr_stream *xdr,
                                 struct nfs41_create_session_res *res)
 {
@@ -5312,6 +5508,11 @@ static int decode_destroy_session(struct xdr_stream *xdr, void *dummy)
        return decode_op_hdr(xdr, OP_DESTROY_SESSION);
 }
 
+static int decode_destroy_clientid(struct xdr_stream *xdr, void *dummy)
+{
+       return decode_op_hdr(xdr, OP_DESTROY_CLIENTID);
+}
+
 static int decode_reclaim_complete(struct xdr_stream *xdr, void *dummy)
 {
        return decode_op_hdr(xdr, OP_RECLAIM_COMPLETE);
@@ -5800,9 +6001,6 @@ static int nfs4_xdr_dec_remove(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
        if (status)
                goto out;
        status = decode_remove(xdr, &res->cinfo);
-       if (status)
-               goto out;
-       decode_getfattr(xdr, res->dir_attr, res->server);
 out:
        return status;
 }
@@ -5832,15 +6030,6 @@ static int nfs4_xdr_dec_rename(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
        if (status)
                goto out;
        status = decode_rename(xdr, &res->old_cinfo, &res->new_cinfo);
-       if (status)
-               goto out;
-       /* Current FH is target directory */
-       if (decode_getfattr(xdr, res->new_fattr, res->server))
-               goto out;
-       status = decode_restorefh(xdr);
-       if (status)
-               goto out;
-       decode_getfattr(xdr, res->old_fattr, res->server);
 out:
        return status;
 }
@@ -5876,8 +6065,6 @@ static int nfs4_xdr_dec_link(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
         * Note order: OP_LINK leaves the directory as the current
         *             filehandle.
         */
-       if (decode_getfattr(xdr, res->dir_attr, res->server))
-               goto out;
        status = decode_restorefh(xdr);
        if (status)
                goto out;
@@ -5902,9 +6089,6 @@ static int nfs4_xdr_dec_create(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
        if (status)
                goto out;
        status = decode_putfh(xdr);
-       if (status)
-               goto out;
-       status = decode_savefh(xdr);
        if (status)
                goto out;
        status = decode_create(xdr, &res->dir_cinfo);
@@ -5913,12 +6097,7 @@ static int nfs4_xdr_dec_create(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
        status = decode_getfh(xdr, res->fh);
        if (status)
                goto out;
-       if (decode_getfattr(xdr, res->fattr, res->server))
-               goto out;
-       status = decode_restorefh(xdr);
-       if (status)
-               goto out;
-       decode_getfattr(xdr, res->dir_fattr, res->server);
+       decode_getfattr(xdr, res->fattr, res->server);
 out:
        return status;
 }
@@ -6073,9 +6252,6 @@ static int nfs4_xdr_dec_open(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
        if (status)
                goto out;
        status = decode_putfh(xdr);
-       if (status)
-               goto out;
-       status = decode_savefh(xdr);
        if (status)
                goto out;
        status = decode_open(xdr, res);
@@ -6083,11 +6259,7 @@ static int nfs4_xdr_dec_open(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
                goto out;
        if (decode_getfh(xdr, &res->fh) != 0)
                goto out;
-       if (decode_getfattr(xdr, res->f_attr, res->server) != 0)
-               goto out;
-       if (decode_restorefh(xdr) != 0)
-               goto out;
-       decode_getfattr(xdr, res->dir_attr, res->server);
+       decode_getfattr(xdr, res->f_attr, res->server);
 out:
        return status;
 }
@@ -6353,7 +6525,7 @@ out:
  * Decode COMMIT response
  */
 static int nfs4_xdr_dec_commit(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
-                              struct nfs_writeres *res)
+                              struct nfs_commitres *res)
 {
        struct compound_hdr hdr;
        int status;
@@ -6368,10 +6540,6 @@ static int nfs4_xdr_dec_commit(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
        if (status)
                goto out;
        status = decode_commit(xdr, res);
-       if (status)
-               goto out;
-       if (res->fattr)
-               decode_getfattr(xdr, res->fattr, res->server);
 out:
        return status;
 }
@@ -6527,10 +6695,10 @@ static int nfs4_xdr_dec_delegreturn(struct rpc_rqst *rqstp,
        status = decode_putfh(xdr);
        if (status != 0)
                goto out;
-       status = decode_delegreturn(xdr);
+       status = decode_getfattr(xdr, res->fattr, res->server);
        if (status != 0)
                goto out;
-       decode_getfattr(xdr, res->fattr, res->server);
+       status = decode_delegreturn(xdr);
 out:
        return status;
 }
@@ -6590,6 +6758,22 @@ out:
 }
 
 #if defined(CONFIG_NFS_V4_1)
+/*
+ * Decode BIND_CONN_TO_SESSION response
+ */
+static int nfs4_xdr_dec_bind_conn_to_session(struct rpc_rqst *rqstp,
+                                       struct xdr_stream *xdr,
+                                       void *res)
+{
+       struct compound_hdr hdr;
+       int status;
+
+       status = decode_compound_hdr(xdr, &hdr);
+       if (!status)
+               status = decode_bind_conn_to_session(xdr, res);
+       return status;
+}
+
 /*
  * Decode EXCHANGE_ID response
  */
@@ -6638,6 +6822,22 @@ static int nfs4_xdr_dec_destroy_session(struct rpc_rqst *rqstp,
        return status;
 }
 
+/*
+ * Decode DESTROY_CLIENTID response
+ */
+static int nfs4_xdr_dec_destroy_clientid(struct rpc_rqst *rqstp,
+                                       struct xdr_stream *xdr,
+                                       void *res)
+{
+       struct compound_hdr hdr;
+       int status;
+
+       status = decode_compound_hdr(xdr, &hdr);
+       if (!status)
+               status = decode_destroy_clientid(xdr, res);
+       return status;
+}
+
 /*
  * Decode SEQUENCE response
  */
@@ -7085,6 +7285,9 @@ struct rpc_procinfo       nfs4_procedures[] = {
        PROC(TEST_STATEID,      enc_test_stateid,       dec_test_stateid),
        PROC(FREE_STATEID,      enc_free_stateid,       dec_free_stateid),
        PROC(GETDEVICELIST,     enc_getdevicelist,      dec_getdevicelist),
+       PROC(BIND_CONN_TO_SESSION,
+                       enc_bind_conn_to_session, dec_bind_conn_to_session),
+       PROC(DESTROY_CLIENTID,  enc_destroy_clientid,   dec_destroy_clientid),
 #endif /* CONFIG_NFS_V4_1 */
 };
 
index 4bff4a3dab4602ffa8fe1f48df5d3adc3e8709c3..b47277baebab92930bee6c1fbac445fd8978a6b9 100644 (file)
@@ -211,7 +211,7 @@ static void copy_single_comp(struct ore_components *oc, unsigned c,
        memcpy(ocomp->cred, src_comp->oc_cap.cred, sizeof(ocomp->cred));
 }
 
-int __alloc_objio_seg(unsigned numdevs, gfp_t gfp_flags,
+static int __alloc_objio_seg(unsigned numdevs, gfp_t gfp_flags,
                       struct objio_segment **pseg)
 {
 /*     This is the in memory structure of the objio_segment
@@ -440,11 +440,12 @@ static void _read_done(struct ore_io_state *ios, void *private)
 
 int objio_read_pagelist(struct nfs_read_data *rdata)
 {
+       struct nfs_pgio_header *hdr = rdata->header;
        struct objio_state *objios;
        int ret;
 
-       ret = objio_alloc_io_state(NFS_I(rdata->inode)->layout, true,
-                       rdata->lseg, rdata->args.pages, rdata->args.pgbase,
+       ret = objio_alloc_io_state(NFS_I(hdr->inode)->layout, true,
+                       hdr->lseg, rdata->args.pages, rdata->args.pgbase,
                        rdata->args.offset, rdata->args.count, rdata,
                        GFP_KERNEL, &objios);
        if (unlikely(ret))
@@ -483,12 +484,12 @@ static struct page *__r4w_get_page(void *priv, u64 offset, bool *uptodate)
 {
        struct objio_state *objios = priv;
        struct nfs_write_data *wdata = objios->oir.rpcdata;
+       struct address_space *mapping = wdata->header->inode->i_mapping;
        pgoff_t index = offset / PAGE_SIZE;
-       struct page *page = find_get_page(wdata->inode->i_mapping, index);
+       struct page *page = find_get_page(mapping, index);
 
        if (!page) {
-               page = find_or_create_page(wdata->inode->i_mapping,
-                                               index, GFP_NOFS);
+               page = find_or_create_page(mapping, index, GFP_NOFS);
                if (unlikely(!page)) {
                        dprintk("%s: grab_cache_page Failed index=0x%lx\n",
                                __func__, index);
@@ -518,11 +519,12 @@ static const struct _ore_r4w_op _r4w_op = {
 
 int objio_write_pagelist(struct nfs_write_data *wdata, int how)
 {
+       struct nfs_pgio_header *hdr = wdata->header;
        struct objio_state *objios;
        int ret;
 
-       ret = objio_alloc_io_state(NFS_I(wdata->inode)->layout, false,
-                       wdata->lseg, wdata->args.pages, wdata->args.pgbase,
+       ret = objio_alloc_io_state(NFS_I(hdr->inode)->layout, false,
+                       hdr->lseg, wdata->args.pages, wdata->args.pgbase,
                        wdata->args.offset, wdata->args.count, wdata, GFP_NOFS,
                        &objios);
        if (unlikely(ret))
index 595c5fc21a19d15efaab48bff059336d7762c1b3..8746135453011dc70d30ebbd5b70e2b813f7b15d 100644 (file)
@@ -258,7 +258,7 @@ objlayout_read_done(struct objlayout_io_res *oir, ssize_t status, bool sync)
        if (status >= 0)
                rdata->res.count = status;
        else
-               rdata->pnfs_error = status;
+               rdata->header->pnfs_error = status;
        objlayout_iodone(oir);
        /* must not use oir after this point */
 
@@ -279,12 +279,14 @@ objlayout_read_done(struct objlayout_io_res *oir, ssize_t status, bool sync)
 enum pnfs_try_status
 objlayout_read_pagelist(struct nfs_read_data *rdata)
 {
+       struct nfs_pgio_header *hdr = rdata->header;
+       struct inode *inode = hdr->inode;
        loff_t offset = rdata->args.offset;
        size_t count = rdata->args.count;
        int err;
        loff_t eof;
 
-       eof = i_size_read(rdata->inode);
+       eof = i_size_read(inode);
        if (unlikely(offset + count > eof)) {
                if (offset >= eof) {
                        err = 0;
@@ -297,17 +299,17 @@ objlayout_read_pagelist(struct nfs_read_data *rdata)
        }
 
        rdata->res.eof = (offset + count) >= eof;
-       _fix_verify_io_params(rdata->lseg, &rdata->args.pages,
+       _fix_verify_io_params(hdr->lseg, &rdata->args.pages,
                              &rdata->args.pgbase,
                              rdata->args.offset, rdata->args.count);
 
        dprintk("%s: inode(%lx) offset 0x%llx count 0x%Zx eof=%d\n",
-               __func__, rdata->inode->i_ino, offset, count, rdata->res.eof);
+               __func__, inode->i_ino, offset, count, rdata->res.eof);
 
        err = objio_read_pagelist(rdata);
  out:
        if (unlikely(err)) {
-               rdata->pnfs_error = err;
+               hdr->pnfs_error = err;
                dprintk("%s: Returned Error %d\n", __func__, err);
                return PNFS_NOT_ATTEMPTED;
        }
@@ -340,7 +342,7 @@ objlayout_write_done(struct objlayout_io_res *oir, ssize_t status, bool sync)
                wdata->res.count = status;
                wdata->verf.committed = oir->committed;
        } else {
-               wdata->pnfs_error = status;
+               wdata->header->pnfs_error = status;
        }
        objlayout_iodone(oir);
        /* must not use oir after this point */
@@ -363,15 +365,16 @@ enum pnfs_try_status
 objlayout_write_pagelist(struct nfs_write_data *wdata,
                         int how)
 {
+       struct nfs_pgio_header *hdr = wdata->header;
        int err;
 
-       _fix_verify_io_params(wdata->lseg, &wdata->args.pages,
+       _fix_verify_io_params(hdr->lseg, &wdata->args.pages,
                              &wdata->args.pgbase,
                              wdata->args.offset, wdata->args.count);
 
        err = objio_write_pagelist(wdata, how);
        if (unlikely(err)) {
-               wdata->pnfs_error = err;
+               hdr->pnfs_error = err;
                dprintk("%s: Returned Error %d\n", __func__, err);
                return PNFS_NOT_ATTEMPTED;
        }
index d21fceaa9f6263fecff450506653c21ba055872f..aed913c833f422bbf6a88e2726be5eec6d9bbc40 100644 (file)
 
 static struct kmem_cache *nfs_page_cachep;
 
+bool nfs_pgarray_set(struct nfs_page_array *p, unsigned int pagecount)
+{
+       p->npages = pagecount;
+       if (pagecount <= ARRAY_SIZE(p->page_array))
+               p->pagevec = p->page_array;
+       else {
+               p->pagevec = kcalloc(pagecount, sizeof(struct page *), GFP_KERNEL);
+               if (!p->pagevec)
+                       p->npages = 0;
+       }
+       return p->pagevec != NULL;
+}
+
+void nfs_pgheader_init(struct nfs_pageio_descriptor *desc,
+                      struct nfs_pgio_header *hdr,
+                      void (*release)(struct nfs_pgio_header *hdr))
+{
+       hdr->req = nfs_list_entry(desc->pg_list.next);
+       hdr->inode = desc->pg_inode;
+       hdr->cred = hdr->req->wb_context->cred;
+       hdr->io_start = req_offset(hdr->req);
+       hdr->good_bytes = desc->pg_count;
+       hdr->dreq = desc->pg_dreq;
+       hdr->release = release;
+       hdr->completion_ops = desc->pg_completion_ops;
+       if (hdr->completion_ops->init_hdr)
+               hdr->completion_ops->init_hdr(hdr);
+}
+
+void nfs_set_pgio_error(struct nfs_pgio_header *hdr, int error, loff_t pos)
+{
+       spin_lock(&hdr->lock);
+       if (pos < hdr->io_start + hdr->good_bytes) {
+               set_bit(NFS_IOHDR_ERROR, &hdr->flags);
+               clear_bit(NFS_IOHDR_EOF, &hdr->flags);
+               hdr->good_bytes = pos - hdr->io_start;
+               hdr->error = error;
+       }
+       spin_unlock(&hdr->lock);
+}
+
 static inline struct nfs_page *
 nfs_page_alloc(void)
 {
@@ -76,12 +117,8 @@ nfs_create_request(struct nfs_open_context *ctx, struct inode *inode,
         * long write-back delay. This will be adjusted in
         * update_nfs_request below if the region is not locked. */
        req->wb_page    = page;
-       atomic_set(&req->wb_complete, 0);
        req->wb_index   = page->index;
        page_cache_get(page);
-       BUG_ON(PagePrivate(page));
-       BUG_ON(!PageLocked(page));
-       BUG_ON(page->mapping->host != inode);
        req->wb_offset  = offset;
        req->wb_pgbase  = offset;
        req->wb_bytes   = count;
@@ -104,6 +141,15 @@ void nfs_unlock_request(struct nfs_page *req)
        clear_bit(PG_BUSY, &req->wb_flags);
        smp_mb__after_clear_bit();
        wake_up_bit(&req->wb_flags, PG_BUSY);
+}
+
+/**
+ * nfs_unlock_and_release_request - Unlock request and release the nfs_page
+ * @req:
+ */
+void nfs_unlock_and_release_request(struct nfs_page *req)
+{
+       nfs_unlock_request(req);
        nfs_release_request(req);
 }
 
@@ -203,6 +249,7 @@ EXPORT_SYMBOL_GPL(nfs_generic_pg_test);
 void nfs_pageio_init(struct nfs_pageio_descriptor *desc,
                     struct inode *inode,
                     const struct nfs_pageio_ops *pg_ops,
+                    const struct nfs_pgio_completion_ops *compl_ops,
                     size_t bsize,
                     int io_flags)
 {
@@ -215,9 +262,11 @@ void nfs_pageio_init(struct nfs_pageio_descriptor *desc,
        desc->pg_recoalesce = 0;
        desc->pg_inode = inode;
        desc->pg_ops = pg_ops;
+       desc->pg_completion_ops = compl_ops;
        desc->pg_ioflags = io_flags;
        desc->pg_error = 0;
        desc->pg_lseg = NULL;
+       desc->pg_dreq = NULL;
 }
 
 /**
@@ -241,12 +290,12 @@ static bool nfs_can_coalesce_requests(struct nfs_page *prev,
                return false;
        if (req->wb_context->state != prev->wb_context->state)
                return false;
-       if (req->wb_index != (prev->wb_index + 1))
-               return false;
        if (req->wb_pgbase != 0)
                return false;
        if (prev->wb_pgbase + prev->wb_bytes != PAGE_CACHE_SIZE)
                return false;
+       if (req_offset(req) != req_offset(prev) + prev->wb_bytes)
+               return false;
        return pgio->pg_ops->pg_test(pgio, prev, req);
 }
 
index 38512bcd2e98b4c82e3b03e2592061c06897abe5..b8323aa7b54384af8f51b84b3077d98b8f22d951 100644 (file)
@@ -395,6 +395,9 @@ mark_matching_lsegs_invalid(struct pnfs_layout_hdr *lo,
        dprintk("%s:Begin lo %p\n", __func__, lo);
 
        if (list_empty(&lo->plh_segs)) {
+               /* Reset MDS Threshold I/O counters */
+               NFS_I(lo->plh_inode)->write_io = 0;
+               NFS_I(lo->plh_inode)->read_io = 0;
                if (!test_and_set_bit(NFS_LAYOUT_DESTROYED, &lo->plh_flags))
                        put_layout_hdr_locked(lo);
                return 0;
@@ -455,6 +458,7 @@ pnfs_destroy_layout(struct nfs_inode *nfsi)
        spin_unlock(&nfsi->vfs_inode.i_lock);
        pnfs_free_lseg_list(&tmp_list);
 }
+EXPORT_SYMBOL_GPL(pnfs_destroy_layout);
 
 /*
  * Called by the state manger to remove all layouts established under an
@@ -692,6 +696,7 @@ out:
        dprintk("<-- %s status: %d\n", __func__, status);
        return status;
 }
+EXPORT_SYMBOL_GPL(_pnfs_return_layout);
 
 bool pnfs_roc(struct inode *ino)
 {
@@ -930,6 +935,81 @@ pnfs_find_lseg(struct pnfs_layout_hdr *lo,
        return ret;
 }
 
+/*
+ * Use mdsthreshold hints set at each OPEN to determine if I/O should go
+ * to the MDS or over pNFS
+ *
+ * The nfs_inode read_io and write_io fields are cumulative counters reset
+ * when there are no layout segments. Note that in pnfs_update_layout iomode
+ * is set to IOMODE_READ for a READ request, and set to IOMODE_RW for a
+ * WRITE request.
+ *
+ * A return of true means use MDS I/O.
+ *
+ * From rfc 5661:
+ * If a file's size is smaller than the file size threshold, data accesses
+ * SHOULD be sent to the metadata server.  If an I/O request has a length that
+ * is below the I/O size threshold, the I/O SHOULD be sent to the metadata
+ * server.  If both file size and I/O size are provided, the client SHOULD
+ * reach or exceed  both thresholds before sending its read or write
+ * requests to the data server.
+ */
+static bool pnfs_within_mdsthreshold(struct nfs_open_context *ctx,
+                                    struct inode *ino, int iomode)
+{
+       struct nfs4_threshold *t = ctx->mdsthreshold;
+       struct nfs_inode *nfsi = NFS_I(ino);
+       loff_t fsize = i_size_read(ino);
+       bool size = false, size_set = false, io = false, io_set = false, ret = false;
+
+       if (t == NULL)
+               return ret;
+
+       dprintk("%s bm=0x%x rd_sz=%llu wr_sz=%llu rd_io=%llu wr_io=%llu\n",
+               __func__, t->bm, t->rd_sz, t->wr_sz, t->rd_io_sz, t->wr_io_sz);
+
+       switch (iomode) {
+       case IOMODE_READ:
+               if (t->bm & THRESHOLD_RD) {
+                       dprintk("%s fsize %llu\n", __func__, fsize);
+                       size_set = true;
+                       if (fsize < t->rd_sz)
+                               size = true;
+               }
+               if (t->bm & THRESHOLD_RD_IO) {
+                       dprintk("%s nfsi->read_io %llu\n", __func__,
+                               nfsi->read_io);
+                       io_set = true;
+                       if (nfsi->read_io < t->rd_io_sz)
+                               io = true;
+               }
+               break;
+       case IOMODE_RW:
+               if (t->bm & THRESHOLD_WR) {
+                       dprintk("%s fsize %llu\n", __func__, fsize);
+                       size_set = true;
+                       if (fsize < t->wr_sz)
+                               size = true;
+               }
+               if (t->bm & THRESHOLD_WR_IO) {
+                       dprintk("%s nfsi->write_io %llu\n", __func__,
+                               nfsi->write_io);
+                       io_set = true;
+                       if (nfsi->write_io < t->wr_io_sz)
+                               io = true;
+               }
+               break;
+       }
+       if (size_set && io_set) {
+               if (size && io)
+                       ret = true;
+       } else if (size || io)
+               ret = true;
+
+       dprintk("<-- %s size %d io %d ret %d\n", __func__, size, io, ret);
+       return ret;
+}
+
 /*
  * Layout segment is retreived from the server if not cached.
  * The appropriate layout segment is referenced and returned to the caller.
@@ -957,6 +1037,10 @@ pnfs_update_layout(struct inode *ino,
 
        if (!pnfs_enabled_sb(NFS_SERVER(ino)))
                return NULL;
+
+       if (pnfs_within_mdsthreshold(ctx, ino, iomode))
+               return NULL;
+
        spin_lock(&ino->i_lock);
        lo = pnfs_find_alloc_layout(ino, ctx, gfp_flags);
        if (lo == NULL) {
@@ -1082,6 +1166,10 @@ pnfs_generic_pg_init_read(struct nfs_pageio_descriptor *pgio, struct nfs_page *r
 {
        BUG_ON(pgio->pg_lseg != NULL);
 
+       if (req->wb_offset != req->wb_pgbase) {
+               nfs_pageio_reset_read_mds(pgio);
+               return;
+       }
        pgio->pg_lseg = pnfs_update_layout(pgio->pg_inode,
                                           req->wb_context,
                                           req_offset(req),
@@ -1100,6 +1188,10 @@ pnfs_generic_pg_init_write(struct nfs_pageio_descriptor *pgio, struct nfs_page *
 {
        BUG_ON(pgio->pg_lseg != NULL);
 
+       if (req->wb_offset != req->wb_pgbase) {
+               nfs_pageio_reset_write_mds(pgio);
+               return;
+       }
        pgio->pg_lseg = pnfs_update_layout(pgio->pg_inode,
                                           req->wb_context,
                                           req_offset(req),
@@ -1113,26 +1205,31 @@ pnfs_generic_pg_init_write(struct nfs_pageio_descriptor *pgio, struct nfs_page *
 EXPORT_SYMBOL_GPL(pnfs_generic_pg_init_write);
 
 bool
-pnfs_pageio_init_read(struct nfs_pageio_descriptor *pgio, struct inode *inode)
+pnfs_pageio_init_read(struct nfs_pageio_descriptor *pgio, struct inode *inode,
+                     const struct nfs_pgio_completion_ops *compl_ops)
 {
        struct nfs_server *server = NFS_SERVER(inode);
        struct pnfs_layoutdriver_type *ld = server->pnfs_curr_ld;
 
        if (ld == NULL)
                return false;
-       nfs_pageio_init(pgio, inode, ld->pg_read_ops, server->rsize, 0);
+       nfs_pageio_init(pgio, inode, ld->pg_read_ops, compl_ops,
+                       server->rsize, 0);
        return true;
 }
 
 bool
-pnfs_pageio_init_write(struct nfs_pageio_descriptor *pgio, struct inode *inode, int ioflags)
+pnfs_pageio_init_write(struct nfs_pageio_descriptor *pgio, struct inode *inode,
+                      int ioflags,
+                      const struct nfs_pgio_completion_ops *compl_ops)
 {
        struct nfs_server *server = NFS_SERVER(inode);
        struct pnfs_layoutdriver_type *ld = server->pnfs_curr_ld;
 
        if (ld == NULL)
                return false;
-       nfs_pageio_init(pgio, inode, ld->pg_write_ops, server->wsize, ioflags);
+       nfs_pageio_init(pgio, inode, ld->pg_write_ops, compl_ops,
+                       server->wsize, ioflags);
        return true;
 }
 
@@ -1162,13 +1259,15 @@ pnfs_generic_pg_test(struct nfs_pageio_descriptor *pgio, struct nfs_page *prev,
 }
 EXPORT_SYMBOL_GPL(pnfs_generic_pg_test);
 
-static int pnfs_write_done_resend_to_mds(struct inode *inode, struct list_head *head)
+int pnfs_write_done_resend_to_mds(struct inode *inode,
+                               struct list_head *head,
+                               const struct nfs_pgio_completion_ops *compl_ops)
 {
        struct nfs_pageio_descriptor pgio;
        LIST_HEAD(failed);
 
        /* Resend all requests through the MDS */
-       nfs_pageio_init_write_mds(&pgio, inode, FLUSH_STABLE);
+       nfs_pageio_init_write_mds(&pgio, inode, FLUSH_STABLE, compl_ops);
        while (!list_empty(head)) {
                struct nfs_page *req = nfs_list_entry(head->next);
 
@@ -1188,30 +1287,37 @@ static int pnfs_write_done_resend_to_mds(struct inode *inode, struct list_head *
        }
        return 0;
 }
+EXPORT_SYMBOL_GPL(pnfs_write_done_resend_to_mds);
+
+static void pnfs_ld_handle_write_error(struct nfs_write_data *data)
+{
+       struct nfs_pgio_header *hdr = data->header;
+
+       dprintk("pnfs write error = %d\n", hdr->pnfs_error);
+       if (NFS_SERVER(hdr->inode)->pnfs_curr_ld->flags &
+           PNFS_LAYOUTRET_ON_ERROR) {
+               clear_bit(NFS_INO_LAYOUTCOMMIT, &NFS_I(hdr->inode)->flags);
+               pnfs_return_layout(hdr->inode);
+       }
+       if (!test_and_set_bit(NFS_IOHDR_REDO, &hdr->flags))
+               data->task.tk_status = pnfs_write_done_resend_to_mds(hdr->inode,
+                                                       &hdr->pages,
+                                                       hdr->completion_ops);
+}
 
 /*
  * Called by non rpc-based layout drivers
  */
 void pnfs_ld_write_done(struct nfs_write_data *data)
 {
-       if (likely(!data->pnfs_error)) {
+       struct nfs_pgio_header *hdr = data->header;
+
+       if (!hdr->pnfs_error) {
                pnfs_set_layoutcommit(data);
-               data->mds_ops->rpc_call_done(&data->task, data);
-       } else {
-               dprintk("pnfs write error = %d\n", data->pnfs_error);
-               if (NFS_SERVER(data->inode)->pnfs_curr_ld->flags &
-                                               PNFS_LAYOUTRET_ON_ERROR) {
-                       /* Don't lo_commit on error, Server will needs to
-                        * preform a file recovery.
-                        */
-                       clear_bit(NFS_INO_LAYOUTCOMMIT,
-                                 &NFS_I(data->inode)->flags);
-                       pnfs_return_layout(data->inode);
-               }
-               data->task.tk_status = pnfs_write_done_resend_to_mds(data->inode, &data->pages);
-       }
-       put_lseg(data->lseg);
-       data->mds_ops->rpc_release(data);
+               hdr->mds_ops->rpc_call_done(&data->task, data);
+       } else
+               pnfs_ld_handle_write_error(data);
+       hdr->mds_ops->rpc_release(data);
 }
 EXPORT_SYMBOL_GPL(pnfs_ld_write_done);
 
@@ -1219,12 +1325,13 @@ static void
 pnfs_write_through_mds(struct nfs_pageio_descriptor *desc,
                struct nfs_write_data *data)
 {
-       list_splice_tail_init(&data->pages, &desc->pg_list);
-       if (data->req && list_empty(&data->req->wb_list))
-               nfs_list_add_request(data->req, &desc->pg_list);
-       nfs_pageio_reset_write_mds(desc);
-       desc->pg_recoalesce = 1;
-       put_lseg(data->lseg);
+       struct nfs_pgio_header *hdr = data->header;
+
+       if (!test_and_set_bit(NFS_IOHDR_REDO, &hdr->flags)) {
+               list_splice_tail_init(&hdr->pages, &desc->pg_list);
+               nfs_pageio_reset_write_mds(desc);
+               desc->pg_recoalesce = 1;
+       }
        nfs_writedata_release(data);
 }
 
@@ -1234,23 +1341,18 @@ pnfs_try_to_write_data(struct nfs_write_data *wdata,
                        struct pnfs_layout_segment *lseg,
                        int how)
 {
-       struct inode *inode = wdata->inode;
+       struct nfs_pgio_header *hdr = wdata->header;
+       struct inode *inode = hdr->inode;
        enum pnfs_try_status trypnfs;
        struct nfs_server *nfss = NFS_SERVER(inode);
 
-       wdata->mds_ops = call_ops;
-       wdata->lseg = get_lseg(lseg);
+       hdr->mds_ops = call_ops;
 
        dprintk("%s: Writing ino:%lu %u@%llu (how %d)\n", __func__,
                inode->i_ino, wdata->args.count, wdata->args.offset, how);
-
        trypnfs = nfss->pnfs_curr_ld->write_pagelist(wdata, how);
-       if (trypnfs == PNFS_NOT_ATTEMPTED) {
-               put_lseg(wdata->lseg);
-               wdata->lseg = NULL;
-       } else
+       if (trypnfs != PNFS_NOT_ATTEMPTED)
                nfs_inc_stats(inode, NFSIOS_PNFS_WRITE);
-
        dprintk("%s End (trypnfs:%d)\n", __func__, trypnfs);
        return trypnfs;
 }
@@ -1266,7 +1368,7 @@ pnfs_do_multiple_writes(struct nfs_pageio_descriptor *desc, struct list_head *he
        while (!list_empty(head)) {
                enum pnfs_try_status trypnfs;
 
-               data = list_entry(head->next, struct nfs_write_data, list);
+               data = list_first_entry(head, struct nfs_write_data, list);
                list_del_init(&data->list);
 
                trypnfs = pnfs_try_to_write_data(data, call_ops, lseg, how);
@@ -1276,43 +1378,82 @@ pnfs_do_multiple_writes(struct nfs_pageio_descriptor *desc, struct list_head *he
        put_lseg(lseg);
 }
 
+static void pnfs_writehdr_free(struct nfs_pgio_header *hdr)
+{
+       put_lseg(hdr->lseg);
+       nfs_writehdr_free(hdr);
+}
+
 int
 pnfs_generic_pg_writepages(struct nfs_pageio_descriptor *desc)
 {
-       LIST_HEAD(head);
+       struct nfs_write_header *whdr;
+       struct nfs_pgio_header *hdr;
        int ret;
 
-       ret = nfs_generic_flush(desc, &head);
-       if (ret != 0) {
+       whdr = nfs_writehdr_alloc();
+       if (!whdr) {
+               desc->pg_completion_ops->error_cleanup(&desc->pg_list);
                put_lseg(desc->pg_lseg);
                desc->pg_lseg = NULL;
-               return ret;
+               return -ENOMEM;
        }
-       pnfs_do_multiple_writes(desc, &head, desc->pg_ioflags);
-       return 0;
+       hdr = &whdr->header;
+       nfs_pgheader_init(desc, hdr, pnfs_writehdr_free);
+       hdr->lseg = get_lseg(desc->pg_lseg);
+       atomic_inc(&hdr->refcnt);
+       ret = nfs_generic_flush(desc, hdr);
+       if (ret != 0) {
+               put_lseg(desc->pg_lseg);
+               desc->pg_lseg = NULL;
+       } else
+               pnfs_do_multiple_writes(desc, &hdr->rpc_list, desc->pg_ioflags);
+       if (atomic_dec_and_test(&hdr->refcnt))
+               hdr->completion_ops->completion(hdr);
+       return ret;
 }
 EXPORT_SYMBOL_GPL(pnfs_generic_pg_writepages);
 
-static void pnfs_ld_handle_read_error(struct nfs_read_data *data)
+int pnfs_read_done_resend_to_mds(struct inode *inode,
+                               struct list_head *head,
+                               const struct nfs_pgio_completion_ops *compl_ops)
 {
        struct nfs_pageio_descriptor pgio;
+       LIST_HEAD(failed);
 
-       put_lseg(data->lseg);
-       data->lseg = NULL;
-       dprintk("pnfs write error = %d\n", data->pnfs_error);
-       if (NFS_SERVER(data->inode)->pnfs_curr_ld->flags &
-                                               PNFS_LAYOUTRET_ON_ERROR)
-               pnfs_return_layout(data->inode);
-
-       nfs_pageio_init_read_mds(&pgio, data->inode);
-
-       while (!list_empty(&data->pages)) {
-               struct nfs_page *req = nfs_list_entry(data->pages.next);
+       /* Resend all requests through the MDS */
+       nfs_pageio_init_read_mds(&pgio, inode, compl_ops);
+       while (!list_empty(head)) {
+               struct nfs_page *req = nfs_list_entry(head->next);
 
                nfs_list_remove_request(req);
-               nfs_pageio_add_request(&pgio, req);
+               if (!nfs_pageio_add_request(&pgio, req))
+                       nfs_list_add_request(req, &failed);
        }
        nfs_pageio_complete(&pgio);
+
+       if (!list_empty(&failed)) {
+               list_move(&failed, head);
+               return -EIO;
+       }
+       return 0;
+}
+EXPORT_SYMBOL_GPL(pnfs_read_done_resend_to_mds);
+
+static void pnfs_ld_handle_read_error(struct nfs_read_data *data)
+{
+       struct nfs_pgio_header *hdr = data->header;
+
+       dprintk("pnfs read error = %d\n", hdr->pnfs_error);
+       if (NFS_SERVER(hdr->inode)->pnfs_curr_ld->flags &
+           PNFS_LAYOUTRET_ON_ERROR) {
+               clear_bit(NFS_INO_LAYOUTCOMMIT, &NFS_I(hdr->inode)->flags);
+               pnfs_return_layout(hdr->inode);
+       }
+       if (!test_and_set_bit(NFS_IOHDR_REDO, &hdr->flags))
+               data->task.tk_status = pnfs_read_done_resend_to_mds(hdr->inode,
+                                                       &hdr->pages,
+                                                       hdr->completion_ops);
 }
 
 /*
@@ -1320,13 +1461,14 @@ static void pnfs_ld_handle_read_error(struct nfs_read_data *data)
  */
 void pnfs_ld_read_done(struct nfs_read_data *data)
 {
-       if (likely(!data->pnfs_error)) {
+       struct nfs_pgio_header *hdr = data->header;
+
+       if (likely(!hdr->pnfs_error)) {
                __nfs4_read_done_cb(data);
-               data->mds_ops->rpc_call_done(&data->task, data);
+               hdr->mds_ops->rpc_call_done(&data->task, data);
        } else
                pnfs_ld_handle_read_error(data);
-       put_lseg(data->lseg);
-       data->mds_ops->rpc_release(data);
+       hdr->mds_ops->rpc_release(data);
 }
 EXPORT_SYMBOL_GPL(pnfs_ld_read_done);
 
@@ -1334,11 +1476,13 @@ static void
 pnfs_read_through_mds(struct nfs_pageio_descriptor *desc,
                struct nfs_read_data *data)
 {
-       list_splice_tail_init(&data->pages, &desc->pg_list);
-       if (data->req && list_empty(&data->req->wb_list))
-               nfs_list_add_request(data->req, &desc->pg_list);
-       nfs_pageio_reset_read_mds(desc);
-       desc->pg_recoalesce = 1;
+       struct nfs_pgio_header *hdr = data->header;
+
+       if (!test_and_set_bit(NFS_IOHDR_REDO, &hdr->flags)) {
+               list_splice_tail_init(&hdr->pages, &desc->pg_list);
+               nfs_pageio_reset_read_mds(desc);
+               desc->pg_recoalesce = 1;
+       }
        nfs_readdata_release(data);
 }
 
@@ -1350,23 +1494,19 @@ pnfs_try_to_read_data(struct nfs_read_data *rdata,
                       const struct rpc_call_ops *call_ops,
                       struct pnfs_layout_segment *lseg)
 {
-       struct inode *inode = rdata->inode;
+       struct nfs_pgio_header *hdr = rdata->header;
+       struct inode *inode = hdr->inode;
        struct nfs_server *nfss = NFS_SERVER(inode);
        enum pnfs_try_status trypnfs;
 
-       rdata->mds_ops = call_ops;
-       rdata->lseg = get_lseg(lseg);
+       hdr->mds_ops = call_ops;
 
        dprintk("%s: Reading ino:%lu %u@%llu\n",
                __func__, inode->i_ino, rdata->args.count, rdata->args.offset);
 
        trypnfs = nfss->pnfs_curr_ld->read_pagelist(rdata);
-       if (trypnfs == PNFS_NOT_ATTEMPTED) {
-               put_lseg(rdata->lseg);
-               rdata->lseg = NULL;
-       } else {
+       if (trypnfs != PNFS_NOT_ATTEMPTED)
                nfs_inc_stats(inode, NFSIOS_PNFS_READ);
-       }
        dprintk("%s End (trypnfs:%d)\n", __func__, trypnfs);
        return trypnfs;
 }
@@ -1382,7 +1522,7 @@ pnfs_do_multiple_reads(struct nfs_pageio_descriptor *desc, struct list_head *hea
        while (!list_empty(head)) {
                enum pnfs_try_status trypnfs;
 
-               data = list_entry(head->next, struct nfs_read_data, list);
+               data = list_first_entry(head, struct nfs_read_data, list);
                list_del_init(&data->list);
 
                trypnfs = pnfs_try_to_read_data(data, call_ops, lseg);
@@ -1392,20 +1532,40 @@ pnfs_do_multiple_reads(struct nfs_pageio_descriptor *desc, struct list_head *hea
        put_lseg(lseg);
 }
 
+static void pnfs_readhdr_free(struct nfs_pgio_header *hdr)
+{
+       put_lseg(hdr->lseg);
+       nfs_readhdr_free(hdr);
+}
+
 int
 pnfs_generic_pg_readpages(struct nfs_pageio_descriptor *desc)
 {
-       LIST_HEAD(head);
+       struct nfs_read_header *rhdr;
+       struct nfs_pgio_header *hdr;
        int ret;
 
-       ret = nfs_generic_pagein(desc, &head);
-       if (ret != 0) {
+       rhdr = nfs_readhdr_alloc();
+       if (!rhdr) {
+               desc->pg_completion_ops->error_cleanup(&desc->pg_list);
+               ret = -ENOMEM;
                put_lseg(desc->pg_lseg);
                desc->pg_lseg = NULL;
                return ret;
        }
-       pnfs_do_multiple_reads(desc, &head);
-       return 0;
+       hdr = &rhdr->header;
+       nfs_pgheader_init(desc, hdr, pnfs_readhdr_free);
+       hdr->lseg = get_lseg(desc->pg_lseg);
+       atomic_inc(&hdr->refcnt);
+       ret = nfs_generic_pagein(desc, hdr);
+       if (ret != 0) {
+               put_lseg(desc->pg_lseg);
+               desc->pg_lseg = NULL;
+       } else
+               pnfs_do_multiple_reads(desc, &hdr->rpc_list);
+       if (atomic_dec_and_test(&hdr->refcnt))
+               hdr->completion_ops->completion(hdr);
+       return ret;
 }
 EXPORT_SYMBOL_GPL(pnfs_generic_pg_readpages);
 
@@ -1438,30 +1598,32 @@ EXPORT_SYMBOL_GPL(pnfs_set_lo_fail);
 void
 pnfs_set_layoutcommit(struct nfs_write_data *wdata)
 {
-       struct nfs_inode *nfsi = NFS_I(wdata->inode);
+       struct nfs_pgio_header *hdr = wdata->header;
+       struct inode *inode = hdr->inode;
+       struct nfs_inode *nfsi = NFS_I(inode);
        loff_t end_pos = wdata->mds_offset + wdata->res.count;
        bool mark_as_dirty = false;
 
-       spin_lock(&nfsi->vfs_inode.i_lock);
+       spin_lock(&inode->i_lock);
        if (!test_and_set_bit(NFS_INO_LAYOUTCOMMIT, &nfsi->flags)) {
                mark_as_dirty = true;
                dprintk("%s: Set layoutcommit for inode %lu ",
-                       __func__, wdata->inode->i_ino);
+                       __func__, inode->i_ino);
        }
-       if (!test_and_set_bit(NFS_LSEG_LAYOUTCOMMIT, &wdata->lseg->pls_flags)) {
+       if (!test_and_set_bit(NFS_LSEG_LAYOUTCOMMIT, &hdr->lseg->pls_flags)) {
                /* references matched in nfs4_layoutcommit_release */
-               get_lseg(wdata->lseg);
+               get_lseg(hdr->lseg);
        }
        if (end_pos > nfsi->layout->plh_lwb)
                nfsi->layout->plh_lwb = end_pos;
-       spin_unlock(&nfsi->vfs_inode.i_lock);
+       spin_unlock(&inode->i_lock);
        dprintk("%s: lseg %p end_pos %llu\n",
-               __func__, wdata->lseg, nfsi->layout->plh_lwb);
+               __func__, hdr->lseg, nfsi->layout->plh_lwb);
 
        /* if pnfs_layoutcommit_inode() runs between inode locks, the next one
         * will be a noop because NFS_INO_LAYOUTCOMMIT will not be set */
        if (mark_as_dirty)
-               mark_inode_dirty_sync(wdata->inode);
+               mark_inode_dirty_sync(inode);
 }
 EXPORT_SYMBOL_GPL(pnfs_set_layoutcommit);
 
@@ -1550,3 +1712,15 @@ out_free:
        kfree(data);
        goto out;
 }
+
+struct nfs4_threshold *pnfs_mdsthreshold_alloc(void)
+{
+       struct nfs4_threshold *thp;
+
+       thp = kzalloc(sizeof(*thp), GFP_NOFS);
+       if (!thp) {
+               dprintk("%s mdsthreshold allocation failed\n", __func__);
+               return NULL;
+       }
+       return thp;
+}
index 442ebf68eeecf51dfaa6b8835318b53010eefe19..29fd23c0efdcb07c699c5e2e94c1e23dad8de103 100644 (file)
@@ -63,6 +63,7 @@ enum {
        NFS_LAYOUT_BULK_RECALL,         /* bulk recall affecting layout */
        NFS_LAYOUT_ROC,                 /* some lseg had roc bit set */
        NFS_LAYOUT_DESTROYED,           /* no new use of layout allowed */
+       NFS_LAYOUT_INVALID,             /* layout is being destroyed */
 };
 
 enum layoutdriver_policy_flags {
@@ -94,11 +95,20 @@ struct pnfs_layoutdriver_type {
        const struct nfs_pageio_ops *pg_read_ops;
        const struct nfs_pageio_ops *pg_write_ops;
 
+       struct pnfs_ds_commit_info *(*get_ds_info) (struct inode *inode);
        void (*mark_request_commit) (struct nfs_page *req,
-                                       struct pnfs_layout_segment *lseg);
-       void (*clear_request_commit) (struct nfs_page *req);
-       int (*scan_commit_lists) (struct inode *inode, int max, spinlock_t *lock);
-       int (*commit_pagelist)(struct inode *inode, struct list_head *mds_pages, int how);
+                                    struct pnfs_layout_segment *lseg,
+                                    struct nfs_commit_info *cinfo);
+       void (*clear_request_commit) (struct nfs_page *req,
+                                     struct nfs_commit_info *cinfo);
+       int (*scan_commit_lists) (struct nfs_commit_info *cinfo,
+                                 int max);
+       void (*recover_commit_reqs) (struct list_head *list,
+                                    struct nfs_commit_info *cinfo);
+       int (*commit_pagelist)(struct inode *inode,
+                              struct list_head *mds_pages,
+                              int how,
+                              struct nfs_commit_info *cinfo);
 
        /*
         * Return PNFS_ATTEMPTED to indicate the layout code has attempted
@@ -168,8 +178,10 @@ extern int nfs4_proc_layoutreturn(struct nfs4_layoutreturn *lrp);
 void get_layout_hdr(struct pnfs_layout_hdr *lo);
 void put_lseg(struct pnfs_layout_segment *lseg);
 
-bool pnfs_pageio_init_read(struct nfs_pageio_descriptor *, struct inode *);
-bool pnfs_pageio_init_write(struct nfs_pageio_descriptor *, struct inode *, int);
+bool pnfs_pageio_init_read(struct nfs_pageio_descriptor *, struct inode *,
+                          const struct nfs_pgio_completion_ops *);
+bool pnfs_pageio_init_write(struct nfs_pageio_descriptor *, struct inode *,
+                           int, const struct nfs_pgio_completion_ops *);
 
 void set_pnfs_layoutdriver(struct nfs_server *, const struct nfs_fh *, u32);
 void unset_pnfs_layoutdriver(struct nfs_server *);
@@ -211,6 +223,11 @@ struct pnfs_layout_segment *pnfs_update_layout(struct inode *ino,
                                               gfp_t gfp_flags);
 
 void nfs4_deviceid_mark_client_invalid(struct nfs_client *clp);
+int pnfs_read_done_resend_to_mds(struct inode *inode, struct list_head *head,
+                       const struct nfs_pgio_completion_ops *compl_ops);
+int pnfs_write_done_resend_to_mds(struct inode *inode, struct list_head *head,
+                       const struct nfs_pgio_completion_ops *compl_ops);
+struct nfs4_threshold *pnfs_mdsthreshold_alloc(void);
 
 /* nfs4_deviceid_flags */
 enum {
@@ -261,49 +278,66 @@ static inline int pnfs_enabled_sb(struct nfs_server *nfss)
 }
 
 static inline int
-pnfs_commit_list(struct inode *inode, struct list_head *mds_pages, int how)
+pnfs_commit_list(struct inode *inode, struct list_head *mds_pages, int how,
+                struct nfs_commit_info *cinfo)
 {
-       if (!test_and_clear_bit(NFS_INO_PNFS_COMMIT, &NFS_I(inode)->flags))
+       if (cinfo->ds == NULL || cinfo->ds->ncommitting == 0)
                return PNFS_NOT_ATTEMPTED;
-       return NFS_SERVER(inode)->pnfs_curr_ld->commit_pagelist(inode, mds_pages, how);
+       return NFS_SERVER(inode)->pnfs_curr_ld->commit_pagelist(inode, mds_pages, how, cinfo);
+}
+
+static inline struct pnfs_ds_commit_info *
+pnfs_get_ds_info(struct inode *inode)
+{
+       struct pnfs_layoutdriver_type *ld = NFS_SERVER(inode)->pnfs_curr_ld;
+
+       if (ld == NULL || ld->get_ds_info == NULL)
+               return NULL;
+       return ld->get_ds_info(inode);
 }
 
 static inline bool
-pnfs_mark_request_commit(struct nfs_page *req, struct pnfs_layout_segment *lseg)
+pnfs_mark_request_commit(struct nfs_page *req, struct pnfs_layout_segment *lseg,
+                        struct nfs_commit_info *cinfo)
 {
        struct inode *inode = req->wb_context->dentry->d_inode;
        struct pnfs_layoutdriver_type *ld = NFS_SERVER(inode)->pnfs_curr_ld;
 
        if (lseg == NULL || ld->mark_request_commit == NULL)
                return false;
-       ld->mark_request_commit(req, lseg);
+       ld->mark_request_commit(req, lseg, cinfo);
        return true;
 }
 
 static inline bool
-pnfs_clear_request_commit(struct nfs_page *req)
+pnfs_clear_request_commit(struct nfs_page *req, struct nfs_commit_info *cinfo)
 {
        struct inode *inode = req->wb_context->dentry->d_inode;
        struct pnfs_layoutdriver_type *ld = NFS_SERVER(inode)->pnfs_curr_ld;
 
        if (ld == NULL || ld->clear_request_commit == NULL)
                return false;
-       ld->clear_request_commit(req);
+       ld->clear_request_commit(req, cinfo);
        return true;
 }
 
 static inline int
-pnfs_scan_commit_lists(struct inode *inode, int max, spinlock_t *lock)
+pnfs_scan_commit_lists(struct inode *inode, struct nfs_commit_info *cinfo,
+                      int max)
 {
-       struct pnfs_layoutdriver_type *ld = NFS_SERVER(inode)->pnfs_curr_ld;
-       int ret;
-
-       if (ld == NULL || ld->scan_commit_lists == NULL)
+       if (cinfo->ds == NULL || cinfo->ds->nwritten == 0)
                return 0;
-       ret = ld->scan_commit_lists(inode, max, lock);
-       if (ret != 0)
-               set_bit(NFS_INO_PNFS_COMMIT, &NFS_I(inode)->flags);
-       return ret;
+       else
+               return NFS_SERVER(inode)->pnfs_curr_ld->scan_commit_lists(cinfo, max);
+}
+
+static inline void
+pnfs_recover_commit_reqs(struct inode *inode, struct list_head *list,
+                        struct nfs_commit_info *cinfo)
+{
+       if (cinfo->ds == NULL || cinfo->ds->nwritten == 0)
+               return;
+       NFS_SERVER(inode)->pnfs_curr_ld->recover_commit_reqs(list, cinfo);
 }
 
 /* Should the pNFS client commit and return the layout upon a setattr */
@@ -327,6 +361,14 @@ static inline int pnfs_return_layout(struct inode *ino)
        return 0;
 }
 
+static inline bool
+pnfs_use_threshold(struct nfs4_threshold **dst, struct nfs4_threshold *src,
+                  struct nfs_server *nfss)
+{
+       return (dst && src && src->bm != 0 &&
+                                       nfss->pnfs_curr_ld->id == src->l_type);
+}
+
 #ifdef NFS_DEBUG
 void nfs4_print_deviceid(const struct nfs4_deviceid *dev_id);
 #else
@@ -396,45 +438,74 @@ static inline void unset_pnfs_layoutdriver(struct nfs_server *s)
 {
 }
 
-static inline bool pnfs_pageio_init_read(struct nfs_pageio_descriptor *pgio, struct inode *inode)
+static inline bool pnfs_pageio_init_read(struct nfs_pageio_descriptor *pgio, struct inode *inode,
+                                        const struct nfs_pgio_completion_ops *compl_ops)
 {
        return false;
 }
 
-static inline bool pnfs_pageio_init_write(struct nfs_pageio_descriptor *pgio, struct inode *inode, int ioflags)
+static inline bool pnfs_pageio_init_write(struct nfs_pageio_descriptor *pgio, struct inode *inode, int ioflags,
+                                         const struct nfs_pgio_completion_ops *compl_ops)
 {
        return false;
 }
 
 static inline int
-pnfs_commit_list(struct inode *inode, struct list_head *mds_pages, int how)
+pnfs_commit_list(struct inode *inode, struct list_head *mds_pages, int how,
+                struct nfs_commit_info *cinfo)
 {
        return PNFS_NOT_ATTEMPTED;
 }
 
+static inline struct pnfs_ds_commit_info *
+pnfs_get_ds_info(struct inode *inode)
+{
+       return NULL;
+}
+
 static inline bool
-pnfs_mark_request_commit(struct nfs_page *req, struct pnfs_layout_segment *lseg)
+pnfs_mark_request_commit(struct nfs_page *req, struct pnfs_layout_segment *lseg,
+                        struct nfs_commit_info *cinfo)
 {
        return false;
 }
 
 static inline bool
-pnfs_clear_request_commit(struct nfs_page *req)
+pnfs_clear_request_commit(struct nfs_page *req, struct nfs_commit_info *cinfo)
 {
        return false;
 }
 
 static inline int
-pnfs_scan_commit_lists(struct inode *inode, int max, spinlock_t *lock)
+pnfs_scan_commit_lists(struct inode *inode, struct nfs_commit_info *cinfo,
+                      int max)
 {
        return 0;
 }
 
+static inline void
+pnfs_recover_commit_reqs(struct inode *inode, struct list_head *list,
+                        struct nfs_commit_info *cinfo)
+{
+}
+
 static inline int pnfs_layoutcommit_inode(struct inode *inode, bool sync)
 {
        return 0;
 }
 
+static inline bool
+pnfs_use_threshold(struct nfs4_threshold **dst, struct nfs4_threshold *src,
+                  struct nfs_server *nfss)
+{
+       return false;
+}
+
+static inline struct nfs4_threshold *pnfs_mdsthreshold_alloc(void)
+{
+       return NULL;
+}
+
 #endif /* CONFIG_NFS_V4_1 */
 
 #endif /* FS_NFS_PNFS_H */
index d6408b6437de4f9f0c55f9f211f61df129976b7c..a706b6bcc286a5a401318e868b0d1fbab2a206a4 100644 (file)
@@ -178,7 +178,7 @@ nfs_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
 }
 
 static int
-nfs_proc_lookup(struct rpc_clnt *clnt, struct inode *dir, struct qstr *name,
+nfs_proc_lookup(struct inode *dir, struct qstr *name,
                struct nfs_fh *fhandle, struct nfs_fattr *fattr)
 {
        struct nfs_diropargs    arg = {
@@ -640,12 +640,14 @@ nfs_proc_pathconf(struct nfs_server *server, struct nfs_fh *fhandle,
 
 static int nfs_read_done(struct rpc_task *task, struct nfs_read_data *data)
 {
+       struct inode *inode = data->header->inode;
+
        if (nfs_async_handle_expired_key(task))
                return -EAGAIN;
 
-       nfs_invalidate_atime(data->inode);
+       nfs_invalidate_atime(inode);
        if (task->tk_status >= 0) {
-               nfs_refresh_inode(data->inode, data->res.fattr);
+               nfs_refresh_inode(inode, data->res.fattr);
                /* Emulate the eof flag, which isn't normally needed in NFSv2
                 * as it is guaranteed to always return the file attributes
                 */
@@ -667,11 +669,13 @@ static void nfs_proc_read_rpc_prepare(struct rpc_task *task, struct nfs_read_dat
 
 static int nfs_write_done(struct rpc_task *task, struct nfs_write_data *data)
 {
+       struct inode *inode = data->header->inode;
+
        if (nfs_async_handle_expired_key(task))
                return -EAGAIN;
 
        if (task->tk_status >= 0)
-               nfs_post_op_update_inode_force_wcc(data->inode, data->res.fattr);
+               nfs_post_op_update_inode_force_wcc(inode, data->res.fattr);
        return 0;
 }
 
@@ -687,8 +691,13 @@ static void nfs_proc_write_rpc_prepare(struct rpc_task *task, struct nfs_write_d
        rpc_call_start(task);
 }
 
+static void nfs_proc_commit_rpc_prepare(struct rpc_task *task, struct nfs_commit_data *data)
+{
+       BUG();
+}
+
 static void
-nfs_proc_commit_setup(struct nfs_write_data *data, struct rpc_message *msg)
+nfs_proc_commit_setup(struct nfs_commit_data *data, struct rpc_message *msg)
 {
        BUG();
 }
@@ -732,6 +741,7 @@ const struct nfs_rpc_ops nfs_v2_clientops = {
        .file_inode_ops = &nfs_file_inode_operations,
        .file_ops       = &nfs_file_operations,
        .getroot        = nfs_proc_get_root,
+       .submount       = nfs_submount,
        .getattr        = nfs_proc_getattr,
        .setattr        = nfs_proc_setattr,
        .lookup         = nfs_proc_lookup,
@@ -763,6 +773,7 @@ const struct nfs_rpc_ops nfs_v2_clientops = {
        .write_rpc_prepare = nfs_proc_write_rpc_prepare,
        .write_done     = nfs_write_done,
        .commit_setup   = nfs_proc_commit_setup,
+       .commit_rpc_prepare = nfs_proc_commit_rpc_prepare,
        .lock           = nfs_proc_lock,
        .lock_check_bounds = nfs_lock_check_bounds,
        .close_context  = nfs_close_context,
index 0a4be28c2ea3c76f57321bf765708924c4a2fdcf..86ced78362142119328ad827138c0c716668aeac 100644 (file)
 #define NFSDBG_FACILITY                NFSDBG_PAGECACHE
 
 static const struct nfs_pageio_ops nfs_pageio_read_ops;
-static const struct rpc_call_ops nfs_read_partial_ops;
-static const struct rpc_call_ops nfs_read_full_ops;
+static const struct rpc_call_ops nfs_read_common_ops;
+static const struct nfs_pgio_completion_ops nfs_async_read_completion_ops;
 
 static struct kmem_cache *nfs_rdata_cachep;
 
-struct nfs_read_data *nfs_readdata_alloc(unsigned int pagecount)
+struct nfs_read_header *nfs_readhdr_alloc(void)
 {
-       struct nfs_read_data *p;
-
-       p = kmem_cache_zalloc(nfs_rdata_cachep, GFP_KERNEL);
-       if (p) {
-               INIT_LIST_HEAD(&p->pages);
-               p->npages = pagecount;
-               if (pagecount <= ARRAY_SIZE(p->page_array))
-                       p->pagevec = p->page_array;
-               else {
-                       p->pagevec = kcalloc(pagecount, sizeof(struct page *), GFP_KERNEL);
-                       if (!p->pagevec) {
-                               kmem_cache_free(nfs_rdata_cachep, p);
-                               p = NULL;
-                       }
-               }
+       struct nfs_read_header *rhdr;
+
+       rhdr = kmem_cache_zalloc(nfs_rdata_cachep, GFP_KERNEL);
+       if (rhdr) {
+               struct nfs_pgio_header *hdr = &rhdr->header;
+
+               INIT_LIST_HEAD(&hdr->pages);
+               INIT_LIST_HEAD(&hdr->rpc_list);
+               spin_lock_init(&hdr->lock);
+               atomic_set(&hdr->refcnt, 0);
+       }
+       return rhdr;
+}
+
+static struct nfs_read_data *nfs_readdata_alloc(struct nfs_pgio_header *hdr,
+                                               unsigned int pagecount)
+{
+       struct nfs_read_data *data, *prealloc;
+
+       prealloc = &container_of(hdr, struct nfs_read_header, header)->rpc_data;
+       if (prealloc->header == NULL)
+               data = prealloc;
+       else
+               data = kzalloc(sizeof(*data), GFP_KERNEL);
+       if (!data)
+               goto out;
+
+       if (nfs_pgarray_set(&data->pages, pagecount)) {
+               data->header = hdr;
+               atomic_inc(&hdr->refcnt);
+       } else {
+               if (data != prealloc)
+                       kfree(data);
+               data = NULL;
        }
-       return p;
+out:
+       return data;
 }
 
-void nfs_readdata_free(struct nfs_read_data *p)
+void nfs_readhdr_free(struct nfs_pgio_header *hdr)
 {
-       if (p && (p->pagevec != &p->page_array[0]))
-               kfree(p->pagevec);
-       kmem_cache_free(nfs_rdata_cachep, p);
+       struct nfs_read_header *rhdr = container_of(hdr, struct nfs_read_header, header);
+
+       kmem_cache_free(nfs_rdata_cachep, rhdr);
 }
 
 void nfs_readdata_release(struct nfs_read_data *rdata)
 {
+       struct nfs_pgio_header *hdr = rdata->header;
+       struct nfs_read_header *read_header = container_of(hdr, struct nfs_read_header, header);
+
        put_nfs_open_context(rdata->args.context);
-       nfs_readdata_free(rdata);
+       if (rdata->pages.pagevec != rdata->pages.page_array)
+               kfree(rdata->pages.pagevec);
+       if (rdata != &read_header->rpc_data)
+               kfree(rdata);
+       else
+               rdata->header = NULL;
+       if (atomic_dec_and_test(&hdr->refcnt))
+               hdr->completion_ops->completion(hdr);
 }
 
 static
@@ -78,39 +108,11 @@ int nfs_return_empty_page(struct page *page)
        return 0;
 }
 
-static void nfs_readpage_truncate_uninitialised_page(struct nfs_read_data *data)
-{
-       unsigned int remainder = data->args.count - data->res.count;
-       unsigned int base = data->args.pgbase + data->res.count;
-       unsigned int pglen;
-       struct page **pages;
-
-       if (data->res.eof == 0 || remainder == 0)
-               return;
-       /*
-        * Note: "remainder" can never be negative, since we check for
-        *      this in the XDR code.
-        */
-       pages = &data->args.pages[base >> PAGE_CACHE_SHIFT];
-       base &= ~PAGE_CACHE_MASK;
-       pglen = PAGE_CACHE_SIZE - base;
-       for (;;) {
-               if (remainder <= pglen) {
-                       zero_user(*pages, base, remainder);
-                       break;
-               }
-               zero_user(*pages, base, pglen);
-               pages++;
-               remainder -= pglen;
-               pglen = PAGE_CACHE_SIZE;
-               base = 0;
-       }
-}
-
 void nfs_pageio_init_read_mds(struct nfs_pageio_descriptor *pgio,
-               struct inode *inode)
+                             struct inode *inode,
+                             const struct nfs_pgio_completion_ops *compl_ops)
 {
-       nfs_pageio_init(pgio, inode, &nfs_pageio_read_ops,
+       nfs_pageio_init(pgio, inode, &nfs_pageio_read_ops, compl_ops,
                        NFS_SERVER(inode)->rsize, 0);
 }
 
@@ -121,11 +123,12 @@ void nfs_pageio_reset_read_mds(struct nfs_pageio_descriptor *pgio)
 }
 EXPORT_SYMBOL_GPL(nfs_pageio_reset_read_mds);
 
-static void nfs_pageio_init_read(struct nfs_pageio_descriptor *pgio,
-               struct inode *inode)
+void nfs_pageio_init_read(struct nfs_pageio_descriptor *pgio,
+                         struct inode *inode,
+                         const struct nfs_pgio_completion_ops *compl_ops)
 {
-       if (!pnfs_pageio_init_read(pgio, inode))
-               nfs_pageio_init_read_mds(pgio, inode);
+       if (!pnfs_pageio_init_read(pgio, inode, compl_ops))
+               nfs_pageio_init_read_mds(pgio, inode, compl_ops);
 }
 
 int nfs_readpage_async(struct nfs_open_context *ctx, struct inode *inode,
@@ -146,9 +149,10 @@ int nfs_readpage_async(struct nfs_open_context *ctx, struct inode *inode,
        if (len < PAGE_CACHE_SIZE)
                zero_user_segment(page, len, PAGE_CACHE_SIZE);
 
-       nfs_pageio_init_read(&pgio, inode);
+       nfs_pageio_init_read(&pgio, inode, &nfs_async_read_completion_ops);
        nfs_pageio_add_request(&pgio, new);
        nfs_pageio_complete(&pgio);
+       NFS_I(inode)->read_io += pgio.pg_bytes_written;
        return 0;
 }
 
@@ -169,16 +173,49 @@ static void nfs_readpage_release(struct nfs_page *req)
        nfs_release_request(req);
 }
 
-int nfs_initiate_read(struct nfs_read_data *data, struct rpc_clnt *clnt,
-                     const struct rpc_call_ops *call_ops)
+/* Note io was page aligned */
+static void nfs_read_completion(struct nfs_pgio_header *hdr)
+{
+       unsigned long bytes = 0;
+
+       if (test_bit(NFS_IOHDR_REDO, &hdr->flags))
+               goto out;
+       while (!list_empty(&hdr->pages)) {
+               struct nfs_page *req = nfs_list_entry(hdr->pages.next);
+               struct page *page = req->wb_page;
+
+               if (test_bit(NFS_IOHDR_EOF, &hdr->flags)) {
+                       if (bytes > hdr->good_bytes)
+                               zero_user(page, 0, PAGE_SIZE);
+                       else if (hdr->good_bytes - bytes < PAGE_SIZE)
+                               zero_user_segment(page,
+                                       hdr->good_bytes & ~PAGE_MASK,
+                                       PAGE_SIZE);
+               }
+               bytes += req->wb_bytes;
+               if (test_bit(NFS_IOHDR_ERROR, &hdr->flags)) {
+                       if (bytes <= hdr->good_bytes)
+                               SetPageUptodate(page);
+               } else
+                       SetPageUptodate(page);
+               nfs_list_remove_request(req);
+               nfs_readpage_release(req);
+       }
+out:
+       hdr->release(hdr);
+}
+
+int nfs_initiate_read(struct rpc_clnt *clnt,
+                     struct nfs_read_data *data,
+                     const struct rpc_call_ops *call_ops, int flags)
 {
-       struct inode *inode = data->inode;
+       struct inode *inode = data->header->inode;
        int swap_flags = IS_SWAPFILE(inode) ? NFS_RPC_SWAPFLAGS : 0;
        struct rpc_task *task;
        struct rpc_message msg = {
                .rpc_argp = &data->args,
                .rpc_resp = &data->res,
-               .rpc_cred = data->cred,
+               .rpc_cred = data->header->cred,
        };
        struct rpc_task_setup task_setup_data = {
                .task = &data->task,
@@ -187,7 +224,7 @@ int nfs_initiate_read(struct nfs_read_data *data, struct rpc_clnt *clnt,
                .callback_ops = call_ops,
                .callback_data = data,
                .workqueue = nfsiod_workqueue,
-               .flags = RPC_TASK_ASYNC | swap_flags,
+               .flags = RPC_TASK_ASYNC | swap_flags | flags,
        };
 
        /* Set up the initial task struct. */
@@ -212,19 +249,15 @@ EXPORT_SYMBOL_GPL(nfs_initiate_read);
 /*
  * Set up the NFS read request struct
  */
-static void nfs_read_rpcsetup(struct nfs_page *req, struct nfs_read_data *data,
+static void nfs_read_rpcsetup(struct nfs_read_data *data,
                unsigned int count, unsigned int offset)
 {
-       struct inode *inode = req->wb_context->dentry->d_inode;
-
-       data->req         = req;
-       data->inode       = inode;
-       data->cred        = req->wb_context->cred;
+       struct nfs_page *req = data->header->req;
 
-       data->args.fh     = NFS_FH(inode);
+       data->args.fh     = NFS_FH(data->header->inode);
        data->args.offset = req_offset(req) + offset;
        data->args.pgbase = req->wb_pgbase + offset;
-       data->args.pages  = data->pagevec;
+       data->args.pages  = data->pages.pagevec;
        data->args.count  = count;
        data->args.context = get_nfs_open_context(req->wb_context);
        data->args.lock_context = req->wb_lock_context;
@@ -238,9 +271,9 @@ static void nfs_read_rpcsetup(struct nfs_page *req, struct nfs_read_data *data,
 static int nfs_do_read(struct nfs_read_data *data,
                const struct rpc_call_ops *call_ops)
 {
-       struct inode *inode = data->args.context->dentry->d_inode;
+       struct inode *inode = data->header->inode;
 
-       return nfs_initiate_read(data, NFS_CLIENT(inode), call_ops);
+       return nfs_initiate_read(NFS_CLIENT(inode), data, call_ops, 0);
 }
 
 static int
@@ -253,7 +286,7 @@ nfs_do_multiple_reads(struct list_head *head,
        while (!list_empty(head)) {
                int ret2;
 
-               data = list_entry(head->next, struct nfs_read_data, list);
+               data = list_first_entry(head, struct nfs_read_data, list);
                list_del_init(&data->list);
 
                ret2 = nfs_do_read(data, call_ops);
@@ -275,6 +308,24 @@ nfs_async_read_error(struct list_head *head)
        }
 }
 
+static const struct nfs_pgio_completion_ops nfs_async_read_completion_ops = {
+       .error_cleanup = nfs_async_read_error,
+       .completion = nfs_read_completion,
+};
+
+static void nfs_pagein_error(struct nfs_pageio_descriptor *desc,
+               struct nfs_pgio_header *hdr)
+{
+       set_bit(NFS_IOHDR_REDO, &hdr->flags);
+       while (!list_empty(&hdr->rpc_list)) {
+               struct nfs_read_data *data = list_first_entry(&hdr->rpc_list,
+                               struct nfs_read_data, list);
+               list_del(&data->list);
+               nfs_readdata_release(data);
+       }
+       desc->pg_completion_ops->error_cleanup(&desc->pg_list);
+}
+
 /*
  * Generate multiple requests to fill a single page.
  *
@@ -288,93 +339,95 @@ nfs_async_read_error(struct list_head *head)
  * won't see the new data until our attribute cache is updated.  This is more
  * or less conventional NFS client behavior.
  */
-static int nfs_pagein_multi(struct nfs_pageio_descriptor *desc, struct list_head *res)
+static int nfs_pagein_multi(struct nfs_pageio_descriptor *desc,
+                           struct nfs_pgio_header *hdr)
 {
-       struct nfs_page *req = nfs_list_entry(desc->pg_list.next);
+       struct nfs_page *req = hdr->req;
        struct page *page = req->wb_page;
        struct nfs_read_data *data;
        size_t rsize = desc->pg_bsize, nbytes;
        unsigned int offset;
-       int requests = 0;
-       int ret = 0;
-
-       nfs_list_remove_request(req);
 
        offset = 0;
        nbytes = desc->pg_count;
        do {
                size_t len = min(nbytes,rsize);
 
-               data = nfs_readdata_alloc(1);
-               if (!data)
-                       goto out_bad;
-               data->pagevec[0] = page;
-               nfs_read_rpcsetup(req, data, len, offset);
-               list_add(&data->list, res);
-               requests++;
+               data = nfs_readdata_alloc(hdr, 1);
+               if (!data) {
+                       nfs_pagein_error(desc, hdr);
+                       return -ENOMEM;
+               }
+               data->pages.pagevec[0] = page;
+               nfs_read_rpcsetup(data, len, offset);
+               list_add(&data->list, &hdr->rpc_list);
                nbytes -= len;
                offset += len;
-       } while(nbytes != 0);
-       atomic_set(&req->wb_complete, requests);
-       desc->pg_rpc_callops = &nfs_read_partial_ops;
-       return ret;
-out_bad:
-       while (!list_empty(res)) {
-               data = list_entry(res->next, struct nfs_read_data, list);
-               list_del(&data->list);
-               nfs_readdata_release(data);
-       }
-       nfs_readpage_release(req);
-       return -ENOMEM;
+       } while (nbytes != 0);
+
+       nfs_list_remove_request(req);
+       nfs_list_add_request(req, &hdr->pages);
+       desc->pg_rpc_callops = &nfs_read_common_ops;
+       return 0;
 }
 
-static int nfs_pagein_one(struct nfs_pageio_descriptor *desc, struct list_head *res)
+static int nfs_pagein_one(struct nfs_pageio_descriptor *desc,
+                         struct nfs_pgio_header *hdr)
 {
        struct nfs_page         *req;
        struct page             **pages;
-       struct nfs_read_data    *data;
+       struct nfs_read_data    *data;
        struct list_head *head = &desc->pg_list;
-       int ret = 0;
 
-       data = nfs_readdata_alloc(nfs_page_array_len(desc->pg_base,
-                                                    desc->pg_count));
+       data = nfs_readdata_alloc(hdr, nfs_page_array_len(desc->pg_base,
+                                                         desc->pg_count));
        if (!data) {
-               nfs_async_read_error(head);
-               ret = -ENOMEM;
-               goto out;
+               nfs_pagein_error(desc, hdr);
+               return -ENOMEM;
        }
 
-       pages = data->pagevec;
+       pages = data->pages.pagevec;
        while (!list_empty(head)) {
                req = nfs_list_entry(head->next);
                nfs_list_remove_request(req);
-               nfs_list_add_request(req, &data->pages);
+               nfs_list_add_request(req, &hdr->pages);
                *pages++ = req->wb_page;
        }
-       req = nfs_list_entry(data->pages.next);
 
-       nfs_read_rpcsetup(req, data, desc->pg_count, 0);
-       list_add(&data->list, res);
-       desc->pg_rpc_callops = &nfs_read_full_ops;
-out:
-       return ret;
+       nfs_read_rpcsetup(data, desc->pg_count, 0);
+       list_add(&data->list, &hdr->rpc_list);
+       desc->pg_rpc_callops = &nfs_read_common_ops;
+       return 0;
 }
 
-int nfs_generic_pagein(struct nfs_pageio_descriptor *desc, struct list_head *head)
+int nfs_generic_pagein(struct nfs_pageio_descriptor *desc,
+                      struct nfs_pgio_header *hdr)
 {
        if (desc->pg_bsize < PAGE_CACHE_SIZE)
-               return nfs_pagein_multi(desc, head);
-       return nfs_pagein_one(desc, head);
+               return nfs_pagein_multi(desc, hdr);
+       return nfs_pagein_one(desc, hdr);
 }
 
 static int nfs_generic_pg_readpages(struct nfs_pageio_descriptor *desc)
 {
-       LIST_HEAD(head);
+       struct nfs_read_header *rhdr;
+       struct nfs_pgio_header *hdr;
        int ret;
 
-       ret = nfs_generic_pagein(desc, &head);
+       rhdr = nfs_readhdr_alloc();
+       if (!rhdr) {
+               desc->pg_completion_ops->error_cleanup(&desc->pg_list);
+               return -ENOMEM;
+       }
+       hdr = &rhdr->header;
+       nfs_pgheader_init(desc, hdr, nfs_readhdr_free);
+       atomic_inc(&hdr->refcnt);
+       ret = nfs_generic_pagein(desc, hdr);
        if (ret == 0)
-               ret = nfs_do_multiple_reads(&head, desc->pg_rpc_callops);
+               ret = nfs_do_multiple_reads(&hdr->rpc_list,
+                                           desc->pg_rpc_callops);
+       if (atomic_dec_and_test(&hdr->refcnt))
+               hdr->completion_ops->completion(hdr);
        return ret;
 }
 
@@ -389,20 +442,21 @@ static const struct nfs_pageio_ops nfs_pageio_read_ops = {
  */
 int nfs_readpage_result(struct rpc_task *task, struct nfs_read_data *data)
 {
+       struct inode *inode = data->header->inode;
        int status;
 
        dprintk("NFS: %s: %5u, (status %d)\n", __func__, task->tk_pid,
                        task->tk_status);
 
-       status = NFS_PROTO(data->inode)->read_done(task, data);
+       status = NFS_PROTO(inode)->read_done(task, data);
        if (status != 0)
                return status;
 
-       nfs_add_stats(data->inode, NFSIOS_SERVERREADBYTES, data->res.count);
+       nfs_add_stats(inode, NFSIOS_SERVERREADBYTES, data->res.count);
 
        if (task->tk_status == -ESTALE) {
-               set_bit(NFS_INO_STALE, &NFS_I(data->inode)->flags);
-               nfs_mark_for_revalidate(data->inode);
+               set_bit(NFS_INO_STALE, &NFS_I(inode)->flags);
+               nfs_mark_for_revalidate(inode);
        }
        return 0;
 }
@@ -412,15 +466,13 @@ static void nfs_readpage_retry(struct rpc_task *task, struct nfs_read_data *data
        struct nfs_readargs *argp = &data->args;
        struct nfs_readres *resp = &data->res;
 
-       if (resp->eof || resp->count == argp->count)
-               return;
-
        /* This is a short read! */
-       nfs_inc_stats(data->inode, NFSIOS_SHORTREAD);
+       nfs_inc_stats(data->header->inode, NFSIOS_SHORTREAD);
        /* Has the server at least made some progress? */
-       if (resp->count == 0)
+       if (resp->count == 0) {
+               nfs_set_pgio_error(data->header, -EIO, argp->offset);
                return;
-
+       }
        /* Yes, so retry the read at the end of the data */
        data->mds_offset += resp->count;
        argp->offset += resp->count;
@@ -429,114 +481,46 @@ static void nfs_readpage_retry(struct rpc_task *task, struct nfs_read_data *data
        rpc_restart_call_prepare(task);
 }
 
-/*
- * Handle a read reply that fills part of a page.
- */
-static void nfs_readpage_result_partial(struct rpc_task *task, void *calldata)
+static void nfs_readpage_result_common(struct rpc_task *task, void *calldata)
 {
        struct nfs_read_data *data = calldata;
+       struct nfs_pgio_header *hdr = data->header;
+
+       /* Note the only returns of nfs_readpage_result are 0 and -EAGAIN */
        if (nfs_readpage_result(task, data) != 0)
                return;
        if (task->tk_status < 0)
-               return;
-
-       nfs_readpage_truncate_uninitialised_page(data);
-       nfs_readpage_retry(task, data);
+               nfs_set_pgio_error(hdr, task->tk_status, data->args.offset);
+       else if (data->res.eof) {
+               loff_t bound;
+
+               bound = data->args.offset + data->res.count;
+               spin_lock(&hdr->lock);
+               if (bound < hdr->io_start + hdr->good_bytes) {
+                       set_bit(NFS_IOHDR_EOF, &hdr->flags);
+                       clear_bit(NFS_IOHDR_ERROR, &hdr->flags);
+                       hdr->good_bytes = bound - hdr->io_start;
+               }
+               spin_unlock(&hdr->lock);
+       } else if (data->res.count != data->args.count)
+               nfs_readpage_retry(task, data);
 }
 
-static void nfs_readpage_release_partial(void *calldata)
+static void nfs_readpage_release_common(void *calldata)
 {
-       struct nfs_read_data *data = calldata;
-       struct nfs_page *req = data->req;
-       struct page *page = req->wb_page;
-       int status = data->task.tk_status;
-
-       if (status < 0)
-               set_bit(PG_PARTIAL_READ_FAILED, &req->wb_flags);
-
-       if (atomic_dec_and_test(&req->wb_complete)) {
-               if (!test_bit(PG_PARTIAL_READ_FAILED, &req->wb_flags))
-                       SetPageUptodate(page);
-               nfs_readpage_release(req);
-       }
        nfs_readdata_release(calldata);
 }
 
 void nfs_read_prepare(struct rpc_task *task, void *calldata)
 {
        struct nfs_read_data *data = calldata;
-       NFS_PROTO(data->inode)->read_rpc_prepare(task, data);
-}
-
-static const struct rpc_call_ops nfs_read_partial_ops = {
-       .rpc_call_prepare = nfs_read_prepare,
-       .rpc_call_done = nfs_readpage_result_partial,
-       .rpc_release = nfs_readpage_release_partial,
-};
-
-static void nfs_readpage_set_pages_uptodate(struct nfs_read_data *data)
-{
-       unsigned int count = data->res.count;
-       unsigned int base = data->args.pgbase;
-       struct page **pages;
-
-       if (data->res.eof)
-               count = data->args.count;
-       if (unlikely(count == 0))
-               return;
-       pages = &data->args.pages[base >> PAGE_CACHE_SHIFT];
-       base &= ~PAGE_CACHE_MASK;
-       count += base;
-       for (;count >= PAGE_CACHE_SIZE; count -= PAGE_CACHE_SIZE, pages++)
-               SetPageUptodate(*pages);
-       if (count == 0)
-               return;
-       /* Was this a short read? */
-       if (data->res.eof || data->res.count == data->args.count)
-               SetPageUptodate(*pages);
-}
-
-/*
- * This is the callback from RPC telling us whether a reply was
- * received or some error occurred (timeout or socket shutdown).
- */
-static void nfs_readpage_result_full(struct rpc_task *task, void *calldata)
-{
-       struct nfs_read_data *data = calldata;
-
-       if (nfs_readpage_result(task, data) != 0)
-               return;
-       if (task->tk_status < 0)
-               return;
-       /*
-        * Note: nfs_readpage_retry may change the values of
-        * data->args. In the multi-page case, we therefore need
-        * to ensure that we call nfs_readpage_set_pages_uptodate()
-        * first.
-        */
-       nfs_readpage_truncate_uninitialised_page(data);
-       nfs_readpage_set_pages_uptodate(data);
-       nfs_readpage_retry(task, data);
-}
-
-static void nfs_readpage_release_full(void *calldata)
-{
-       struct nfs_read_data *data = calldata;
-
-       while (!list_empty(&data->pages)) {
-               struct nfs_page *req = nfs_list_entry(data->pages.next);
-
-               nfs_list_remove_request(req);
-               nfs_readpage_release(req);
-       }
-       nfs_readdata_release(calldata);
+       NFS_PROTO(data->header->inode)->read_rpc_prepare(task, data);
 }
 
-static const struct rpc_call_ops nfs_read_full_ops = {
+static const struct rpc_call_ops nfs_read_common_ops = {
        .rpc_call_prepare = nfs_read_prepare,
-       .rpc_call_done = nfs_readpage_result_full,
-       .rpc_release = nfs_readpage_release_full,
+       .rpc_call_done = nfs_readpage_result_common,
+       .rpc_release = nfs_readpage_release_common,
 };
 
 /*
@@ -668,11 +652,12 @@ int nfs_readpages(struct file *filp, struct address_space *mapping,
        if (ret == 0)
                goto read_complete; /* all pages were read */
 
-       nfs_pageio_init_read(&pgio, inode);
+       nfs_pageio_init_read(&pgio, inode, &nfs_async_read_completion_ops);
 
        ret = read_cache_pages(mapping, pages, readpage_async_filler, &desc);
 
        nfs_pageio_complete(&pgio);
+       NFS_I(inode)->read_io += pgio.pg_bytes_written;
        npages = (pgio.pg_bytes_written + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
        nfs_add_stats(inode, NFSIOS_READPAGES, npages);
 read_complete:
@@ -684,7 +669,7 @@ out:
 int __init nfs_init_readpagecache(void)
 {
        nfs_rdata_cachep = kmem_cache_create("nfs_read_data",
-                                            sizeof(struct nfs_read_data),
+                                            sizeof(struct nfs_read_header),
                                             0, SLAB_HWCACHE_ALIGN,
                                             NULL);
        if (nfs_rdata_cachep == NULL)
index 4ac7fca7e4bf32fc01ac980c26dfcb255325f3b1..ff656c022684e9e2b0d94587cf9d807d670bd715 100644 (file)
@@ -66,6 +66,7 @@
 #include "pnfs.h"
 
 #define NFSDBG_FACILITY                NFSDBG_VFS
+#define NFS_TEXT_DATA          1
 
 #ifdef CONFIG_NFS_V3
 #define NFS_DEFAULT_VERSION 3
@@ -277,12 +278,22 @@ static match_table_t nfs_vers_tokens = {
        { Opt_vers_err, NULL }
 };
 
+struct nfs_mount_info {
+       void (*fill_super)(struct super_block *, struct nfs_mount_info *);
+       int (*set_security)(struct super_block *, struct dentry *, struct nfs_mount_info *);
+       struct nfs_parsed_mount_data *parsed;
+       struct nfs_clone_mount *cloned;
+       struct nfs_fh *mntfh;
+};
+
 static void nfs_umount_begin(struct super_block *);
 static int  nfs_statfs(struct dentry *, struct kstatfs *);
 static int  nfs_show_options(struct seq_file *, struct dentry *);
 static int  nfs_show_devname(struct seq_file *, struct dentry *);
 static int  nfs_show_path(struct seq_file *, struct dentry *);
 static int  nfs_show_stats(struct seq_file *, struct dentry *);
+static struct dentry *nfs_fs_mount_common(struct file_system_type *,
+               struct nfs_server *, int, const char *, struct nfs_mount_info *);
 static struct dentry *nfs_fs_mount(struct file_system_type *,
                int, const char *, void *);
 static struct dentry *nfs_xdev_mount(struct file_system_type *fs_type,
@@ -323,12 +334,11 @@ static const struct super_operations nfs_sops = {
 };
 
 #ifdef CONFIG_NFS_V4
-static int nfs4_validate_text_mount_data(void *options,
+static void nfs4_validate_mount_flags(struct nfs_parsed_mount_data *);
+static int nfs4_validate_mount_data(void *options,
        struct nfs_parsed_mount_data *args, const char *dev_name);
 static struct dentry *nfs4_try_mount(int flags, const char *dev_name,
-       struct nfs_parsed_mount_data *data);
-static struct dentry *nfs4_mount(struct file_system_type *fs_type,
-       int flags, const char *dev_name, void *raw_data);
+       struct nfs_mount_info *mount_info);
 static struct dentry *nfs4_remote_mount(struct file_system_type *fs_type,
        int flags, const char *dev_name, void *raw_data);
 static struct dentry *nfs4_xdev_mount(struct file_system_type *fs_type,
@@ -342,7 +352,7 @@ static void nfs4_kill_super(struct super_block *sb);
 static struct file_system_type nfs4_fs_type = {
        .owner          = THIS_MODULE,
        .name           = "nfs4",
-       .mount          = nfs4_mount,
+       .mount          = nfs_fs_mount,
        .kill_sb        = nfs4_kill_super,
        .fs_flags       = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA,
 };
@@ -786,8 +796,8 @@ static void show_pnfs(struct seq_file *m, struct nfs_server *server)
 
 static void show_implementation_id(struct seq_file *m, struct nfs_server *nfss)
 {
-       if (nfss->nfs_client && nfss->nfs_client->impl_id) {
-               struct nfs41_impl_id *impl_id = nfss->nfs_client->impl_id;
+       if (nfss->nfs_client && nfss->nfs_client->cl_implid) {
+               struct nfs41_impl_id *impl_id = nfss->nfs_client->cl_implid;
                seq_printf(m, "\n\timpl_id:\tname='%s',domain='%s',"
                           "date='%llu,%u'",
                           impl_id->name, impl_id->domain,
@@ -938,7 +948,7 @@ static void nfs_umount_begin(struct super_block *sb)
                rpc_killall_tasks(rpc);
 }
 
-static struct nfs_parsed_mount_data *nfs_alloc_parsed_mount_data(unsigned int version)
+static struct nfs_parsed_mount_data *nfs_alloc_parsed_mount_data(void)
 {
        struct nfs_parsed_mount_data *data;
 
@@ -953,8 +963,8 @@ static struct nfs_parsed_mount_data *nfs_alloc_parsed_mount_data(unsigned int ve
                data->nfs_server.protocol = XPRT_TRANSPORT_TCP;
                data->auth_flavors[0]   = RPC_AUTH_UNIX;
                data->auth_flavor_len   = 1;
-               data->version           = version;
                data->minorversion      = 0;
+               data->need_mount        = true;
                data->net               = current->nsproxy->net_ns;
                security_init_mnt_opts(&data->lsm_opts);
        }
@@ -1674,8 +1684,8 @@ static int nfs_walk_authlist(struct nfs_parsed_mount_data *args,
  * Use the remote server's MOUNT service to request the NFS file handle
  * corresponding to the provided path.
  */
-static int nfs_try_mount(struct nfs_parsed_mount_data *args,
-                        struct nfs_fh *root_fh)
+static int nfs_request_mount(struct nfs_parsed_mount_data *args,
+                            struct nfs_fh *root_fh)
 {
        rpc_authflavor_t server_authlist[NFS_MAX_SECFLAVORS];
        unsigned int server_authlist_len = ARRAY_SIZE(server_authlist);
@@ -1738,6 +1748,26 @@ static int nfs_try_mount(struct nfs_parsed_mount_data *args,
        return nfs_walk_authlist(args, &request);
 }
 
+static struct dentry *nfs_try_mount(int flags, const char *dev_name,
+                                   struct nfs_mount_info *mount_info)
+{
+       int status;
+       struct nfs_server *server;
+
+       if (mount_info->parsed->need_mount) {
+               status = nfs_request_mount(mount_info->parsed, mount_info->mntfh);
+               if (status)
+                       return ERR_PTR(status);
+       }
+
+       /* Get a volume representation */
+       server = nfs_create_server(mount_info->parsed, mount_info->mntfh);
+       if (IS_ERR(server))
+               return ERR_CAST(server);
+
+       return nfs_fs_mount_common(&nfs_fs_type, server, flags, dev_name, mount_info);
+}
+
 /*
  * Split "dev_name" into "hostname:export_path".
  *
@@ -1826,10 +1856,10 @@ out_path:
  * + breaking back: trying proto=udp after proto=tcp, v2 after v3,
  *   mountproto=tcp after mountproto=udp, and so on
  */
-static int nfs_validate_mount_data(void *options,
-                                  struct nfs_parsed_mount_data *args,
-                                  struct nfs_fh *mntfh,
-                                  const char *dev_name)
+static int nfs23_validate_mount_data(void *options,
+                                    struct nfs_parsed_mount_data *args,
+                                    struct nfs_fh *mntfh,
+                                    const char *dev_name)
 {
        struct nfs_mount_data *data = (struct nfs_mount_data *)options;
        struct sockaddr *sap = (struct sockaddr *)&args->nfs_server.address;
@@ -1883,6 +1913,7 @@ static int nfs_validate_mount_data(void *options,
                args->acregmax          = data->acregmax;
                args->acdirmin          = data->acdirmin;
                args->acdirmax          = data->acdirmax;
+               args->need_mount        = false;
 
                memcpy(sap, &data->addr, sizeof(data->addr));
                args->nfs_server.addrlen = sizeof(data->addr);
@@ -1934,43 +1965,8 @@ static int nfs_validate_mount_data(void *options,
                }
 
                break;
-       default: {
-               int status;
-
-               if (nfs_parse_mount_options((char *)options, args) == 0)
-                       return -EINVAL;
-
-               if (!nfs_verify_server_address(sap))
-                       goto out_no_address;
-
-               if (args->version == 4)
-#ifdef CONFIG_NFS_V4
-                       return nfs4_validate_text_mount_data(options,
-                                                            args, dev_name);
-#else
-                       goto out_v4_not_compiled;
-#endif
-
-               nfs_set_port(sap, &args->nfs_server.port, 0);
-
-               nfs_set_mount_transport_protocol(args);
-
-               status = nfs_parse_devname(dev_name,
-                                          &args->nfs_server.hostname,
-                                          PAGE_SIZE,
-                                          &args->nfs_server.export_path,
-                                          NFS_MAXPATHLEN);
-               if (!status)
-                       status = nfs_try_mount(args, mntfh);
-
-               kfree(args->nfs_server.export_path);
-               args->nfs_server.export_path = NULL;
-
-               if (status)
-                       return status;
-
-               break;
-               }
+       default:
+               return NFS_TEXT_DATA;
        }
 
 #ifndef CONFIG_NFS_V3
@@ -1999,12 +1995,6 @@ out_v3_not_compiled:
        return -EPROTONOSUPPORT;
 #endif /* !CONFIG_NFS_V3 */
 
-#ifndef CONFIG_NFS_V4
-out_v4_not_compiled:
-       dfprintk(MOUNT, "NFS: NFSv4 is not compiled into kernel\n");
-       return -EPROTONOSUPPORT;
-#endif /* !CONFIG_NFS_V4 */
-
 out_nomem:
        dfprintk(MOUNT, "NFS: not enough memory to handle mount options\n");
        return -ENOMEM;
@@ -2018,6 +2008,82 @@ out_invalid_fh:
        return -EINVAL;
 }
 
+#ifdef CONFIG_NFS_V4
+static int nfs_validate_mount_data(struct file_system_type *fs_type,
+                                  void *options,
+                                  struct nfs_parsed_mount_data *args,
+                                  struct nfs_fh *mntfh,
+                                  const char *dev_name)
+{
+       if (fs_type == &nfs_fs_type)
+               return nfs23_validate_mount_data(options, args, mntfh, dev_name);
+       return nfs4_validate_mount_data(options, args, dev_name);
+}
+#else
+static int nfs_validate_mount_data(struct file_system_type *fs_type,
+                                  void *options,
+                                  struct nfs_parsed_mount_data *args,
+                                  struct nfs_fh *mntfh,
+                                  const char *dev_name)
+{
+       return nfs23_validate_mount_data(options, args, mntfh, dev_name);
+}
+#endif
+
+static int nfs_validate_text_mount_data(void *options,
+                                       struct nfs_parsed_mount_data *args,
+                                       const char *dev_name)
+{
+       int port = 0;
+       int max_namelen = PAGE_SIZE;
+       int max_pathlen = NFS_MAXPATHLEN;
+       struct sockaddr *sap = (struct sockaddr *)&args->nfs_server.address;
+
+       if (nfs_parse_mount_options((char *)options, args) == 0)
+               return -EINVAL;
+
+       if (!nfs_verify_server_address(sap))
+               goto out_no_address;
+
+       if (args->version == 4) {
+#ifdef CONFIG_NFS_V4
+               port = NFS_PORT;
+               max_namelen = NFS4_MAXNAMLEN;
+               max_pathlen = NFS4_MAXPATHLEN;
+               nfs_validate_transport_protocol(args);
+               nfs4_validate_mount_flags(args);
+#else
+               goto out_v4_not_compiled;
+#endif /* CONFIG_NFS_V4 */
+       } else
+               nfs_set_mount_transport_protocol(args);
+
+       nfs_set_port(sap, &args->nfs_server.port, port);
+
+       if (args->auth_flavor_len > 1)
+               goto out_bad_auth;
+
+       return nfs_parse_devname(dev_name,
+                                  &args->nfs_server.hostname,
+                                  max_namelen,
+                                  &args->nfs_server.export_path,
+                                  max_pathlen);
+
+#ifndef CONFIG_NFS_V4
+out_v4_not_compiled:
+       dfprintk(MOUNT, "NFS: NFSv4 is not compiled into kernel\n");
+       return -EPROTONOSUPPORT;
+#endif /* !CONFIG_NFS_V4 */
+
+out_no_address:
+       dfprintk(MOUNT, "NFS: mount program didn't pass remote address\n");
+       return -EINVAL;
+
+out_bad_auth:
+       dfprintk(MOUNT, "NFS: Too many RPC auth flavours specified\n");
+       return -EINVAL;
+}
+
 static int
 nfs_compare_remount_data(struct nfs_server *nfss,
                         struct nfs_parsed_mount_data *data)
@@ -2129,8 +2195,9 @@ static inline void nfs_initialise_sb(struct super_block *sb)
  * Finish setting up an NFS2/3 superblock
  */
 static void nfs_fill_super(struct super_block *sb,
-                          struct nfs_parsed_mount_data *data)
+                          struct nfs_mount_info *mount_info)
 {
+       struct nfs_parsed_mount_data *data = mount_info->parsed;
        struct nfs_server *server = NFS_SB(sb);
 
        sb->s_blocksize_bits = 0;
@@ -2154,8 +2221,9 @@ static void nfs_fill_super(struct super_block *sb,
  * Finish setting up a cloned NFS2/3 superblock
  */
 static void nfs_clone_super(struct super_block *sb,
-                           const struct super_block *old_sb)
+                           struct nfs_mount_info *mount_info)
 {
+       const struct super_block *old_sb = mount_info->cloned->sb;
        struct nfs_server *server = NFS_SB(sb);
 
        sb->s_blocksize_bits = old_sb->s_blocksize_bits;
@@ -2278,52 +2346,70 @@ static int nfs_compare_super(struct super_block *sb, void *data)
        return nfs_compare_mount_options(sb, server, mntflags);
 }
 
+#ifdef CONFIG_NFS_FSCACHE
+static void nfs_get_cache_cookie(struct super_block *sb,
+                                struct nfs_parsed_mount_data *parsed,
+                                struct nfs_clone_mount *cloned)
+{
+       char *uniq = NULL;
+       int ulen = 0;
+
+       if (parsed && parsed->fscache_uniq) {
+               uniq = parsed->fscache_uniq;
+               ulen = strlen(parsed->fscache_uniq);
+       } else if (cloned) {
+               struct nfs_server *mnt_s = NFS_SB(cloned->sb);
+               if (mnt_s->fscache_key) {
+                       uniq = mnt_s->fscache_key->key.uniquifier;
+                       ulen = mnt_s->fscache_key->key.uniq_len;
+               };
+       }
+
+       nfs_fscache_get_super_cookie(sb, uniq, ulen);
+}
+#else
+static void nfs_get_cache_cookie(struct super_block *sb,
+                                struct nfs_parsed_mount_data *parsed,
+                                struct nfs_clone_mount *cloned)
+{
+}
+#endif
+
 static int nfs_bdi_register(struct nfs_server *server)
 {
        return bdi_register_dev(&server->backing_dev_info, server->s_dev);
 }
 
-static struct dentry *nfs_fs_mount(struct file_system_type *fs_type,
-       int flags, const char *dev_name, void *raw_data)
+static int nfs_set_sb_security(struct super_block *s, struct dentry *mntroot,
+                              struct nfs_mount_info *mount_info)
+{
+       return security_sb_set_mnt_opts(s, &mount_info->parsed->lsm_opts);
+}
+
+static int nfs_clone_sb_security(struct super_block *s, struct dentry *mntroot,
+                                struct nfs_mount_info *mount_info)
+{
+       /* clone any lsm security options from the parent to the new sb */
+       security_sb_clone_mnt_opts(mount_info->cloned->sb, s);
+       if (mntroot->d_inode->i_op != NFS_SB(s)->nfs_client->rpc_ops->dir_inode_ops)
+               return -ESTALE;
+       return 0;
+}
+
+static struct dentry *nfs_fs_mount_common(struct file_system_type *fs_type,
+                                         struct nfs_server *server,
+                                         int flags, const char *dev_name,
+                                         struct nfs_mount_info *mount_info)
 {
-       struct nfs_server *server = NULL;
        struct super_block *s;
-       struct nfs_parsed_mount_data *data;
-       struct nfs_fh *mntfh;
        struct dentry *mntroot = ERR_PTR(-ENOMEM);
        int (*compare_super)(struct super_block *, void *) = nfs_compare_super;
        struct nfs_sb_mountdata sb_mntdata = {
                .mntflags = flags,
+               .server = server,
        };
        int error;
 
-       data = nfs_alloc_parsed_mount_data(NFS_DEFAULT_VERSION);
-       mntfh = nfs_alloc_fhandle();
-       if (data == NULL || mntfh == NULL)
-               goto out;
-
-       /* Validate the mount data */
-       error = nfs_validate_mount_data(raw_data, data, mntfh, dev_name);
-       if (error < 0) {
-               mntroot = ERR_PTR(error);
-               goto out;
-       }
-
-#ifdef CONFIG_NFS_V4
-       if (data->version == 4) {
-               mntroot = nfs4_try_mount(flags, dev_name, data);
-               goto out;
-       }
-#endif /* CONFIG_NFS_V4 */
-
-       /* Get a volume representation */
-       server = nfs_create_server(data, mntfh);
-       if (IS_ERR(server)) {
-               mntroot = ERR_CAST(server);
-               goto out;
-       }
-       sb_mntdata.server = server;
-
        if (server->flags & NFS_MOUNT_UNSHARED)
                compare_super = NULL;
 
@@ -2351,23 +2437,21 @@ static struct dentry *nfs_fs_mount(struct file_system_type *fs_type,
 
        if (!s->s_root) {
                /* initial superblock/root creation */
-               nfs_fill_super(s, data);
-               nfs_fscache_get_super_cookie(s, data->fscache_uniq, NULL);
+               mount_info->fill_super(s, mount_info);
+               nfs_get_cache_cookie(s, mount_info->parsed, mount_info->cloned);
        }
 
-       mntroot = nfs_get_root(s, mntfh, dev_name);
+       mntroot = nfs_get_root(s, mount_info->mntfh, dev_name);
        if (IS_ERR(mntroot))
                goto error_splat_super;
 
-       error = security_sb_set_mnt_opts(s, &data->lsm_opts);
+       error = mount_info->set_security(s, mntroot, mount_info);
        if (error)
                goto error_splat_root;
 
        s->s_flags |= MS_ACTIVE;
 
 out:
-       nfs_free_parsed_mount_data(data);
-       nfs_free_fhandle(mntfh);
        return mntroot;
 
 out_err_nosb:
@@ -2385,6 +2469,43 @@ error_splat_bdi:
        goto out;
 }
 
+static struct dentry *nfs_fs_mount(struct file_system_type *fs_type,
+       int flags, const char *dev_name, void *raw_data)
+{
+       struct nfs_mount_info mount_info = {
+               .fill_super = nfs_fill_super,
+               .set_security = nfs_set_sb_security,
+       };
+       struct dentry *mntroot = ERR_PTR(-ENOMEM);
+       int error;
+
+       mount_info.parsed = nfs_alloc_parsed_mount_data();
+       mount_info.mntfh = nfs_alloc_fhandle();
+       if (mount_info.parsed == NULL || mount_info.mntfh == NULL)
+               goto out;
+
+       /* Validate the mount data */
+       error = nfs_validate_mount_data(fs_type, raw_data, mount_info.parsed, mount_info.mntfh, dev_name);
+       if (error == NFS_TEXT_DATA)
+               error = nfs_validate_text_mount_data(raw_data, mount_info.parsed, dev_name);
+       if (error < 0) {
+               mntroot = ERR_PTR(error);
+               goto out;
+       }
+
+#ifdef CONFIG_NFS_V4
+       if (mount_info.parsed->version == 4)
+               mntroot = nfs4_try_mount(flags, dev_name, &mount_info);
+       else
+#endif /* CONFIG_NFS_V4 */
+               mntroot = nfs_try_mount(flags, dev_name, &mount_info);
+
+out:
+       nfs_free_parsed_mount_data(mount_info.parsed);
+       nfs_free_fhandle(mount_info.mntfh);
+       return mntroot;
+}
+
 /*
  * Ensure that we unregister the bdi before kill_anon_super
  * releases the device name
@@ -2409,93 +2530,51 @@ static void nfs_kill_super(struct super_block *s)
 }
 
 /*
- * Clone an NFS2/3 server record on xdev traversal (FSID-change)
+ * Clone an NFS2/3/4 server record on xdev traversal (FSID-change)
  */
 static struct dentry *
-nfs_xdev_mount(struct file_system_type *fs_type, int flags,
-               const char *dev_name, void *raw_data)
+nfs_xdev_mount_common(struct file_system_type *fs_type, int flags,
+               const char *dev_name, struct nfs_mount_info *mount_info)
 {
-       struct nfs_clone_mount *data = raw_data;
-       struct super_block *s;
+       struct nfs_clone_mount *data = mount_info->cloned;
        struct nfs_server *server;
-       struct dentry *mntroot;
-       int (*compare_super)(struct super_block *, void *) = nfs_compare_super;
-       struct nfs_sb_mountdata sb_mntdata = {
-               .mntflags = flags,
-       };
+       struct dentry *mntroot = ERR_PTR(-ENOMEM);
        int error;
 
-       dprintk("--> nfs_xdev_mount()\n");
+       dprintk("--> nfs_xdev_mount_common()\n");
+
+       mount_info->mntfh = data->fh;
 
        /* create a new volume representation */
        server = nfs_clone_server(NFS_SB(data->sb), data->fh, data->fattr, data->authflavor);
        if (IS_ERR(server)) {
                error = PTR_ERR(server);
-               goto out_err_noserver;
-       }
-       sb_mntdata.server = server;
-
-       if (server->flags & NFS_MOUNT_UNSHARED)
-               compare_super = NULL;
-
-       /* -o noac implies -o sync */
-       if (server->flags & NFS_MOUNT_NOAC)
-               sb_mntdata.mntflags |= MS_SYNCHRONOUS;
-
-       /* Get a superblock - note that we may end up sharing one that already exists */
-       s = sget(&nfs_fs_type, compare_super, nfs_set_super, &sb_mntdata);
-       if (IS_ERR(s)) {
-               error = PTR_ERR(s);
-               goto out_err_nosb;
-       }
-
-       if (s->s_fs_info != server) {
-               nfs_free_server(server);
-               server = NULL;
-       } else {
-               error = nfs_bdi_register(server);
-               if (error)
-                       goto error_splat_bdi;
-       }
-
-       if (!s->s_root) {
-               /* initial superblock/root creation */
-               nfs_clone_super(s, data->sb);
-               nfs_fscache_get_super_cookie(s, NULL, data);
-       }
-
-       mntroot = nfs_get_root(s, data->fh, dev_name);
-       if (IS_ERR(mntroot)) {
-               error = PTR_ERR(mntroot);
-               goto error_splat_super;
-       }
-       if (mntroot->d_inode->i_op != NFS_SB(s)->nfs_client->rpc_ops->dir_inode_ops) {
-               dput(mntroot);
-               error = -ESTALE;
-               goto error_splat_super;
+               goto out_err;
        }
 
-       s->s_flags |= MS_ACTIVE;
-
-       /* clone any lsm security options from the parent to the new sb */
-       security_sb_clone_mnt_opts(data->sb, s);
-
-       dprintk("<-- nfs_xdev_mount() = 0\n");
+       mntroot = nfs_fs_mount_common(fs_type, server, flags, dev_name, mount_info);
+       dprintk("<-- nfs_xdev_mount_common() = 0\n");
+out:
        return mntroot;
 
-out_err_nosb:
-       nfs_free_server(server);
-out_err_noserver:
-       dprintk("<-- nfs_xdev_mount() = %d [error]\n", error);
-       return ERR_PTR(error);
+out_err:
+       dprintk("<-- nfs_xdev_mount_common() = %d [error]\n", error);
+       goto out;
+}
 
-error_splat_super:
-       if (server && !s->s_root)
-               bdi_unregister(&server->backing_dev_info);
-error_splat_bdi:
-       deactivate_locked_super(s);
-       dprintk("<-- nfs_xdev_mount() = %d [splat]\n", error);
-       return ERR_PTR(error);
+/*
+ * Clone an NFS2/3 server record on xdev traversal (FSID-change)
+ */
+static struct dentry *
+nfs_xdev_mount(struct file_system_type *fs_type, int flags,
+               const char *dev_name, void *raw_data)
+{
+       struct nfs_mount_info mount_info = {
+               .fill_super = nfs_clone_super,
+               .set_security = nfs_clone_sb_security,
+               .cloned   = raw_data,
+       };
+       return nfs_xdev_mount_common(&nfs_fs_type, flags, dev_name, &mount_info);
 }
 
 #ifdef CONFIG_NFS_V4
@@ -2504,8 +2583,9 @@ error_splat_bdi:
  * Finish setting up a cloned NFS4 superblock
  */
 static void nfs4_clone_super(struct super_block *sb,
-                           const struct super_block *old_sb)
+                            struct nfs_mount_info *mount_info)
 {
+       const struct super_block *old_sb = mount_info->cloned->sb;
        sb->s_blocksize_bits = old_sb->s_blocksize_bits;
        sb->s_blocksize = old_sb->s_blocksize;
        sb->s_maxbytes = old_sb->s_maxbytes;
@@ -2523,7 +2603,8 @@ static void nfs4_clone_super(struct super_block *sb,
 /*
  * Set up an NFS4 superblock
  */
-static void nfs4_fill_super(struct super_block *sb)
+static void nfs4_fill_super(struct super_block *sb,
+                           struct nfs_mount_info *mount_info)
 {
        sb->s_time_gran = 1;
        sb->s_op = &nfs4_sops;
@@ -2542,37 +2623,6 @@ static void nfs4_validate_mount_flags(struct nfs_parsed_mount_data *args)
                         NFS_MOUNT_LOCAL_FLOCK|NFS_MOUNT_LOCAL_FCNTL);
 }
 
-static int nfs4_validate_text_mount_data(void *options,
-                                        struct nfs_parsed_mount_data *args,
-                                        const char *dev_name)
-{
-       struct sockaddr *sap = (struct sockaddr *)&args->nfs_server.address;
-
-       nfs_set_port(sap, &args->nfs_server.port, NFS_PORT);
-
-       nfs_validate_transport_protocol(args);
-
-       nfs4_validate_mount_flags(args);
-
-       if (args->version != 4) {
-               dfprintk(MOUNT,
-                        "NFS4: Illegal mount version\n");
-               return -EINVAL;
-       }
-
-       if (args->auth_flavor_len > 1) {
-               dfprintk(MOUNT,
-                        "NFS4: Too many RPC auth flavours specified\n");
-               return -EINVAL;
-       }
-
-       return nfs_parse_devname(dev_name,
-                                  &args->nfs_server.hostname,
-                                  NFS4_MAXNAMLEN,
-                                  &args->nfs_server.export_path,
-                                  NFS4_MAXPATHLEN);
-}
-
 /*
  * Validate NFSv4 mount options
  */
@@ -2643,13 +2693,7 @@ static int nfs4_validate_mount_data(void *options,
 
                break;
        default:
-               if (nfs_parse_mount_options((char *)options, args) == 0)
-                       return -EINVAL;
-
-               if (!nfs_verify_server_address(sap))
-                       return -EINVAL;
-
-               return nfs4_validate_text_mount_data(options, args, dev_name);
+               return NFS_TEXT_DATA;
        }
 
        return 0;
@@ -2673,91 +2717,26 @@ out_no_address:
  */
 static struct dentry *
 nfs4_remote_mount(struct file_system_type *fs_type, int flags,
-                 const char *dev_name, void *raw_data)
+                 const char *dev_name, void *info)
 {
-       struct nfs_parsed_mount_data *data = raw_data;
-       struct super_block *s;
+       struct nfs_mount_info *mount_info = info;
        struct nfs_server *server;
-       struct nfs_fh *mntfh;
-       struct dentry *mntroot;
-       int (*compare_super)(struct super_block *, void *) = nfs_compare_super;
-       struct nfs_sb_mountdata sb_mntdata = {
-               .mntflags = flags,
-       };
-       int error = -ENOMEM;
+       struct dentry *mntroot = ERR_PTR(-ENOMEM);
 
-       mntfh = nfs_alloc_fhandle();
-       if (data == NULL || mntfh == NULL)
-               goto out;
+       mount_info->fill_super = nfs4_fill_super;
+       mount_info->set_security = nfs_set_sb_security;
 
        /* Get a volume representation */
-       server = nfs4_create_server(data, mntfh);
+       server = nfs4_create_server(mount_info->parsed, mount_info->mntfh);
        if (IS_ERR(server)) {
-               error = PTR_ERR(server);
+               mntroot = ERR_CAST(server);
                goto out;
        }
-       sb_mntdata.server = server;
 
-       if (server->flags & NFS4_MOUNT_UNSHARED)
-               compare_super = NULL;
-
-       /* -o noac implies -o sync */
-       if (server->flags & NFS_MOUNT_NOAC)
-               sb_mntdata.mntflags |= MS_SYNCHRONOUS;
-
-       /* Get a superblock - note that we may end up sharing one that already exists */
-       s = sget(&nfs4_fs_type, compare_super, nfs_set_super, &sb_mntdata);
-       if (IS_ERR(s)) {
-               error = PTR_ERR(s);
-               goto out_free;
-       }
-
-       if (s->s_fs_info != server) {
-               nfs_free_server(server);
-               server = NULL;
-       } else {
-               error = nfs_bdi_register(server);
-               if (error)
-                       goto error_splat_bdi;
-       }
-
-       if (!s->s_root) {
-               /* initial superblock/root creation */
-               nfs4_fill_super(s);
-               nfs_fscache_get_super_cookie(s, data->fscache_uniq, NULL);
-       }
-
-       mntroot = nfs4_get_root(s, mntfh, dev_name);
-       if (IS_ERR(mntroot)) {
-               error = PTR_ERR(mntroot);
-               goto error_splat_super;
-       }
-
-       error = security_sb_set_mnt_opts(s, &data->lsm_opts);
-       if (error)
-               goto error_splat_root;
-
-       s->s_flags |= MS_ACTIVE;
-
-       nfs_free_fhandle(mntfh);
-       return mntroot;
+       mntroot = nfs_fs_mount_common(fs_type, server, flags, dev_name, mount_info);
 
 out:
-       nfs_free_fhandle(mntfh);
-       return ERR_PTR(error);
-
-out_free:
-       nfs_free_server(server);
-       goto out;
-
-error_splat_root:
-       dput(mntroot);
-error_splat_super:
-       if (server && !s->s_root)
-               bdi_unregister(&server->backing_dev_info);
-error_splat_bdi:
-       deactivate_locked_super(s);
-       goto out;
+       return mntroot;
 }
 
 static struct vfsmount *nfs_do_root_mount(struct file_system_type *fs_type,
@@ -2869,17 +2848,18 @@ static struct dentry *nfs_follow_remote_path(struct vfsmount *root_mnt,
 }
 
 static struct dentry *nfs4_try_mount(int flags, const char *dev_name,
-                        struct nfs_parsed_mount_data *data)
+                        struct nfs_mount_info *mount_info)
 {
        char *export_path;
        struct vfsmount *root_mnt;
        struct dentry *res;
+       struct nfs_parsed_mount_data *data = mount_info->parsed;
 
        dfprintk(MOUNT, "--> nfs4_try_mount()\n");
 
        export_path = data->nfs_server.export_path;
        data->nfs_server.export_path = "/";
-       root_mnt = nfs_do_root_mount(&nfs4_remote_fs_type, flags, data,
+       root_mnt = nfs_do_root_mount(&nfs4_remote_fs_type, flags, mount_info,
                        data->nfs_server.hostname);
        data->nfs_server.export_path = export_path;
 
@@ -2891,38 +2871,6 @@ static struct dentry *nfs4_try_mount(int flags, const char *dev_name,
        return res;
 }
 
-/*
- * Get the superblock for an NFS4 mountpoint
- */
-static struct dentry *nfs4_mount(struct file_system_type *fs_type,
-       int flags, const char *dev_name, void *raw_data)
-{
-       struct nfs_parsed_mount_data *data;
-       int error = -ENOMEM;
-       struct dentry *res = ERR_PTR(-ENOMEM);
-
-       data = nfs_alloc_parsed_mount_data(4);
-       if (data == NULL)
-               goto out;
-
-       /* Validate the mount data */
-       error = nfs4_validate_mount_data(raw_data, data, dev_name);
-       if (error < 0) {
-               res = ERR_PTR(error);
-               goto out;
-       }
-
-       res = nfs4_try_mount(flags, dev_name, data);
-       if (IS_ERR(res))
-               error = PTR_ERR(res);
-
-out:
-       nfs_free_parsed_mount_data(data);
-       dprintk("<-- nfs4_mount() = %d%s\n", error,
-                       error != 0 ? " [error]" : "");
-       return res;
-}
-
 static void nfs4_kill_super(struct super_block *sb)
 {
        struct nfs_server *server = NFS_SB(sb);
@@ -2942,181 +2890,43 @@ static struct dentry *
 nfs4_xdev_mount(struct file_system_type *fs_type, int flags,
                 const char *dev_name, void *raw_data)
 {
-       struct nfs_clone_mount *data = raw_data;
-       struct super_block *s;
-       struct nfs_server *server;
-       struct dentry *mntroot;
-       int (*compare_super)(struct super_block *, void *) = nfs_compare_super;
-       struct nfs_sb_mountdata sb_mntdata = {
-               .mntflags = flags,
+       struct nfs_mount_info mount_info = {
+               .fill_super = nfs4_clone_super,
+               .set_security = nfs_clone_sb_security,
+               .cloned = raw_data,
        };
-       int error;
-
-       dprintk("--> nfs4_xdev_mount()\n");
-
-       /* create a new volume representation */
-       server = nfs_clone_server(NFS_SB(data->sb), data->fh, data->fattr, data->authflavor);
-       if (IS_ERR(server)) {
-               error = PTR_ERR(server);
-               goto out_err_noserver;
-       }
-       sb_mntdata.server = server;
-
-       if (server->flags & NFS4_MOUNT_UNSHARED)
-               compare_super = NULL;
-
-       /* -o noac implies -o sync */
-       if (server->flags & NFS_MOUNT_NOAC)
-               sb_mntdata.mntflags |= MS_SYNCHRONOUS;
-
-       /* Get a superblock - note that we may end up sharing one that already exists */
-       s = sget(&nfs4_fs_type, compare_super, nfs_set_super, &sb_mntdata);
-       if (IS_ERR(s)) {
-               error = PTR_ERR(s);
-               goto out_err_nosb;
-       }
-
-       if (s->s_fs_info != server) {
-               nfs_free_server(server);
-               server = NULL;
-       } else {
-               error = nfs_bdi_register(server);
-               if (error)
-                       goto error_splat_bdi;
-       }
-
-       if (!s->s_root) {
-               /* initial superblock/root creation */
-               nfs4_clone_super(s, data->sb);
-               nfs_fscache_get_super_cookie(s, NULL, data);
-       }
-
-       mntroot = nfs4_get_root(s, data->fh, dev_name);
-       if (IS_ERR(mntroot)) {
-               error = PTR_ERR(mntroot);
-               goto error_splat_super;
-       }
-       if (mntroot->d_inode->i_op != NFS_SB(s)->nfs_client->rpc_ops->dir_inode_ops) {
-               dput(mntroot);
-               error = -ESTALE;
-               goto error_splat_super;
-       }
-
-       s->s_flags |= MS_ACTIVE;
-
-       security_sb_clone_mnt_opts(data->sb, s);
-
-       dprintk("<-- nfs4_xdev_mount() = 0\n");
-       return mntroot;
-
-out_err_nosb:
-       nfs_free_server(server);
-out_err_noserver:
-       dprintk("<-- nfs4_xdev_mount() = %d [error]\n", error);
-       return ERR_PTR(error);
-
-error_splat_super:
-       if (server && !s->s_root)
-               bdi_unregister(&server->backing_dev_info);
-error_splat_bdi:
-       deactivate_locked_super(s);
-       dprintk("<-- nfs4_xdev_mount() = %d [splat]\n", error);
-       return ERR_PTR(error);
+       return nfs_xdev_mount_common(&nfs4_fs_type, flags, dev_name, &mount_info);
 }
 
 static struct dentry *
 nfs4_remote_referral_mount(struct file_system_type *fs_type, int flags,
                           const char *dev_name, void *raw_data)
 {
-       struct nfs_clone_mount *data = raw_data;
-       struct super_block *s;
-       struct nfs_server *server;
-       struct dentry *mntroot;
-       struct nfs_fh *mntfh;
-       int (*compare_super)(struct super_block *, void *) = nfs_compare_super;
-       struct nfs_sb_mountdata sb_mntdata = {
-               .mntflags = flags,
+       struct nfs_mount_info mount_info = {
+               .fill_super = nfs4_fill_super,
+               .set_security = nfs_clone_sb_security,
+               .cloned = raw_data,
        };
-       int error = -ENOMEM;
+       struct nfs_server *server;
+       struct dentry *mntroot = ERR_PTR(-ENOMEM);
 
        dprintk("--> nfs4_referral_get_sb()\n");
 
-       mntfh = nfs_alloc_fhandle();
-       if (mntfh == NULL)
-               goto out_err_nofh;
+       mount_info.mntfh = nfs_alloc_fhandle();
+       if (mount_info.cloned == NULL || mount_info.mntfh == NULL)
+               goto out;
 
        /* create a new volume representation */
-       server = nfs4_create_referral_server(data, mntfh);
+       server = nfs4_create_referral_server(mount_info.cloned, mount_info.mntfh);
        if (IS_ERR(server)) {
-               error = PTR_ERR(server);
-               goto out_err_noserver;
-       }
-       sb_mntdata.server = server;
-
-       if (server->flags & NFS4_MOUNT_UNSHARED)
-               compare_super = NULL;
-
-       /* -o noac implies -o sync */
-       if (server->flags & NFS_MOUNT_NOAC)
-               sb_mntdata.mntflags |= MS_SYNCHRONOUS;
-
-       /* Get a superblock - note that we may end up sharing one that already exists */
-       s = sget(&nfs4_fs_type, compare_super, nfs_set_super, &sb_mntdata);
-       if (IS_ERR(s)) {
-               error = PTR_ERR(s);
-               goto out_err_nosb;
-       }
-
-       if (s->s_fs_info != server) {
-               nfs_free_server(server);
-               server = NULL;
-       } else {
-               error = nfs_bdi_register(server);
-               if (error)
-                       goto error_splat_bdi;
-       }
-
-       if (!s->s_root) {
-               /* initial superblock/root creation */
-               nfs4_fill_super(s);
-               nfs_fscache_get_super_cookie(s, NULL, data);
-       }
-
-       mntroot = nfs4_get_root(s, mntfh, dev_name);
-       if (IS_ERR(mntroot)) {
-               error = PTR_ERR(mntroot);
-               goto error_splat_super;
-       }
-       if (mntroot->d_inode->i_op != NFS_SB(s)->nfs_client->rpc_ops->dir_inode_ops) {
-               dput(mntroot);
-               error = -ESTALE;
-               goto error_splat_super;
+               mntroot = ERR_CAST(server);
+               goto out;
        }
 
-       s->s_flags |= MS_ACTIVE;
-
-       security_sb_clone_mnt_opts(data->sb, s);
-
-       nfs_free_fhandle(mntfh);
-       dprintk("<-- nfs4_referral_get_sb() = 0\n");
+       mntroot = nfs_fs_mount_common(&nfs4_fs_type, server, flags, dev_name, &mount_info);
+out:
+       nfs_free_fhandle(mount_info.mntfh);
        return mntroot;
-
-out_err_nosb:
-       nfs_free_server(server);
-out_err_noserver:
-       nfs_free_fhandle(mntfh);
-out_err_nofh:
-       dprintk("<-- nfs4_referral_get_sb() = %d [error]\n", error);
-       return ERR_PTR(error);
-
-error_splat_super:
-       if (server && !s->s_root)
-               bdi_unregister(&server->backing_dev_info);
-error_splat_bdi:
-       deactivate_locked_super(s);
-       nfs_free_fhandle(mntfh);
-       dprintk("<-- nfs4_referral_get_sb() = %d [splat]\n", error);
-       return ERR_PTR(error);
 }
 
 /*
index c07462320f6b5c41c09ba2ff054e75048951691f..e6fe3d69d14cbe0a5b75fc2cc5905c875f4c0181 100644 (file)
 /*
  * Local function declarations
  */
-static void nfs_pageio_init_write(struct nfs_pageio_descriptor *desc,
-                                 struct inode *inode, int ioflags);
 static void nfs_redirty_request(struct nfs_page *req);
-static const struct rpc_call_ops nfs_write_partial_ops;
-static const struct rpc_call_ops nfs_write_full_ops;
+static const struct rpc_call_ops nfs_write_common_ops;
 static const struct rpc_call_ops nfs_commit_ops;
+static const struct nfs_pgio_completion_ops nfs_async_write_completion_ops;
+static const struct nfs_commit_completion_ops nfs_commit_completion_ops;
 
 static struct kmem_cache *nfs_wdata_cachep;
 static mempool_t *nfs_wdata_mempool;
+static struct kmem_cache *nfs_cdata_cachep;
 static mempool_t *nfs_commit_mempool;
 
-struct nfs_write_data *nfs_commitdata_alloc(void)
+struct nfs_commit_data *nfs_commitdata_alloc(void)
 {
-       struct nfs_write_data *p = mempool_alloc(nfs_commit_mempool, GFP_NOFS);
+       struct nfs_commit_data *p = mempool_alloc(nfs_commit_mempool, GFP_NOFS);
 
        if (p) {
                memset(p, 0, sizeof(*p));
@@ -62,46 +62,73 @@ struct nfs_write_data *nfs_commitdata_alloc(void)
 }
 EXPORT_SYMBOL_GPL(nfs_commitdata_alloc);
 
-void nfs_commit_free(struct nfs_write_data *p)
+void nfs_commit_free(struct nfs_commit_data *p)
 {
-       if (p && (p->pagevec != &p->page_array[0]))
-               kfree(p->pagevec);
        mempool_free(p, nfs_commit_mempool);
 }
 EXPORT_SYMBOL_GPL(nfs_commit_free);
 
-struct nfs_write_data *nfs_writedata_alloc(unsigned int pagecount)
+struct nfs_write_header *nfs_writehdr_alloc(void)
 {
-       struct nfs_write_data *p = mempool_alloc(nfs_wdata_mempool, GFP_NOFS);
+       struct nfs_write_header *p = mempool_alloc(nfs_wdata_mempool, GFP_NOFS);
 
        if (p) {
+               struct nfs_pgio_header *hdr = &p->header;
+
                memset(p, 0, sizeof(*p));
-               INIT_LIST_HEAD(&p->pages);
-               p->npages = pagecount;
-               if (pagecount <= ARRAY_SIZE(p->page_array))
-                       p->pagevec = p->page_array;
-               else {
-                       p->pagevec = kcalloc(pagecount, sizeof(struct page *), GFP_NOFS);
-                       if (!p->pagevec) {
-                               mempool_free(p, nfs_wdata_mempool);
-                               p = NULL;
-                       }
-               }
+               INIT_LIST_HEAD(&hdr->pages);
+               INIT_LIST_HEAD(&hdr->rpc_list);
+               spin_lock_init(&hdr->lock);
+               atomic_set(&hdr->refcnt, 0);
        }
        return p;
 }
 
-void nfs_writedata_free(struct nfs_write_data *p)
+static struct nfs_write_data *nfs_writedata_alloc(struct nfs_pgio_header *hdr,
+                                                 unsigned int pagecount)
+{
+       struct nfs_write_data *data, *prealloc;
+
+       prealloc = &container_of(hdr, struct nfs_write_header, header)->rpc_data;
+       if (prealloc->header == NULL)
+               data = prealloc;
+       else
+               data = kzalloc(sizeof(*data), GFP_KERNEL);
+       if (!data)
+               goto out;
+
+       if (nfs_pgarray_set(&data->pages, pagecount)) {
+               data->header = hdr;
+               atomic_inc(&hdr->refcnt);
+       } else {
+               if (data != prealloc)
+                       kfree(data);
+               data = NULL;
+       }
+out:
+       return data;
+}
+
+void nfs_writehdr_free(struct nfs_pgio_header *hdr)
 {
-       if (p && (p->pagevec != &p->page_array[0]))
-               kfree(p->pagevec);
-       mempool_free(p, nfs_wdata_mempool);
+       struct nfs_write_header *whdr = container_of(hdr, struct nfs_write_header, header);
+       mempool_free(whdr, nfs_wdata_mempool);
 }
 
 void nfs_writedata_release(struct nfs_write_data *wdata)
 {
+       struct nfs_pgio_header *hdr = wdata->header;
+       struct nfs_write_header *write_header = container_of(hdr, struct nfs_write_header, header);
+
        put_nfs_open_context(wdata->args.context);
-       nfs_writedata_free(wdata);
+       if (wdata->pages.pagevec != wdata->pages.page_array)
+               kfree(wdata->pages.pagevec);
+       if (wdata != &write_header->rpc_data)
+               kfree(wdata);
+       else
+               wdata->header = NULL;
+       if (atomic_dec_and_test(&hdr->refcnt))
+               hdr->completion_ops->completion(hdr);
 }
 
 static void nfs_context_set_write_error(struct nfs_open_context *ctx, int error)
@@ -203,7 +230,6 @@ static int nfs_set_page_writeback(struct page *page)
                struct inode *inode = page->mapping->host;
                struct nfs_server *nfss = NFS_SERVER(inode);
 
-               page_cache_get(page);
                if (atomic_long_inc_return(&nfss->writeback) >
                                NFS_CONGESTION_ON_THRESH) {
                        set_bdi_congested(&nfss->backing_dev_info,
@@ -219,7 +245,6 @@ static void nfs_end_page_writeback(struct page *page)
        struct nfs_server *nfss = NFS_SERVER(inode);
 
        end_page_writeback(page);
-       page_cache_release(page);
        if (atomic_long_dec_return(&nfss->writeback) < NFS_CONGESTION_OFF_THRESH)
                clear_bdi_congested(&nfss->backing_dev_info, BLK_RW_ASYNC);
 }
@@ -235,10 +260,10 @@ static struct nfs_page *nfs_find_and_lock_request(struct page *page, bool nonblo
                req = nfs_page_find_request_locked(page);
                if (req == NULL)
                        break;
-               if (nfs_lock_request_dontget(req))
+               if (nfs_lock_request(req))
                        break;
                /* Note: If we hold the page lock, as is the case in nfs_writepage,
-                *       then the call to nfs_lock_request_dontget() will always
+                *       then the call to nfs_lock_request() will always
                 *       succeed provided that someone hasn't already marked the
                 *       request as dirty (in which case we don't care).
                 */
@@ -310,7 +335,8 @@ static int nfs_writepage_locked(struct page *page, struct writeback_control *wbc
        struct nfs_pageio_descriptor pgio;
        int err;
 
-       nfs_pageio_init_write(&pgio, page->mapping->host, wb_priority(wbc));
+       nfs_pageio_init_write(&pgio, page->mapping->host, wb_priority(wbc),
+                             &nfs_async_write_completion_ops);
        err = nfs_do_writepage(page, wbc, &pgio);
        nfs_pageio_complete(&pgio);
        if (err < 0)
@@ -353,7 +379,8 @@ int nfs_writepages(struct address_space *mapping, struct writeback_control *wbc)
 
        nfs_inc_stats(inode, NFSIOS_VFSWRITEPAGES);
 
-       nfs_pageio_init_write(&pgio, inode, wb_priority(wbc));
+       nfs_pageio_init_write(&pgio, inode, wb_priority(wbc),
+                             &nfs_async_write_completion_ops);
        err = write_cache_pages(mapping, wbc, nfs_writepages_callback, &pgio);
        nfs_pageio_complete(&pgio);
 
@@ -379,7 +406,7 @@ static void nfs_inode_add_request(struct inode *inode, struct nfs_page *req)
        struct nfs_inode *nfsi = NFS_I(inode);
 
        /* Lock the request! */
-       nfs_lock_request_dontget(req);
+       nfs_lock_request(req);
 
        spin_lock(&inode->i_lock);
        if (!nfsi->npages && nfs_have_delegation(inode, FMODE_WRITE))
@@ -421,65 +448,88 @@ nfs_mark_request_dirty(struct nfs_page *req)
 /**
  * nfs_request_add_commit_list - add request to a commit list
  * @req: pointer to a struct nfs_page
- * @head: commit list head
+ * @dst: commit list head
+ * @cinfo: holds list lock and accounting info
  *
- * This sets the PG_CLEAN bit, updates the inode global count of
+ * This sets the PG_CLEAN bit, updates the cinfo count of
  * number of outstanding requests requiring a commit as well as
  * the MM page stats.
  *
- * The caller must _not_ hold the inode->i_lock, but must be
+ * The caller must _not_ hold the cinfo->lock, but must be
  * holding the nfs_page lock.
  */
 void
-nfs_request_add_commit_list(struct nfs_page *req, struct list_head *head)
+nfs_request_add_commit_list(struct nfs_page *req, struct list_head *dst,
+                           struct nfs_commit_info *cinfo)
 {
-       struct inode *inode = req->wb_context->dentry->d_inode;
-
        set_bit(PG_CLEAN, &(req)->wb_flags);
-       spin_lock(&inode->i_lock);
-       nfs_list_add_request(req, head);
-       NFS_I(inode)->ncommit++;
-       spin_unlock(&inode->i_lock);
-       inc_zone_page_state(req->wb_page, NR_UNSTABLE_NFS);
-       inc_bdi_stat(req->wb_page->mapping->backing_dev_info, BDI_RECLAIMABLE);
-       __mark_inode_dirty(inode, I_DIRTY_DATASYNC);
+       spin_lock(cinfo->lock);
+       nfs_list_add_request(req, dst);
+       cinfo->mds->ncommit++;
+       spin_unlock(cinfo->lock);
+       if (!cinfo->dreq) {
+               inc_zone_page_state(req->wb_page, NR_UNSTABLE_NFS);
+               inc_bdi_stat(req->wb_page->mapping->backing_dev_info,
+                            BDI_RECLAIMABLE);
+               __mark_inode_dirty(req->wb_context->dentry->d_inode,
+                                  I_DIRTY_DATASYNC);
+       }
 }
 EXPORT_SYMBOL_GPL(nfs_request_add_commit_list);
 
 /**
  * nfs_request_remove_commit_list - Remove request from a commit list
  * @req: pointer to a nfs_page
+ * @cinfo: holds list lock and accounting info
  *
- * This clears the PG_CLEAN bit, and updates the inode global count of
+ * This clears the PG_CLEAN bit, and updates the cinfo's count of
  * number of outstanding requests requiring a commit
  * It does not update the MM page stats.
  *
- * The caller _must_ hold the inode->i_lock and the nfs_page lock.
+ * The caller _must_ hold the cinfo->lock and the nfs_page lock.
  */
 void
-nfs_request_remove_commit_list(struct nfs_page *req)
+nfs_request_remove_commit_list(struct nfs_page *req,
+                              struct nfs_commit_info *cinfo)
 {
-       struct inode *inode = req->wb_context->dentry->d_inode;
-
        if (!test_and_clear_bit(PG_CLEAN, &(req)->wb_flags))
                return;
        nfs_list_remove_request(req);
-       NFS_I(inode)->ncommit--;
+       cinfo->mds->ncommit--;
 }
 EXPORT_SYMBOL_GPL(nfs_request_remove_commit_list);
 
+static void nfs_init_cinfo_from_inode(struct nfs_commit_info *cinfo,
+                                     struct inode *inode)
+{
+       cinfo->lock = &inode->i_lock;
+       cinfo->mds = &NFS_I(inode)->commit_info;
+       cinfo->ds = pnfs_get_ds_info(inode);
+       cinfo->dreq = NULL;
+       cinfo->completion_ops = &nfs_commit_completion_ops;
+}
+
+void nfs_init_cinfo(struct nfs_commit_info *cinfo,
+                   struct inode *inode,
+                   struct nfs_direct_req *dreq)
+{
+       if (dreq)
+               nfs_init_cinfo_from_dreq(cinfo, dreq);
+       else
+               nfs_init_cinfo_from_inode(cinfo, inode);
+}
+EXPORT_SYMBOL_GPL(nfs_init_cinfo);
 
 /*
  * Add a request to the inode's commit list.
  */
-static void
-nfs_mark_request_commit(struct nfs_page *req, struct pnfs_layout_segment *lseg)
+void
+nfs_mark_request_commit(struct nfs_page *req, struct pnfs_layout_segment *lseg,
+                       struct nfs_commit_info *cinfo)
 {
-       struct inode *inode = req->wb_context->dentry->d_inode;
-
-       if (pnfs_mark_request_commit(req, lseg))
+       if (pnfs_mark_request_commit(req, lseg, cinfo))
                return;
-       nfs_request_add_commit_list(req, &NFS_I(inode)->commit_list);
+       nfs_request_add_commit_list(req, &cinfo->mds->list, cinfo);
 }
 
 static void
@@ -494,11 +544,13 @@ nfs_clear_request_commit(struct nfs_page *req)
 {
        if (test_bit(PG_CLEAN, &req->wb_flags)) {
                struct inode *inode = req->wb_context->dentry->d_inode;
+               struct nfs_commit_info cinfo;
 
-               if (!pnfs_clear_request_commit(req)) {
-                       spin_lock(&inode->i_lock);
-                       nfs_request_remove_commit_list(req);
-                       spin_unlock(&inode->i_lock);
+               nfs_init_cinfo_from_inode(&cinfo, inode);
+               if (!pnfs_clear_request_commit(req, &cinfo)) {
+                       spin_lock(cinfo.lock);
+                       nfs_request_remove_commit_list(req, &cinfo);
+                       spin_unlock(cinfo.lock);
                }
                nfs_clear_page_commit(req->wb_page);
        }
@@ -508,28 +560,25 @@ static inline
 int nfs_write_need_commit(struct nfs_write_data *data)
 {
        if (data->verf.committed == NFS_DATA_SYNC)
-               return data->lseg == NULL;
-       else
-               return data->verf.committed != NFS_FILE_SYNC;
+               return data->header->lseg == NULL;
+       return data->verf.committed != NFS_FILE_SYNC;
 }
 
-static inline
-int nfs_reschedule_unstable_write(struct nfs_page *req,
-                                 struct nfs_write_data *data)
+#else
+static void nfs_init_cinfo_from_inode(struct nfs_commit_info *cinfo,
+                                     struct inode *inode)
 {
-       if (test_and_clear_bit(PG_NEED_COMMIT, &req->wb_flags)) {
-               nfs_mark_request_commit(req, data->lseg);
-               return 1;
-       }
-       if (test_and_clear_bit(PG_NEED_RESCHED, &req->wb_flags)) {
-               nfs_mark_request_dirty(req);
-               return 1;
-       }
-       return 0;
 }
-#else
-static void
-nfs_mark_request_commit(struct nfs_page *req, struct pnfs_layout_segment *lseg)
+
+void nfs_init_cinfo(struct nfs_commit_info *cinfo,
+                   struct inode *inode,
+                   struct nfs_direct_req *dreq)
+{
+}
+
+void
+nfs_mark_request_commit(struct nfs_page *req, struct pnfs_layout_segment *lseg,
+                       struct nfs_commit_info *cinfo)
 {
 }
 
@@ -544,25 +593,57 @@ int nfs_write_need_commit(struct nfs_write_data *data)
        return 0;
 }
 
-static inline
-int nfs_reschedule_unstable_write(struct nfs_page *req,
-                                 struct nfs_write_data *data)
+#endif
+
+static void nfs_write_completion(struct nfs_pgio_header *hdr)
 {
-       return 0;
+       struct nfs_commit_info cinfo;
+       unsigned long bytes = 0;
+
+       if (test_bit(NFS_IOHDR_REDO, &hdr->flags))
+               goto out;
+       nfs_init_cinfo_from_inode(&cinfo, hdr->inode);
+       while (!list_empty(&hdr->pages)) {
+               struct nfs_page *req = nfs_list_entry(hdr->pages.next);
+
+               bytes += req->wb_bytes;
+               nfs_list_remove_request(req);
+               if (test_bit(NFS_IOHDR_ERROR, &hdr->flags) &&
+                   (hdr->good_bytes < bytes)) {
+                       nfs_set_pageerror(req->wb_page);
+                       nfs_context_set_write_error(req->wb_context, hdr->error);
+                       goto remove_req;
+               }
+               if (test_bit(NFS_IOHDR_NEED_RESCHED, &hdr->flags)) {
+                       nfs_mark_request_dirty(req);
+                       goto next;
+               }
+               if (test_bit(NFS_IOHDR_NEED_COMMIT, &hdr->flags)) {
+                       nfs_mark_request_commit(req, hdr->lseg, &cinfo);
+                       goto next;
+               }
+remove_req:
+               nfs_inode_remove_request(req);
+next:
+               nfs_unlock_request(req);
+               nfs_end_page_writeback(req->wb_page);
+               nfs_release_request(req);
+       }
+out:
+       hdr->release(hdr);
 }
-#endif
 
 #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4)
-static int
-nfs_need_commit(struct nfs_inode *nfsi)
+static unsigned long
+nfs_reqs_to_commit(struct nfs_commit_info *cinfo)
 {
-       return nfsi->ncommit > 0;
+       return cinfo->mds->ncommit;
 }
 
-/* i_lock held by caller */
-static int
-nfs_scan_commit_list(struct list_head *src, struct list_head *dst, int max,
-               spinlock_t *lock)
+/* cinfo->lock held by caller */
+int
+nfs_scan_commit_list(struct list_head *src, struct list_head *dst,
+                    struct nfs_commit_info *cinfo, int max)
 {
        struct nfs_page *req, *tmp;
        int ret = 0;
@@ -570,12 +651,13 @@ nfs_scan_commit_list(struct list_head *src, struct list_head *dst, int max,
        list_for_each_entry_safe(req, tmp, src, wb_list) {
                if (!nfs_lock_request(req))
                        continue;
-               if (cond_resched_lock(lock))
+               kref_get(&req->wb_kref);
+               if (cond_resched_lock(cinfo->lock))
                        list_safe_reset_next(req, tmp, wb_list);
-               nfs_request_remove_commit_list(req);
+               nfs_request_remove_commit_list(req, cinfo);
                nfs_list_add_request(req, dst);
                ret++;
-               if (ret == max)
+               if ((ret == max) && !cinfo->dreq)
                        break;
        }
        return ret;
@@ -584,37 +666,38 @@ nfs_scan_commit_list(struct list_head *src, struct list_head *dst, int max,
 /*
  * nfs_scan_commit - Scan an inode for commit requests
  * @inode: NFS inode to scan
- * @dst: destination list
+ * @dst: mds destination list
+ * @cinfo: mds and ds lists of reqs ready to commit
  *
  * Moves requests from the inode's 'commit' request list.
  * The requests are *not* checked to ensure that they form a contiguous set.
  */
-static int
-nfs_scan_commit(struct inode *inode, struct list_head *dst)
+int
+nfs_scan_commit(struct inode *inode, struct list_head *dst,
+               struct nfs_commit_info *cinfo)
 {
-       struct nfs_inode *nfsi = NFS_I(inode);
        int ret = 0;
 
-       spin_lock(&inode->i_lock);
-       if (nfsi->ncommit > 0) {
+       spin_lock(cinfo->lock);
+       if (cinfo->mds->ncommit > 0) {
                const int max = INT_MAX;
 
-               ret = nfs_scan_commit_list(&nfsi->commit_list, dst, max,
-                               &inode->i_lock);
-               ret += pnfs_scan_commit_lists(inode, max - ret,
-                               &inode->i_lock);
+               ret = nfs_scan_commit_list(&cinfo->mds->list, dst,
+                                          cinfo, max);
+               ret += pnfs_scan_commit_lists(inode, cinfo, max - ret);
        }
-       spin_unlock(&inode->i_lock);
+       spin_unlock(cinfo->lock);
        return ret;
 }
 
 #else
-static inline int nfs_need_commit(struct nfs_inode *nfsi)
+static unsigned long nfs_reqs_to_commit(struct nfs_commit_info *cinfo)
 {
        return 0;
 }
 
-static inline int nfs_scan_commit(struct inode *inode, struct list_head *dst)
+int nfs_scan_commit(struct inode *inode, struct list_head *dst,
+                   struct nfs_commit_info *cinfo)
 {
        return 0;
 }
@@ -659,7 +742,7 @@ static struct nfs_page *nfs_try_to_update_request(struct inode *inode,
                    || end < req->wb_offset)
                        goto out_flushme;
 
-               if (nfs_lock_request_dontget(req))
+               if (nfs_lock_request(req))
                        break;
 
                /* The request is locked, so wait and then retry */
@@ -729,7 +812,7 @@ static int nfs_writepage_setup(struct nfs_open_context *ctx, struct page *page,
        nfs_grow_file(page, offset, count);
        nfs_mark_uptodate(page, req->wb_pgbase, req->wb_bytes);
        nfs_mark_request_dirty(req);
-       nfs_unlock_request(req);
+       nfs_unlock_and_release_request(req);
        return 0;
 }
 
@@ -766,10 +849,14 @@ int nfs_flush_incompatible(struct file *file, struct page *page)
  * the PageUptodate() flag. In this case, we will need to turn off
  * write optimisations that depend on the page contents being correct.
  */
-static int nfs_write_pageuptodate(struct page *page, struct inode *inode)
+static bool nfs_write_pageuptodate(struct page *page, struct inode *inode)
 {
-       return PageUptodate(page) &&
-               !(NFS_I(inode)->cache_validity & (NFS_INO_REVAL_PAGECACHE|NFS_INO_INVALID_DATA));
+       if (nfs_have_delegated_attributes(inode))
+               goto out;
+       if (NFS_I(inode)->cache_validity & NFS_INO_REVAL_PAGECACHE)
+               return false;
+out:
+       return PageUptodate(page) != 0;
 }
 
 /*
@@ -815,17 +902,6 @@ int nfs_updatepage(struct file *file, struct page *page,
        return status;
 }
 
-static void nfs_writepage_release(struct nfs_page *req,
-                                 struct nfs_write_data *data)
-{
-       struct page *page = req->wb_page;
-
-       if (PageError(req->wb_page) || !nfs_reschedule_unstable_write(req, data))
-               nfs_inode_remove_request(req);
-       nfs_unlock_request(req);
-       nfs_end_page_writeback(page);
-}
-
 static int flush_task_priority(int how)
 {
        switch (how & (FLUSH_HIGHPRI|FLUSH_LOWPRI)) {
@@ -837,18 +913,18 @@ static int flush_task_priority(int how)
        return RPC_PRIORITY_NORMAL;
 }
 
-int nfs_initiate_write(struct nfs_write_data *data,
-                      struct rpc_clnt *clnt,
+int nfs_initiate_write(struct rpc_clnt *clnt,
+                      struct nfs_write_data *data,
                       const struct rpc_call_ops *call_ops,
-                      int how)
+                      int how, int flags)
 {
-       struct inode *inode = data->inode;
+       struct inode *inode = data->header->inode;
        int priority = flush_task_priority(how);
        struct rpc_task *task;
        struct rpc_message msg = {
                .rpc_argp = &data->args,
                .rpc_resp = &data->res,
-               .rpc_cred = data->cred,
+               .rpc_cred = data->header->cred,
        };
        struct rpc_task_setup task_setup_data = {
                .rpc_client = clnt,
@@ -857,7 +933,7 @@ int nfs_initiate_write(struct nfs_write_data *data,
                .callback_ops = call_ops,
                .callback_data = data,
                .workqueue = nfsiod_workqueue,
-               .flags = RPC_TASK_ASYNC,
+               .flags = RPC_TASK_ASYNC | flags,
                .priority = priority,
        };
        int ret = 0;
@@ -892,26 +968,21 @@ EXPORT_SYMBOL_GPL(nfs_initiate_write);
 /*
  * Set up the argument/result storage required for the RPC call.
  */
-static void nfs_write_rpcsetup(struct nfs_page *req,
-               struct nfs_write_data *data,
+static void nfs_write_rpcsetup(struct nfs_write_data *data,
                unsigned int count, unsigned int offset,
-               int how)
+               int how, struct nfs_commit_info *cinfo)
 {
-       struct inode *inode = req->wb_context->dentry->d_inode;
+       struct nfs_page *req = data->header->req;
 
        /* Set up the RPC argument and reply structs
         * NB: take care not to mess about with data->commit et al. */
 
-       data->req = req;
-       data->inode = inode = req->wb_context->dentry->d_inode;
-       data->cred = req->wb_context->cred;
-
-       data->args.fh     = NFS_FH(inode);
+       data->args.fh     = NFS_FH(data->header->inode);
        data->args.offset = req_offset(req) + offset;
        /* pnfs_set_layoutcommit needs this */
        data->mds_offset = data->args.offset;
        data->args.pgbase = req->wb_pgbase + offset;
-       data->args.pages  = data->pagevec;
+       data->args.pages  = data->pages.pagevec;
        data->args.count  = count;
        data->args.context = get_nfs_open_context(req->wb_context);
        data->args.lock_context = req->wb_lock_context;
@@ -920,7 +991,7 @@ static void nfs_write_rpcsetup(struct nfs_page *req,
        case 0:
                break;
        case FLUSH_COND_STABLE:
-               if (nfs_need_commit(NFS_I(inode)))
+               if (nfs_reqs_to_commit(cinfo))
                        break;
        default:
                data->args.stable = NFS_FILE_SYNC;
@@ -936,9 +1007,9 @@ static int nfs_do_write(struct nfs_write_data *data,
                const struct rpc_call_ops *call_ops,
                int how)
 {
-       struct inode *inode = data->args.context->dentry->d_inode;
+       struct inode *inode = data->header->inode;
 
-       return nfs_initiate_write(data, NFS_CLIENT(inode), call_ops, how);
+       return nfs_initiate_write(NFS_CLIENT(inode), data, call_ops, how, 0);
 }
 
 static int nfs_do_multiple_writes(struct list_head *head,
@@ -951,7 +1022,7 @@ static int nfs_do_multiple_writes(struct list_head *head,
        while (!list_empty(head)) {
                int ret2;
 
-               data = list_entry(head->next, struct nfs_write_data, list);
+               data = list_first_entry(head, struct nfs_write_data, list);
                list_del_init(&data->list);
                
                ret2 = nfs_do_write(data, call_ops, how);
@@ -967,31 +1038,60 @@ static int nfs_do_multiple_writes(struct list_head *head,
  */
 static void nfs_redirty_request(struct nfs_page *req)
 {
-       struct page *page = req->wb_page;
-
        nfs_mark_request_dirty(req);
        nfs_unlock_request(req);
-       nfs_end_page_writeback(page);
+       nfs_end_page_writeback(req->wb_page);
+       nfs_release_request(req);
+}
+
+static void nfs_async_write_error(struct list_head *head)
+{
+       struct nfs_page *req;
+
+       while (!list_empty(head)) {
+               req = nfs_list_entry(head->next);
+               nfs_list_remove_request(req);
+               nfs_redirty_request(req);
+       }
+}
+
+static const struct nfs_pgio_completion_ops nfs_async_write_completion_ops = {
+       .error_cleanup = nfs_async_write_error,
+       .completion = nfs_write_completion,
+};
+
+static void nfs_flush_error(struct nfs_pageio_descriptor *desc,
+               struct nfs_pgio_header *hdr)
+{
+       set_bit(NFS_IOHDR_REDO, &hdr->flags);
+       while (!list_empty(&hdr->rpc_list)) {
+               struct nfs_write_data *data = list_first_entry(&hdr->rpc_list,
+                               struct nfs_write_data, list);
+               list_del(&data->list);
+               nfs_writedata_release(data);
+       }
+       desc->pg_completion_ops->error_cleanup(&desc->pg_list);
 }
 
 /*
  * Generate multiple small requests to write out a single
  * contiguous dirty area on one page.
  */
-static int nfs_flush_multi(struct nfs_pageio_descriptor *desc, struct list_head *res)
+static int nfs_flush_multi(struct nfs_pageio_descriptor *desc,
+                          struct nfs_pgio_header *hdr)
 {
-       struct nfs_page *req = nfs_list_entry(desc->pg_list.next);
+       struct nfs_page *req = hdr->req;
        struct page *page = req->wb_page;
        struct nfs_write_data *data;
        size_t wsize = desc->pg_bsize, nbytes;
        unsigned int offset;
        int requests = 0;
-       int ret = 0;
+       struct nfs_commit_info cinfo;
 
-       nfs_list_remove_request(req);
+       nfs_init_cinfo(&cinfo, desc->pg_inode, desc->pg_dreq);
 
        if ((desc->pg_ioflags & FLUSH_COND_STABLE) &&
-           (desc->pg_moreio || NFS_I(desc->pg_inode)->ncommit ||
+           (desc->pg_moreio || nfs_reqs_to_commit(&cinfo) ||
             desc->pg_count > wsize))
                desc->pg_ioflags &= ~FLUSH_COND_STABLE;
 
@@ -1001,28 +1101,22 @@ static int nfs_flush_multi(struct nfs_pageio_descriptor *desc, struct list_head
        do {
                size_t len = min(nbytes, wsize);
 
-               data = nfs_writedata_alloc(1);
-               if (!data)
-                       goto out_bad;
-               data->pagevec[0] = page;
-               nfs_write_rpcsetup(req, data, len, offset, desc->pg_ioflags);
-               list_add(&data->list, res);
+               data = nfs_writedata_alloc(hdr, 1);
+               if (!data) {
+                       nfs_flush_error(desc, hdr);
+                       return -ENOMEM;
+               }
+               data->pages.pagevec[0] = page;
+               nfs_write_rpcsetup(data, len, offset, desc->pg_ioflags, &cinfo);
+               list_add(&data->list, &hdr->rpc_list);
                requests++;
                nbytes -= len;
                offset += len;
        } while (nbytes != 0);
-       atomic_set(&req->wb_complete, requests);
-       desc->pg_rpc_callops = &nfs_write_partial_ops;
-       return ret;
-
-out_bad:
-       while (!list_empty(res)) {
-               data = list_entry(res->next, struct nfs_write_data, list);
-               list_del(&data->list);
-               nfs_writedata_release(data);
-       }
-       nfs_redirty_request(req);
-       return -ENOMEM;
+       nfs_list_remove_request(req);
+       nfs_list_add_request(req, &hdr->pages);
+       desc->pg_rpc_callops = &nfs_write_common_ops;
+       return 0;
 }
 
 /*
@@ -1033,62 +1127,71 @@ out_bad:
  * This is the case if nfs_updatepage detects a conflicting request
  * that has been written but not committed.
  */
-static int nfs_flush_one(struct nfs_pageio_descriptor *desc, struct list_head *res)
+static int nfs_flush_one(struct nfs_pageio_descriptor *desc,
+                        struct nfs_pgio_header *hdr)
 {
        struct nfs_page         *req;
        struct page             **pages;
        struct nfs_write_data   *data;
        struct list_head *head = &desc->pg_list;
-       int ret = 0;
+       struct nfs_commit_info cinfo;
 
-       data = nfs_writedata_alloc(nfs_page_array_len(desc->pg_base,
-                                                     desc->pg_count));
+       data = nfs_writedata_alloc(hdr, nfs_page_array_len(desc->pg_base,
+                                                          desc->pg_count));
        if (!data) {
-               while (!list_empty(head)) {
-                       req = nfs_list_entry(head->next);
-                       nfs_list_remove_request(req);
-                       nfs_redirty_request(req);
-               }
-               ret = -ENOMEM;
-               goto out;
+               nfs_flush_error(desc, hdr);
+               return -ENOMEM;
        }
-       pages = data->pagevec;
+
+       nfs_init_cinfo(&cinfo, desc->pg_inode, desc->pg_dreq);
+       pages = data->pages.pagevec;
        while (!list_empty(head)) {
                req = nfs_list_entry(head->next);
                nfs_list_remove_request(req);
-               nfs_list_add_request(req, &data->pages);
+               nfs_list_add_request(req, &hdr->pages);
                *pages++ = req->wb_page;
        }
-       req = nfs_list_entry(data->pages.next);
 
        if ((desc->pg_ioflags & FLUSH_COND_STABLE) &&
-           (desc->pg_moreio || NFS_I(desc->pg_inode)->ncommit))
+           (desc->pg_moreio || nfs_reqs_to_commit(&cinfo)))
                desc->pg_ioflags &= ~FLUSH_COND_STABLE;
 
        /* Set up the argument struct */
-       nfs_write_rpcsetup(req, data, desc->pg_count, 0, desc->pg_ioflags);
-       list_add(&data->list, res);
-       desc->pg_rpc_callops = &nfs_write_full_ops;
-out:
-       return ret;
+       nfs_write_rpcsetup(data, desc->pg_count, 0, desc->pg_ioflags, &cinfo);
+       list_add(&data->list, &hdr->rpc_list);
+       desc->pg_rpc_callops = &nfs_write_common_ops;
+       return 0;
 }
 
-int nfs_generic_flush(struct nfs_pageio_descriptor *desc, struct list_head *head)
+int nfs_generic_flush(struct nfs_pageio_descriptor *desc,
+                     struct nfs_pgio_header *hdr)
 {
        if (desc->pg_bsize < PAGE_CACHE_SIZE)
-               return nfs_flush_multi(desc, head);
-       return nfs_flush_one(desc, head);
+               return nfs_flush_multi(desc, hdr);
+       return nfs_flush_one(desc, hdr);
 }
 
 static int nfs_generic_pg_writepages(struct nfs_pageio_descriptor *desc)
 {
-       LIST_HEAD(head);
+       struct nfs_write_header *whdr;
+       struct nfs_pgio_header *hdr;
        int ret;
 
-       ret = nfs_generic_flush(desc, &head);
+       whdr = nfs_writehdr_alloc();
+       if (!whdr) {
+               desc->pg_completion_ops->error_cleanup(&desc->pg_list);
+               return -ENOMEM;
+       }
+       hdr = &whdr->header;
+       nfs_pgheader_init(desc, hdr, nfs_writehdr_free);
+       atomic_inc(&hdr->refcnt);
+       ret = nfs_generic_flush(desc, hdr);
        if (ret == 0)
-               ret = nfs_do_multiple_writes(&head, desc->pg_rpc_callops,
-                               desc->pg_ioflags);
+               ret = nfs_do_multiple_writes(&hdr->rpc_list,
+                                            desc->pg_rpc_callops,
+                                            desc->pg_ioflags);
+       if (atomic_dec_and_test(&hdr->refcnt))
+               hdr->completion_ops->completion(hdr);
        return ret;
 }
 
@@ -1098,9 +1201,10 @@ static const struct nfs_pageio_ops nfs_pageio_write_ops = {
 };
 
 void nfs_pageio_init_write_mds(struct nfs_pageio_descriptor *pgio,
-                                 struct inode *inode, int ioflags)
+                              struct inode *inode, int ioflags,
+                              const struct nfs_pgio_completion_ops *compl_ops)
 {
-       nfs_pageio_init(pgio, inode, &nfs_pageio_write_ops,
+       nfs_pageio_init(pgio, inode, &nfs_pageio_write_ops, compl_ops,
                                NFS_SERVER(inode)->wsize, ioflags);
 }
 
@@ -1111,80 +1215,27 @@ void nfs_pageio_reset_write_mds(struct nfs_pageio_descriptor *pgio)
 }
 EXPORT_SYMBOL_GPL(nfs_pageio_reset_write_mds);
 
-static void nfs_pageio_init_write(struct nfs_pageio_descriptor *pgio,
-                                 struct inode *inode, int ioflags)
+void nfs_pageio_init_write(struct nfs_pageio_descriptor *pgio,
+                          struct inode *inode, int ioflags,
+                          const struct nfs_pgio_completion_ops *compl_ops)
 {
-       if (!pnfs_pageio_init_write(pgio, inode, ioflags))
-               nfs_pageio_init_write_mds(pgio, inode, ioflags);
+       if (!pnfs_pageio_init_write(pgio, inode, ioflags, compl_ops))
+               nfs_pageio_init_write_mds(pgio, inode, ioflags, compl_ops);
 }
 
-/*
- * Handle a write reply that flushed part of a page.
- */
-static void nfs_writeback_done_partial(struct rpc_task *task, void *calldata)
+void nfs_write_prepare(struct rpc_task *task, void *calldata)
 {
-       struct nfs_write_data   *data = calldata;
-
-       dprintk("NFS: %5u write(%s/%lld %d@%lld)",
-               task->tk_pid,
-               data->req->wb_context->dentry->d_inode->i_sb->s_id,
-               (long long)
-                 NFS_FILEID(data->req->wb_context->dentry->d_inode),
-               data->req->wb_bytes, (long long)req_offset(data->req));
-
-       nfs_writeback_done(task, data);
+       struct nfs_write_data *data = calldata;
+       NFS_PROTO(data->header->inode)->write_rpc_prepare(task, data);
 }
 
-static void nfs_writeback_release_partial(void *calldata)
+void nfs_commit_prepare(struct rpc_task *task, void *calldata)
 {
-       struct nfs_write_data   *data = calldata;
-       struct nfs_page         *req = data->req;
-       struct page             *page = req->wb_page;
-       int status = data->task.tk_status;
+       struct nfs_commit_data *data = calldata;
 
-       if (status < 0) {
-               nfs_set_pageerror(page);
-               nfs_context_set_write_error(req->wb_context, status);
-               dprintk(", error = %d\n", status);
-               goto out;
-       }
-
-       if (nfs_write_need_commit(data)) {
-               struct inode *inode = page->mapping->host;
-
-               spin_lock(&inode->i_lock);
-               if (test_bit(PG_NEED_RESCHED, &req->wb_flags)) {
-                       /* Do nothing we need to resend the writes */
-               } else if (!test_and_set_bit(PG_NEED_COMMIT, &req->wb_flags)) {
-                       memcpy(&req->wb_verf, &data->verf, sizeof(req->wb_verf));
-                       dprintk(" defer commit\n");
-               } else if (memcmp(&req->wb_verf, &data->verf, sizeof(req->wb_verf))) {
-                       set_bit(PG_NEED_RESCHED, &req->wb_flags);
-                       clear_bit(PG_NEED_COMMIT, &req->wb_flags);
-                       dprintk(" server reboot detected\n");
-               }
-               spin_unlock(&inode->i_lock);
-       } else
-               dprintk(" OK\n");
-
-out:
-       if (atomic_dec_and_test(&req->wb_complete))
-               nfs_writepage_release(req, data);
-       nfs_writedata_release(calldata);
+       NFS_PROTO(data->inode)->commit_rpc_prepare(task, data);
 }
 
-void nfs_write_prepare(struct rpc_task *task, void *calldata)
-{
-       struct nfs_write_data *data = calldata;
-       NFS_PROTO(data->inode)->write_rpc_prepare(task, data);
-}
-
-static const struct rpc_call_ops nfs_write_partial_ops = {
-       .rpc_call_prepare = nfs_write_prepare,
-       .rpc_call_done = nfs_writeback_done_partial,
-       .rpc_release = nfs_writeback_release_partial,
-};
-
 /*
  * Handle a write reply that flushes a whole page.
  *
@@ -1192,59 +1243,37 @@ static const struct rpc_call_ops nfs_write_partial_ops = {
  *       writebacks since the page->count is kept > 1 for as long
  *       as the page has a write request pending.
  */
-static void nfs_writeback_done_full(struct rpc_task *task, void *calldata)
+static void nfs_writeback_done_common(struct rpc_task *task, void *calldata)
 {
        struct nfs_write_data   *data = calldata;
 
        nfs_writeback_done(task, data);
 }
 
-static void nfs_writeback_release_full(void *calldata)
+static void nfs_writeback_release_common(void *calldata)
 {
        struct nfs_write_data   *data = calldata;
+       struct nfs_pgio_header *hdr = data->header;
        int status = data->task.tk_status;
+       struct nfs_page *req = hdr->req;
 
-       /* Update attributes as result of writeback. */
-       while (!list_empty(&data->pages)) {
-               struct nfs_page *req = nfs_list_entry(data->pages.next);
-               struct page *page = req->wb_page;
-
-               nfs_list_remove_request(req);
-
-               dprintk("NFS: %5u write (%s/%lld %d@%lld)",
-                       data->task.tk_pid,
-                       req->wb_context->dentry->d_inode->i_sb->s_id,
-                       (long long)NFS_FILEID(req->wb_context->dentry->d_inode),
-                       req->wb_bytes,
-                       (long long)req_offset(req));
-
-               if (status < 0) {
-                       nfs_set_pageerror(page);
-                       nfs_context_set_write_error(req->wb_context, status);
-                       dprintk(", error = %d\n", status);
-                       goto remove_request;
-               }
-
-               if (nfs_write_need_commit(data)) {
+       if ((status >= 0) && nfs_write_need_commit(data)) {
+               spin_lock(&hdr->lock);
+               if (test_bit(NFS_IOHDR_NEED_RESCHED, &hdr->flags))
+                       ; /* Do nothing */
+               else if (!test_and_set_bit(NFS_IOHDR_NEED_COMMIT, &hdr->flags))
                        memcpy(&req->wb_verf, &data->verf, sizeof(req->wb_verf));
-                       nfs_mark_request_commit(req, data->lseg);
-                       dprintk(" marked for commit\n");
-                       goto next;
-               }
-               dprintk(" OK\n");
-remove_request:
-               nfs_inode_remove_request(req);
-       next:
-               nfs_unlock_request(req);
-               nfs_end_page_writeback(page);
+               else if (memcmp(&req->wb_verf, &data->verf, sizeof(req->wb_verf)))
+                       set_bit(NFS_IOHDR_NEED_RESCHED, &hdr->flags);
+               spin_unlock(&hdr->lock);
        }
-       nfs_writedata_release(calldata);
+       nfs_writedata_release(data);
 }
 
-static const struct rpc_call_ops nfs_write_full_ops = {
+static const struct rpc_call_ops nfs_write_common_ops = {
        .rpc_call_prepare = nfs_write_prepare,
-       .rpc_call_done = nfs_writeback_done_full,
-       .rpc_release = nfs_writeback_release_full,
+       .rpc_call_done = nfs_writeback_done_common,
+       .rpc_release = nfs_writeback_release_common,
 };
 
 
@@ -1255,6 +1284,7 @@ void nfs_writeback_done(struct rpc_task *task, struct nfs_write_data *data)
 {
        struct nfs_writeargs    *argp = &data->args;
        struct nfs_writeres     *resp = &data->res;
+       struct inode            *inode = data->header->inode;
        int status;
 
        dprintk("NFS: %5u nfs_writeback_done (status %d)\n",
@@ -1267,10 +1297,10 @@ void nfs_writeback_done(struct rpc_task *task, struct nfs_write_data *data)
         * another writer had changed the file, but some applications
         * depend on tighter cache coherency when writing.
         */
-       status = NFS_PROTO(data->inode)->write_done(task, data);
+       status = NFS_PROTO(inode)->write_done(task, data);
        if (status != 0)
                return;
-       nfs_add_stats(data->inode, NFSIOS_SERVERWRITTENBYTES, resp->count);
+       nfs_add_stats(inode, NFSIOS_SERVERWRITTENBYTES, resp->count);
 
 #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4)
        if (resp->verf->committed < argp->stable && task->tk_status >= 0) {
@@ -1288,46 +1318,47 @@ void nfs_writeback_done(struct rpc_task *task, struct nfs_write_data *data)
                if (time_before(complain, jiffies)) {
                        dprintk("NFS:       faulty NFS server %s:"
                                " (committed = %d) != (stable = %d)\n",
-                               NFS_SERVER(data->inode)->nfs_client->cl_hostname,
+                               NFS_SERVER(inode)->nfs_client->cl_hostname,
                                resp->verf->committed, argp->stable);
                        complain = jiffies + 300 * HZ;
                }
        }
 #endif
-       /* Is this a short write? */
-       if (task->tk_status >= 0 && resp->count < argp->count) {
+       if (task->tk_status < 0)
+               nfs_set_pgio_error(data->header, task->tk_status, argp->offset);
+       else if (resp->count < argp->count) {
                static unsigned long    complain;
 
-               nfs_inc_stats(data->inode, NFSIOS_SHORTWRITE);
+               /* This a short write! */
+               nfs_inc_stats(inode, NFSIOS_SHORTWRITE);
 
                /* Has the server at least made some progress? */
-               if (resp->count != 0) {
-                       /* Was this an NFSv2 write or an NFSv3 stable write? */
-                       if (resp->verf->committed != NFS_UNSTABLE) {
-                               /* Resend from where the server left off */
-                               data->mds_offset += resp->count;
-                               argp->offset += resp->count;
-                               argp->pgbase += resp->count;
-                               argp->count -= resp->count;
-                       } else {
-                               /* Resend as a stable write in order to avoid
-                                * headaches in the case of a server crash.
-                                */
-                               argp->stable = NFS_FILE_SYNC;
+               if (resp->count == 0) {
+                       if (time_before(complain, jiffies)) {
+                               printk(KERN_WARNING
+                                      "NFS: Server wrote zero bytes, expected %u.\n",
+                                      argp->count);
+                               complain = jiffies + 300 * HZ;
                        }
-                       rpc_restart_call_prepare(task);
+                       nfs_set_pgio_error(data->header, -EIO, argp->offset);
+                       task->tk_status = -EIO;
                        return;
                }
-               if (time_before(complain, jiffies)) {
-                       printk(KERN_WARNING
-                              "NFS: Server wrote zero bytes, expected %u.\n",
-                                       argp->count);
-                       complain = jiffies + 300 * HZ;
+               /* Was this an NFSv2 write or an NFSv3 stable write? */
+               if (resp->verf->committed != NFS_UNSTABLE) {
+                       /* Resend from where the server left off */
+                       data->mds_offset += resp->count;
+                       argp->offset += resp->count;
+                       argp->pgbase += resp->count;
+                       argp->count -= resp->count;
+               } else {
+                       /* Resend as a stable write in order to avoid
+                        * headaches in the case of a server crash.
+                        */
+                       argp->stable = NFS_FILE_SYNC;
                }
-               /* Can't do anything about it except throw an error. */
-               task->tk_status = -EIO;
+               rpc_restart_call_prepare(task);
        }
-       return;
 }
 
 
@@ -1347,26 +1378,23 @@ static int nfs_commit_set_lock(struct nfs_inode *nfsi, int may_wait)
        return (ret < 0) ? ret : 1;
 }
 
-void nfs_commit_clear_lock(struct nfs_inode *nfsi)
+static void nfs_commit_clear_lock(struct nfs_inode *nfsi)
 {
        clear_bit(NFS_INO_COMMIT, &nfsi->flags);
        smp_mb__after_clear_bit();
        wake_up_bit(&nfsi->flags, NFS_INO_COMMIT);
 }
-EXPORT_SYMBOL_GPL(nfs_commit_clear_lock);
 
-void nfs_commitdata_release(void *data)
+void nfs_commitdata_release(struct nfs_commit_data *data)
 {
-       struct nfs_write_data *wdata = data;
-
-       put_nfs_open_context(wdata->args.context);
-       nfs_commit_free(wdata);
+       put_nfs_open_context(data->context);
+       nfs_commit_free(data);
 }
 EXPORT_SYMBOL_GPL(nfs_commitdata_release);
 
-int nfs_initiate_commit(struct nfs_write_data *data, struct rpc_clnt *clnt,
+int nfs_initiate_commit(struct rpc_clnt *clnt, struct nfs_commit_data *data,
                        const struct rpc_call_ops *call_ops,
-                       int how)
+                       int how, int flags)
 {
        struct rpc_task *task;
        int priority = flush_task_priority(how);
@@ -1382,7 +1410,7 @@ int nfs_initiate_commit(struct nfs_write_data *data, struct rpc_clnt *clnt,
                .callback_ops = call_ops,
                .callback_data = data,
                .workqueue = nfsiod_workqueue,
-               .flags = RPC_TASK_ASYNC,
+               .flags = RPC_TASK_ASYNC | flags,
                .priority = priority,
        };
        /* Set up the initial task struct.  */
@@ -1403,9 +1431,10 @@ EXPORT_SYMBOL_GPL(nfs_initiate_commit);
 /*
  * Set up the argument/result storage required for the RPC call.
  */
-void nfs_init_commit(struct nfs_write_data *data,
-                           struct list_head *head,
-                           struct pnfs_layout_segment *lseg)
+void nfs_init_commit(struct nfs_commit_data *data,
+                    struct list_head *head,
+                    struct pnfs_layout_segment *lseg,
+                    struct nfs_commit_info *cinfo)
 {
        struct nfs_page *first = nfs_list_entry(head->next);
        struct inode *inode = first->wb_context->dentry->d_inode;
@@ -1419,13 +1448,14 @@ void nfs_init_commit(struct nfs_write_data *data,
        data->cred        = first->wb_context->cred;
        data->lseg        = lseg; /* reference transferred */
        data->mds_ops     = &nfs_commit_ops;
+       data->completion_ops = cinfo->completion_ops;
+       data->dreq        = cinfo->dreq;
 
        data->args.fh     = NFS_FH(data->inode);
        /* Note: we always request a commit of the entire inode */
        data->args.offset = 0;
        data->args.count  = 0;
-       data->args.context = get_nfs_open_context(first->wb_context);
-       data->res.count   = 0;
+       data->context     = get_nfs_open_context(first->wb_context);
        data->res.fattr   = &data->fattr;
        data->res.verf    = &data->verf;
        nfs_fattr_init(&data->fattr);
@@ -1433,18 +1463,21 @@ void nfs_init_commit(struct nfs_write_data *data,
 EXPORT_SYMBOL_GPL(nfs_init_commit);
 
 void nfs_retry_commit(struct list_head *page_list,
-                     struct pnfs_layout_segment *lseg)
+                     struct pnfs_layout_segment *lseg,
+                     struct nfs_commit_info *cinfo)
 {
        struct nfs_page *req;
 
        while (!list_empty(page_list)) {
                req = nfs_list_entry(page_list->next);
                nfs_list_remove_request(req);
-               nfs_mark_request_commit(req, lseg);
-               dec_zone_page_state(req->wb_page, NR_UNSTABLE_NFS);
-               dec_bdi_stat(req->wb_page->mapping->backing_dev_info,
-                            BDI_RECLAIMABLE);
-               nfs_unlock_request(req);
+               nfs_mark_request_commit(req, lseg, cinfo);
+               if (!cinfo->dreq) {
+                       dec_zone_page_state(req->wb_page, NR_UNSTABLE_NFS);
+                       dec_bdi_stat(req->wb_page->mapping->backing_dev_info,
+                                    BDI_RECLAIMABLE);
+               }
+               nfs_unlock_and_release_request(req);
        }
 }
 EXPORT_SYMBOL_GPL(nfs_retry_commit);
@@ -1453,9 +1486,10 @@ EXPORT_SYMBOL_GPL(nfs_retry_commit);
  * Commit dirty pages
  */
 static int
-nfs_commit_list(struct inode *inode, struct list_head *head, int how)
+nfs_commit_list(struct inode *inode, struct list_head *head, int how,
+               struct nfs_commit_info *cinfo)
 {
-       struct nfs_write_data   *data;
+       struct nfs_commit_data  *data;
 
        data = nfs_commitdata_alloc();
 
@@ -1463,11 +1497,13 @@ nfs_commit_list(struct inode *inode, struct list_head *head, int how)
                goto out_bad;
 
        /* Set up the argument struct */
-       nfs_init_commit(data, head, NULL);
-       return nfs_initiate_commit(data, NFS_CLIENT(inode), data->mds_ops, how);
+       nfs_init_commit(data, head, NULL, cinfo);
+       atomic_inc(&cinfo->mds->rpcs_out);
+       return nfs_initiate_commit(NFS_CLIENT(inode), data, data->mds_ops,
+                                  how, 0);
  out_bad:
-       nfs_retry_commit(head, NULL);
-       nfs_commit_clear_lock(NFS_I(inode));
+       nfs_retry_commit(head, NULL, cinfo);
+       cinfo->completion_ops->error_cleanup(NFS_I(inode));
        return -ENOMEM;
 }
 
@@ -1476,7 +1512,7 @@ nfs_commit_list(struct inode *inode, struct list_head *head, int how)
  */
 static void nfs_commit_done(struct rpc_task *task, void *calldata)
 {
-       struct nfs_write_data   *data = calldata;
+       struct nfs_commit_data  *data = calldata;
 
         dprintk("NFS: %5u nfs_commit_done (status %d)\n",
                                 task->tk_pid, task->tk_status);
@@ -1485,10 +1521,11 @@ static void nfs_commit_done(struct rpc_task *task, void *calldata)
        NFS_PROTO(data->inode)->commit_done(task, data);
 }
 
-void nfs_commit_release_pages(struct nfs_write_data *data)
+static void nfs_commit_release_pages(struct nfs_commit_data *data)
 {
        struct nfs_page *req;
        int status = data->task.tk_status;
+       struct nfs_commit_info cinfo;
 
        while (!list_empty(&data->pages)) {
                req = nfs_list_entry(data->pages.next);
@@ -1519,42 +1556,59 @@ void nfs_commit_release_pages(struct nfs_write_data *data)
                dprintk(" mismatch\n");
                nfs_mark_request_dirty(req);
        next:
-               nfs_unlock_request(req);
+               nfs_unlock_and_release_request(req);
        }
+       nfs_init_cinfo(&cinfo, data->inode, data->dreq);
+       if (atomic_dec_and_test(&cinfo.mds->rpcs_out))
+               nfs_commit_clear_lock(NFS_I(data->inode));
 }
-EXPORT_SYMBOL_GPL(nfs_commit_release_pages);
 
 static void nfs_commit_release(void *calldata)
 {
-       struct nfs_write_data *data = calldata;
+       struct nfs_commit_data *data = calldata;
 
-       nfs_commit_release_pages(data);
-       nfs_commit_clear_lock(NFS_I(data->inode));
+       data->completion_ops->completion(data);
        nfs_commitdata_release(calldata);
 }
 
 static const struct rpc_call_ops nfs_commit_ops = {
-       .rpc_call_prepare = nfs_write_prepare,
+       .rpc_call_prepare = nfs_commit_prepare,
        .rpc_call_done = nfs_commit_done,
        .rpc_release = nfs_commit_release,
 };
 
+static const struct nfs_commit_completion_ops nfs_commit_completion_ops = {
+       .completion = nfs_commit_release_pages,
+       .error_cleanup = nfs_commit_clear_lock,
+};
+
+int nfs_generic_commit_list(struct inode *inode, struct list_head *head,
+                           int how, struct nfs_commit_info *cinfo)
+{
+       int status;
+
+       status = pnfs_commit_list(inode, head, how, cinfo);
+       if (status == PNFS_NOT_ATTEMPTED)
+               status = nfs_commit_list(inode, head, how, cinfo);
+       return status;
+}
+
 int nfs_commit_inode(struct inode *inode, int how)
 {
        LIST_HEAD(head);
+       struct nfs_commit_info cinfo;
        int may_wait = how & FLUSH_SYNC;
        int res;
 
        res = nfs_commit_set_lock(NFS_I(inode), may_wait);
        if (res <= 0)
                goto out_mark_dirty;
-       res = nfs_scan_commit(inode, &head);
+       nfs_init_cinfo_from_inode(&cinfo, inode);
+       res = nfs_scan_commit(inode, &head, &cinfo);
        if (res) {
                int error;
 
-               error = pnfs_commit_list(inode, &head, how);
-               if (error == PNFS_NOT_ATTEMPTED)
-                       error = nfs_commit_list(inode, &head, how);
+               error = nfs_generic_commit_list(inode, &head, how, &cinfo);
                if (error < 0)
                        return error;
                if (!may_wait)
@@ -1585,14 +1639,14 @@ static int nfs_commit_unstable_pages(struct inode *inode, struct writeback_contr
        int ret = 0;
 
        /* no commits means nothing needs to be done */
-       if (!nfsi->ncommit)
+       if (!nfsi->commit_info.ncommit)
                return ret;
 
        if (wbc->sync_mode == WB_SYNC_NONE) {
                /* Don't commit yet if this is a non-blocking flush and there
                 * are a lot of outstanding writes for this mapping.
                 */
-               if (nfsi->ncommit <= (nfsi->npages >> 1))
+               if (nfsi->commit_info.ncommit <= (nfsi->npages >> 1))
                        goto out_mark_dirty;
 
                /* don't wait for the COMMIT response */
@@ -1665,7 +1719,7 @@ int nfs_wb_page_cancel(struct inode *inode, struct page *page)
                req = nfs_page_find_request(page);
                if (req == NULL)
                        break;
-               if (nfs_lock_request_dontget(req)) {
+               if (nfs_lock_request(req)) {
                        nfs_clear_request_commit(req);
                        nfs_inode_remove_request(req);
                        /*
@@ -1673,7 +1727,7 @@ int nfs_wb_page_cancel(struct inode *inode, struct page *page)
                         * page as being dirty
                         */
                        cancel_dirty_page(page, PAGE_CACHE_SIZE);
-                       nfs_unlock_request(req);
+                       nfs_unlock_and_release_request(req);
                        break;
                }
                ret = nfs_wait_on_request(req);
@@ -1742,7 +1796,7 @@ int nfs_migrate_page(struct address_space *mapping, struct page *newpage,
 int __init nfs_init_writepagecache(void)
 {
        nfs_wdata_cachep = kmem_cache_create("nfs_write_data",
-                                            sizeof(struct nfs_write_data),
+                                            sizeof(struct nfs_write_header),
                                             0, SLAB_HWCACHE_ALIGN,
                                             NULL);
        if (nfs_wdata_cachep == NULL)
@@ -1753,6 +1807,13 @@ int __init nfs_init_writepagecache(void)
        if (nfs_wdata_mempool == NULL)
                return -ENOMEM;
 
+       nfs_cdata_cachep = kmem_cache_create("nfs_commit_data",
+                                            sizeof(struct nfs_commit_data),
+                                            0, SLAB_HWCACHE_ALIGN,
+                                            NULL);
+       if (nfs_cdata_cachep == NULL)
+               return -ENOMEM;
+
        nfs_commit_mempool = mempool_create_slab_pool(MIN_POOL_COMMIT,
                                                      nfs_wdata_cachep);
        if (nfs_commit_mempool == NULL)
index 204438cc914ea522b83907aaf618bb0dbcfa4068..34a10d78b839f4c73b3d851e19820bc712129f36 100644 (file)
@@ -11,7 +11,7 @@ int nfsexp_flags(struct svc_rqst *rqstp, struct svc_export *exp)
        struct exp_flavor_info *end = exp->ex_flavors + exp->ex_nflavors;
 
        for (f = exp->ex_flavors; f < end; f++) {
-               if (f->pseudoflavor == rqstp->rq_flavor)
+               if (f->pseudoflavor == rqstp->rq_cred.cr_flavor)
                        return f->flags;
        }
        return exp->ex_flags;
index 8e9689abbc0c7594fb6aa6cb7fb7735018165162..ba233499b9a5fc1b374bc7d79ad8f636f01135b0 100644 (file)
 #include <linux/namei.h>
 #include <linux/module.h>
 #include <linux/exportfs.h>
+#include <linux/sunrpc/svc_xprt.h>
 
 #include <net/ipv6.h>
 
 #include "nfsd.h"
 #include "nfsfh.h"
+#include "netns.h"
 
 #define NFSDDBG_FACILITY       NFSDDBG_EXPORT
 
@@ -38,7 +40,6 @@ typedef struct svc_export     svc_export;
 #define        EXPKEY_HASHBITS         8
 #define        EXPKEY_HASHMAX          (1 << EXPKEY_HASHBITS)
 #define        EXPKEY_HASHMASK         (EXPKEY_HASHMAX -1)
-static struct cache_head *expkey_table[EXPKEY_HASHMAX];
 
 static void expkey_put(struct kref *ref)
 {
@@ -71,9 +72,9 @@ static int expkey_upcall(struct cache_detail *cd, struct cache_head *h)
        return sunrpc_cache_pipe_upcall(cd, h, expkey_request);
 }
 
-static struct svc_expkey *svc_expkey_update(struct svc_expkey *new, struct svc_expkey *old);
-static struct svc_expkey *svc_expkey_lookup(struct svc_expkey *);
-static struct cache_detail svc_expkey_cache;
+static struct svc_expkey *svc_expkey_update(struct cache_detail *cd, struct svc_expkey *new,
+                                           struct svc_expkey *old);
+static struct svc_expkey *svc_expkey_lookup(struct cache_detail *cd, struct svc_expkey *);
 
 static int expkey_parse(struct cache_detail *cd, char *mesg, int mlen)
 {
@@ -131,7 +132,7 @@ static int expkey_parse(struct cache_detail *cd, char *mesg, int mlen)
        key.ek_fsidtype = fsidtype;
        memcpy(key.ek_fsid, buf, len);
 
-       ek = svc_expkey_lookup(&key);
+       ek = svc_expkey_lookup(cd, &key);
        err = -ENOMEM;
        if (!ek)
                goto out;
@@ -145,7 +146,7 @@ static int expkey_parse(struct cache_detail *cd, char *mesg, int mlen)
        err = 0;
        if (len == 0) {
                set_bit(CACHE_NEGATIVE, &key.h.flags);
-               ek = svc_expkey_update(&key, ek);
+               ek = svc_expkey_update(cd, &key, ek);
                if (!ek)
                        err = -ENOMEM;
        } else {
@@ -155,7 +156,7 @@ static int expkey_parse(struct cache_detail *cd, char *mesg, int mlen)
 
                dprintk("Found the path %s\n", buf);
 
-               ek = svc_expkey_update(&key, ek);
+               ek = svc_expkey_update(cd, &key, ek);
                if (!ek)
                        err = -ENOMEM;
                path_put(&key.ek_path);
@@ -163,7 +164,7 @@ static int expkey_parse(struct cache_detail *cd, char *mesg, int mlen)
        cache_flush();
  out:
        if (ek)
-               cache_put(&ek->h, &svc_expkey_cache);
+               cache_put(&ek->h, cd);
        if (dom)
                auth_domain_put(dom);
        kfree(buf);
@@ -239,10 +240,9 @@ static struct cache_head *expkey_alloc(void)
                return NULL;
 }
 
-static struct cache_detail svc_expkey_cache = {
+static struct cache_detail svc_expkey_cache_template = {
        .owner          = THIS_MODULE,
        .hash_size      = EXPKEY_HASHMAX,
-       .hash_table     = expkey_table,
        .name           = "nfsd.fh",
        .cache_put      = expkey_put,
        .cache_upcall   = expkey_upcall,
@@ -268,13 +268,12 @@ svc_expkey_hash(struct svc_expkey *item)
 }
 
 static struct svc_expkey *
-svc_expkey_lookup(struct svc_expkey *item)
+svc_expkey_lookup(struct cache_detail *cd, struct svc_expkey *item)
 {
        struct cache_head *ch;
        int hash = svc_expkey_hash(item);
 
-       ch = sunrpc_cache_lookup(&svc_expkey_cache, &item->h,
-                                hash);
+       ch = sunrpc_cache_lookup(cd, &item->h, hash);
        if (ch)
                return container_of(ch, struct svc_expkey, h);
        else
@@ -282,13 +281,13 @@ svc_expkey_lookup(struct svc_expkey *item)
 }
 
 static struct svc_expkey *
-svc_expkey_update(struct svc_expkey *new, struct svc_expkey *old)
+svc_expkey_update(struct cache_detail *cd, struct svc_expkey *new,
+                 struct svc_expkey *old)
 {
        struct cache_head *ch;
        int hash = svc_expkey_hash(new);
 
-       ch = sunrpc_cache_update(&svc_expkey_cache, &new->h,
-                                &old->h, hash);
+       ch = sunrpc_cache_update(cd, &new->h, &old->h, hash);
        if (ch)
                return container_of(ch, struct svc_expkey, h);
        else
@@ -299,8 +298,6 @@ svc_expkey_update(struct svc_expkey *new, struct svc_expkey *old)
 #define        EXPORT_HASHBITS         8
 #define        EXPORT_HASHMAX          (1<< EXPORT_HASHBITS)
 
-static struct cache_head *export_table[EXPORT_HASHMAX];
-
 static void nfsd4_fslocs_free(struct nfsd4_fs_locations *fsloc)
 {
        int i;
@@ -525,6 +522,7 @@ static int svc_export_parse(struct cache_detail *cd, char *mesg, int mlen)
                goto out1;
 
        exp.ex_client = dom;
+       exp.cd = cd;
 
        /* expiry */
        err = -EINVAL;
@@ -672,6 +670,7 @@ static void svc_export_init(struct cache_head *cnew, struct cache_head *citem)
        new->ex_fslocs.locations = NULL;
        new->ex_fslocs.locations_count = 0;
        new->ex_fslocs.migrated = 0;
+       new->cd = item->cd;
 }
 
 static void export_update(struct cache_head *cnew, struct cache_head *citem)
@@ -707,10 +706,9 @@ static struct cache_head *svc_export_alloc(void)
                return NULL;
 }
 
-struct cache_detail svc_export_cache = {
+static struct cache_detail svc_export_cache_template = {
        .owner          = THIS_MODULE,
        .hash_size      = EXPORT_HASHMAX,
-       .hash_table     = export_table,
        .name           = "nfsd.export",
        .cache_put      = svc_export_put,
        .cache_upcall   = svc_export_upcall,
@@ -739,8 +737,7 @@ svc_export_lookup(struct svc_export *exp)
        struct cache_head *ch;
        int hash = svc_export_hash(exp);
 
-       ch = sunrpc_cache_lookup(&svc_export_cache, &exp->h,
-                                hash);
+       ch = sunrpc_cache_lookup(exp->cd, &exp->h, hash);
        if (ch)
                return container_of(ch, struct svc_export, h);
        else
@@ -753,9 +750,7 @@ svc_export_update(struct svc_export *new, struct svc_export *old)
        struct cache_head *ch;
        int hash = svc_export_hash(old);
 
-       ch = sunrpc_cache_update(&svc_export_cache, &new->h,
-                                &old->h,
-                                hash);
+       ch = sunrpc_cache_update(old->cd, &new->h, &old->h, hash);
        if (ch)
                return container_of(ch, struct svc_export, h);
        else
@@ -764,7 +759,8 @@ svc_export_update(struct svc_export *new, struct svc_export *old)
 
 
 static struct svc_expkey *
-exp_find_key(svc_client *clp, int fsid_type, u32 *fsidv, struct cache_req *reqp)
+exp_find_key(struct cache_detail *cd, svc_client *clp, int fsid_type,
+            u32 *fsidv, struct cache_req *reqp)
 {
        struct svc_expkey key, *ek;
        int err;
@@ -776,18 +772,18 @@ exp_find_key(svc_client *clp, int fsid_type, u32 *fsidv, struct cache_req *reqp)
        key.ek_fsidtype = fsid_type;
        memcpy(key.ek_fsid, fsidv, key_len(fsid_type));
 
-       ek = svc_expkey_lookup(&key);
+       ek = svc_expkey_lookup(cd, &key);
        if (ek == NULL)
                return ERR_PTR(-ENOMEM);
-       err = cache_check(&svc_expkey_cache, &ek->h, reqp);
+       err = cache_check(cd, &ek->h, reqp);
        if (err)
                return ERR_PTR(err);
        return ek;
 }
 
 
-static svc_export *exp_get_by_name(svc_client *clp, const struct path *path,
-                                    struct cache_req *reqp)
+static svc_export *exp_get_by_name(struct cache_detail *cd, svc_client *clp,
+                                  const struct path *path, struct cache_req *reqp)
 {
        struct svc_export *exp, key;
        int err;
@@ -797,11 +793,12 @@ static svc_export *exp_get_by_name(svc_client *clp, const struct path *path,
 
        key.ex_client = clp;
        key.ex_path = *path;
+       key.cd = cd;
 
        exp = svc_export_lookup(&key);
        if (exp == NULL)
                return ERR_PTR(-ENOMEM);
-       err = cache_check(&svc_export_cache, &exp->h, reqp);
+       err = cache_check(cd, &exp->h, reqp);
        if (err)
                return ERR_PTR(err);
        return exp;
@@ -810,16 +807,17 @@ static svc_export *exp_get_by_name(svc_client *clp, const struct path *path,
 /*
  * Find the export entry for a given dentry.
  */
-static struct svc_export *exp_parent(svc_client *clp, struct path *path)
+static struct svc_export *exp_parent(struct cache_detail *cd, svc_client *clp,
+                                    struct path *path)
 {
        struct dentry *saved = dget(path->dentry);
-       svc_export *exp = exp_get_by_name(clp, path, NULL);
+       svc_export *exp = exp_get_by_name(cd, clp, path, NULL);
 
        while (PTR_ERR(exp) == -ENOENT && !IS_ROOT(path->dentry)) {
                struct dentry *parent = dget_parent(path->dentry);
                dput(path->dentry);
                path->dentry = parent;
-               exp = exp_get_by_name(clp, path, NULL);
+               exp = exp_get_by_name(cd, clp, path, NULL);
        }
        dput(path->dentry);
        path->dentry = saved;
@@ -834,13 +832,16 @@ static struct svc_export *exp_parent(svc_client *clp, struct path *path)
  * since its harder to fool a kernel module than a user space program.
  */
 int
-exp_rootfh(svc_client *clp, char *name, struct knfsd_fh *f, int maxsize)
+exp_rootfh(struct net *net, svc_client *clp, char *name,
+          struct knfsd_fh *f, int maxsize)
 {
        struct svc_export       *exp;
        struct path             path;
        struct inode            *inode;
        struct svc_fh           fh;
        int                     err;
+       struct nfsd_net         *nn = net_generic(net, nfsd_net_id);
+       struct cache_detail     *cd = nn->svc_export_cache;
 
        err = -EPERM;
        /* NB: we probably ought to check that it's NUL-terminated */
@@ -853,7 +854,7 @@ exp_rootfh(svc_client *clp, char *name, struct knfsd_fh *f, int maxsize)
        dprintk("nfsd: exp_rootfh(%s [%p] %s:%s/%ld)\n",
                 name, path.dentry, clp->name,
                 inode->i_sb->s_id, inode->i_ino);
-       exp = exp_parent(clp, &path);
+       exp = exp_parent(cd, clp, &path);
        if (IS_ERR(exp)) {
                err = PTR_ERR(exp);
                goto out;
@@ -875,16 +876,18 @@ out:
        return err;
 }
 
-static struct svc_export *exp_find(struct auth_domain *clp, int fsid_type,
+static struct svc_export *exp_find(struct cache_detail *cd,
+                                  struct auth_domain *clp, int fsid_type,
                                   u32 *fsidv, struct cache_req *reqp)
 {
        struct svc_export *exp;
-       struct svc_expkey *ek = exp_find_key(clp, fsid_type, fsidv, reqp);
+       struct nfsd_net *nn = net_generic(cd->net, nfsd_net_id);
+       struct svc_expkey *ek = exp_find_key(nn->svc_expkey_cache, clp, fsid_type, fsidv, reqp);
        if (IS_ERR(ek))
                return ERR_CAST(ek);
 
-       exp = exp_get_by_name(clp, &ek->ek_path, reqp);
-       cache_put(&ek->h, &svc_expkey_cache);
+       exp = exp_get_by_name(cd, clp, &ek->ek_path, reqp);
+       cache_put(&ek->h, nn->svc_expkey_cache);
 
        if (IS_ERR(exp))
                return ERR_CAST(exp);
@@ -901,13 +904,13 @@ __be32 check_nfsd_access(struct svc_export *exp, struct svc_rqst *rqstp)
                return 0;
        /* ip-address based client; check sec= export option: */
        for (f = exp->ex_flavors; f < end; f++) {
-               if (f->pseudoflavor == rqstp->rq_flavor)
+               if (f->pseudoflavor == rqstp->rq_cred.cr_flavor)
                        return 0;
        }
        /* defaults in absence of sec= options: */
        if (exp->ex_nflavors == 0) {
-               if (rqstp->rq_flavor == RPC_AUTH_NULL ||
-                   rqstp->rq_flavor == RPC_AUTH_UNIX)
+               if (rqstp->rq_cred.cr_flavor == RPC_AUTH_NULL ||
+                   rqstp->rq_cred.cr_flavor == RPC_AUTH_UNIX)
                        return 0;
        }
        return nfserr_wrongsec;
@@ -926,12 +929,14 @@ struct svc_export *
 rqst_exp_get_by_name(struct svc_rqst *rqstp, struct path *path)
 {
        struct svc_export *gssexp, *exp = ERR_PTR(-ENOENT);
+       struct nfsd_net *nn = net_generic(rqstp->rq_xprt->xpt_net, nfsd_net_id);
+       struct cache_detail *cd = nn->svc_export_cache;
 
        if (rqstp->rq_client == NULL)
                goto gss;
 
        /* First try the auth_unix client: */
-       exp = exp_get_by_name(rqstp->rq_client, path, &rqstp->rq_chandle);
+       exp = exp_get_by_name(cd, rqstp->rq_client, path, &rqstp->rq_chandle);
        if (PTR_ERR(exp) == -ENOENT)
                goto gss;
        if (IS_ERR(exp))
@@ -943,7 +948,7 @@ gss:
        /* Otherwise, try falling back on gss client */
        if (rqstp->rq_gssclient == NULL)
                return exp;
-       gssexp = exp_get_by_name(rqstp->rq_gssclient, path, &rqstp->rq_chandle);
+       gssexp = exp_get_by_name(cd, rqstp->rq_gssclient, path, &rqstp->rq_chandle);
        if (PTR_ERR(gssexp) == -ENOENT)
                return exp;
        if (!IS_ERR(exp))
@@ -955,12 +960,15 @@ struct svc_export *
 rqst_exp_find(struct svc_rqst *rqstp, int fsid_type, u32 *fsidv)
 {
        struct svc_export *gssexp, *exp = ERR_PTR(-ENOENT);
+       struct nfsd_net *nn = net_generic(rqstp->rq_xprt->xpt_net, nfsd_net_id);
+       struct cache_detail *cd = nn->svc_export_cache;
 
        if (rqstp->rq_client == NULL)
                goto gss;
 
        /* First try the auth_unix client: */
-       exp = exp_find(rqstp->rq_client, fsid_type, fsidv, &rqstp->rq_chandle);
+       exp = exp_find(cd, rqstp->rq_client, fsid_type,
+                      fsidv, &rqstp->rq_chandle);
        if (PTR_ERR(exp) == -ENOENT)
                goto gss;
        if (IS_ERR(exp))
@@ -972,7 +980,7 @@ gss:
        /* Otherwise, try falling back on gss client */
        if (rqstp->rq_gssclient == NULL)
                return exp;
-       gssexp = exp_find(rqstp->rq_gssclient, fsid_type, fsidv,
+       gssexp = exp_find(cd, rqstp->rq_gssclient, fsid_type, fsidv,
                                                &rqstp->rq_chandle);
        if (PTR_ERR(gssexp) == -ENOENT)
                return exp;
@@ -1029,13 +1037,15 @@ exp_pseudoroot(struct svc_rqst *rqstp, struct svc_fh *fhp)
 /* Iterator */
 
 static void *e_start(struct seq_file *m, loff_t *pos)
-       __acquires(svc_export_cache.hash_lock)
+       __acquires(((struct cache_detail *)m->private)->hash_lock)
 {
        loff_t n = *pos;
        unsigned hash, export;
        struct cache_head *ch;
-       
-       read_lock(&svc_export_cache.hash_lock);
+       struct cache_detail *cd = m->private;
+       struct cache_head **export_table = cd->hash_table;
+
+       read_lock(&cd->hash_lock);
        if (!n--)
                return SEQ_START_TOKEN;
        hash = n >> 32;
@@ -1060,6 +1070,8 @@ static void *e_next(struct seq_file *m, void *p, loff_t *pos)
 {
        struct cache_head *ch = p;
        int hash = (*pos >> 32);
+       struct cache_detail *cd = m->private;
+       struct cache_head **export_table = cd->hash_table;
 
        if (p == SEQ_START_TOKEN)
                hash = 0;
@@ -1082,9 +1094,11 @@ static void *e_next(struct seq_file *m, void *p, loff_t *pos)
 }
 
 static void e_stop(struct seq_file *m, void *p)
-       __releases(svc_export_cache.hash_lock)
+       __releases(((struct cache_detail *)m->private)->hash_lock)
 {
-       read_unlock(&svc_export_cache.hash_lock);
+       struct cache_detail *cd = m->private;
+
+       read_unlock(&cd->hash_lock);
 }
 
 static struct flags {
@@ -1195,6 +1209,7 @@ static int e_show(struct seq_file *m, void *p)
 {
        struct cache_head *cp = p;
        struct svc_export *exp = container_of(cp, struct svc_export, h);
+       struct cache_detail *cd = m->private;
 
        if (p == SEQ_START_TOKEN) {
                seq_puts(m, "# Version 1.1\n");
@@ -1203,10 +1218,10 @@ static int e_show(struct seq_file *m, void *p)
        }
 
        cache_get(&exp->h);
-       if (cache_check(&svc_export_cache, &exp->h, NULL))
+       if (cache_check(cd, &exp->h, NULL))
                return 0;
-       cache_put(&exp->h, &svc_export_cache);
-       return svc_export_show(m, &svc_export_cache, cp);
+       exp_put(exp);
+       return svc_export_show(m, cd, cp);
 }
 
 const struct seq_operations nfs_exports_op = {
@@ -1216,48 +1231,70 @@ const struct seq_operations nfs_exports_op = {
        .show   = e_show,
 };
 
-
 /*
  * Initialize the exports module.
  */
 int
-nfsd_export_init(void)
+nfsd_export_init(struct net *net)
 {
        int rv;
-       dprintk("nfsd: initializing export module.\n");
+       struct nfsd_net *nn = net_generic(net, nfsd_net_id);
+
+       dprintk("nfsd: initializing export module (net: %p).\n", net);
 
-       rv = cache_register_net(&svc_export_cache, &init_net);
+       nn->svc_export_cache = cache_create_net(&svc_export_cache_template, net);
+       if (IS_ERR(nn->svc_export_cache))
+               return PTR_ERR(nn->svc_export_cache);
+       rv = cache_register_net(nn->svc_export_cache, net);
        if (rv)
-               return rv;
-       rv = cache_register_net(&svc_expkey_cache, &init_net);
+               goto destroy_export_cache;
+
+       nn->svc_expkey_cache = cache_create_net(&svc_expkey_cache_template, net);
+       if (IS_ERR(nn->svc_expkey_cache)) {
+               rv = PTR_ERR(nn->svc_expkey_cache);
+               goto unregister_export_cache;
+       }
+       rv = cache_register_net(nn->svc_expkey_cache, net);
        if (rv)
-               cache_unregister_net(&svc_export_cache, &init_net);
-       return rv;
+               goto destroy_expkey_cache;
+       return 0;
 
+destroy_expkey_cache:
+       cache_destroy_net(nn->svc_expkey_cache, net);
+unregister_export_cache:
+       cache_unregister_net(nn->svc_export_cache, net);
+destroy_export_cache:
+       cache_destroy_net(nn->svc_export_cache, net);
+       return rv;
 }
 
 /*
  * Flush exports table - called when last nfsd thread is killed
  */
 void
-nfsd_export_flush(void)
+nfsd_export_flush(struct net *net)
 {
-       cache_purge(&svc_expkey_cache);
-       cache_purge(&svc_export_cache);
+       struct nfsd_net *nn = net_generic(net, nfsd_net_id);
+
+       cache_purge(nn->svc_expkey_cache);
+       cache_purge(nn->svc_export_cache);
 }
 
 /*
  * Shutdown the exports module.
  */
 void
-nfsd_export_shutdown(void)
+nfsd_export_shutdown(struct net *net)
 {
+       struct nfsd_net *nn = net_generic(net, nfsd_net_id);
 
-       dprintk("nfsd: shutting down export module.\n");
+       dprintk("nfsd: shutting down export module (net: %p).\n", net);
 
-       cache_unregister_net(&svc_expkey_cache, &init_net);
-       cache_unregister_net(&svc_export_cache, &init_net);
-       svcauth_unix_purge();
+       cache_unregister_net(nn->svc_expkey_cache, net);
+       cache_unregister_net(nn->svc_export_cache, net);
+       cache_destroy_net(nn->svc_expkey_cache, net);
+       cache_destroy_net(nn->svc_export_cache, net);
+       svcauth_unix_purge(net);
 
-       dprintk("nfsd: export shutdown complete.\n");
+       dprintk("nfsd: export shutdown complete (net: %p).\n", net);
 }
index 9559ce468732e7c00ae40cd4fc3a379a525ecef7..e6c38159622fe6bc337f3d24ada7db683838ceac 100644 (file)
@@ -58,6 +58,7 @@ static int nfsd_inject_set(void *op_ptr, u64 val)
 
 static int nfsd_inject_get(void *data, u64 *val)
 {
+       *val = 0;
        return 0;
 }
 
index 2f3be1321534375b65cac5705806edda29c6088d..9d513efc01baad65a0807284082315c3d5a68206 100644 (file)
 #define IDMAP_NAMESZ 128
 
 #ifdef CONFIG_NFSD_V4
-int nfsd_idmap_init(void);
-void nfsd_idmap_shutdown(void);
+int nfsd_idmap_init(struct net *);
+void nfsd_idmap_shutdown(struct net *);
 #else
-static inline int nfsd_idmap_init(void)
+static inline int nfsd_idmap_init(struct net *net)
 {
        return 0;
 }
-static inline void nfsd_idmap_shutdown(void)
+static inline void nfsd_idmap_shutdown(struct net *net)
 {
 }
 #endif
index 12e0cff435b43c06689cab12763a5890942cd37d..39365636b244fbfc7aaac3a794f83f87d7ea69a6 100644 (file)
@@ -28,6 +28,12 @@ struct cld_net;
 
 struct nfsd_net {
        struct cld_net *cld_net;
+
+       struct cache_detail *svc_expkey_cache;
+       struct cache_detail *svc_export_cache;
+
+       struct cache_detail *idtoname_cache;
+       struct cache_detail *nametoid_cache;
 };
 
 extern int nfsd_net_id;
index c8e9f637153ab3e44ba293f7097e7b32e77d4e54..a5fd6b982f277ce648bbd528947964ea2ef63c73 100644 (file)
@@ -650,9 +650,10 @@ static int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *c
        struct rpc_clnt *client;
 
        if (clp->cl_minorversion == 0) {
-               if (!clp->cl_principal && (clp->cl_flavor >= RPC_AUTH_GSS_KRB5))
+               if (!clp->cl_cred.cr_principal &&
+                               (clp->cl_flavor >= RPC_AUTH_GSS_KRB5))
                        return -EINVAL;
-               args.client_name = clp->cl_principal;
+               args.client_name = clp->cl_cred.cr_principal;
                args.prognumber = conn->cb_prog,
                args.protocol = XPRT_TRANSPORT_TCP;
                args.authflavor = clp->cl_flavor;
index 322d11ce06a452858ed0e547cf1e70e7883c08ee..dae36f1dee95e68defce943bedf01efc46d61a54 100644 (file)
 #include <linux/seq_file.h>
 #include <linux/sched.h>
 #include <linux/slab.h>
+#include <linux/sunrpc/svc_xprt.h>
 #include <net/net_namespace.h>
 #include "idmap.h"
 #include "nfsd.h"
+#include "netns.h"
 
 /*
  * Turn off idmapping when using AUTH_SYS.
@@ -107,8 +109,6 @@ ent_alloc(void)
  * ID -> Name cache
  */
 
-static struct cache_head *idtoname_table[ENT_HASHMAX];
-
 static uint32_t
 idtoname_hash(struct ent *ent)
 {
@@ -183,13 +183,13 @@ warn_no_idmapd(struct cache_detail *detail, int has_died)
 
 
 static int         idtoname_parse(struct cache_detail *, char *, int);
-static struct ent *idtoname_lookup(struct ent *);
-static struct ent *idtoname_update(struct ent *, struct ent *);
+static struct ent *idtoname_lookup(struct cache_detail *, struct ent *);
+static struct ent *idtoname_update(struct cache_detail *, struct ent *,
+                                  struct ent *);
 
-static struct cache_detail idtoname_cache = {
+static struct cache_detail idtoname_cache_template = {
        .owner          = THIS_MODULE,
        .hash_size      = ENT_HASHMAX,
-       .hash_table     = idtoname_table,
        .name           = "nfs4.idtoname",
        .cache_put      = ent_put,
        .cache_upcall   = idtoname_upcall,
@@ -244,7 +244,7 @@ idtoname_parse(struct cache_detail *cd, char *buf, int buflen)
                goto out;
 
        error = -ENOMEM;
-       res = idtoname_lookup(&ent);
+       res = idtoname_lookup(cd, &ent);
        if (!res)
                goto out;
 
@@ -260,11 +260,11 @@ idtoname_parse(struct cache_detail *cd, char *buf, int buflen)
        else
                memcpy(ent.name, buf1, sizeof(ent.name));
        error = -ENOMEM;
-       res = idtoname_update(&ent, res);
+       res = idtoname_update(cd, &ent, res);
        if (res == NULL)
                goto out;
 
-       cache_put(&res->h, &idtoname_cache);
+       cache_put(&res->h, cd);
 
        error = 0;
 out:
@@ -275,10 +275,9 @@ out:
 
 
 static struct ent *
-idtoname_lookup(struct ent *item)
+idtoname_lookup(struct cache_detail *cd, struct ent *item)
 {
-       struct cache_head *ch = sunrpc_cache_lookup(&idtoname_cache,
-                                                   &item->h,
+       struct cache_head *ch = sunrpc_cache_lookup(cd, &item->h,
                                                    idtoname_hash(item));
        if (ch)
                return container_of(ch, struct ent, h);
@@ -287,10 +286,9 @@ idtoname_lookup(struct ent *item)
 }
 
 static struct ent *
-idtoname_update(struct ent *new, struct ent *old)
+idtoname_update(struct cache_detail *cd, struct ent *new, struct ent *old)
 {
-       struct cache_head *ch = sunrpc_cache_update(&idtoname_cache,
-                                                   &new->h, &old->h,
+       struct cache_head *ch = sunrpc_cache_update(cd, &new->h, &old->h,
                                                    idtoname_hash(new));
        if (ch)
                return container_of(ch, struct ent, h);
@@ -303,8 +301,6 @@ idtoname_update(struct ent *new, struct ent *old)
  * Name -> ID cache
  */
 
-static struct cache_head *nametoid_table[ENT_HASHMAX];
-
 static inline int
 nametoid_hash(struct ent *ent)
 {
@@ -359,14 +355,14 @@ nametoid_show(struct seq_file *m, struct cache_detail *cd, struct cache_head *h)
        return 0;
 }
 
-static struct ent *nametoid_lookup(struct ent *);
-static struct ent *nametoid_update(struct ent *, struct ent *);
+static struct ent *nametoid_lookup(struct cache_detail *, struct ent *);
+static struct ent *nametoid_update(struct cache_detail *, struct ent *,
+                                  struct ent *);
 static int         nametoid_parse(struct cache_detail *, char *, int);
 
-static struct cache_detail nametoid_cache = {
+static struct cache_detail nametoid_cache_template = {
        .owner          = THIS_MODULE,
        .hash_size      = ENT_HASHMAX,
-       .hash_table     = nametoid_table,
        .name           = "nfs4.nametoid",
        .cache_put      = ent_put,
        .cache_upcall   = nametoid_upcall,
@@ -426,14 +422,14 @@ nametoid_parse(struct cache_detail *cd, char *buf, int buflen)
                set_bit(CACHE_NEGATIVE, &ent.h.flags);
 
        error = -ENOMEM;
-       res = nametoid_lookup(&ent);
+       res = nametoid_lookup(cd, &ent);
        if (res == NULL)
                goto out;
-       res = nametoid_update(&ent, res);
+       res = nametoid_update(cd, &ent, res);
        if (res == NULL)
                goto out;
 
-       cache_put(&res->h, &nametoid_cache);
+       cache_put(&res->h, cd);
        error = 0;
 out:
        kfree(buf1);
@@ -443,10 +439,9 @@ out:
 
 
 static struct ent *
-nametoid_lookup(struct ent *item)
+nametoid_lookup(struct cache_detail *cd, struct ent *item)
 {
-       struct cache_head *ch = sunrpc_cache_lookup(&nametoid_cache,
-                                                   &item->h,
+       struct cache_head *ch = sunrpc_cache_lookup(cd, &item->h,
                                                    nametoid_hash(item));
        if (ch)
                return container_of(ch, struct ent, h);
@@ -455,10 +450,9 @@ nametoid_lookup(struct ent *item)
 }
 
 static struct ent *
-nametoid_update(struct ent *new, struct ent *old)
+nametoid_update(struct cache_detail *cd, struct ent *new, struct ent *old)
 {
-       struct cache_head *ch = sunrpc_cache_update(&nametoid_cache,
-                                                   &new->h, &old->h,
+       struct cache_head *ch = sunrpc_cache_update(cd, &new->h, &old->h,
                                                    nametoid_hash(new));
        if (ch)
                return container_of(ch, struct ent, h);
@@ -471,34 +465,55 @@ nametoid_update(struct ent *new, struct ent *old)
  */
 
 int
-nfsd_idmap_init(void)
+nfsd_idmap_init(struct net *net)
 {
        int rv;
+       struct nfsd_net *nn = net_generic(net, nfsd_net_id);
 
-       rv = cache_register_net(&idtoname_cache, &init_net);
+       nn->idtoname_cache = cache_create_net(&idtoname_cache_template, net);
+       if (IS_ERR(nn->idtoname_cache))
+               return PTR_ERR(nn->idtoname_cache);
+       rv = cache_register_net(nn->idtoname_cache, net);
        if (rv)
-               return rv;
-       rv = cache_register_net(&nametoid_cache, &init_net);
+               goto destroy_idtoname_cache;
+       nn->nametoid_cache = cache_create_net(&nametoid_cache_template, net);
+       if (IS_ERR(nn->nametoid_cache)) {
+               rv = PTR_ERR(nn->idtoname_cache);
+               goto unregister_idtoname_cache;
+       }
+       rv = cache_register_net(nn->nametoid_cache, net);
        if (rv)
-               cache_unregister_net(&idtoname_cache, &init_net);
+               goto destroy_nametoid_cache;
+       return 0;
+
+destroy_nametoid_cache:
+       cache_destroy_net(nn->nametoid_cache, net);
+unregister_idtoname_cache:
+       cache_unregister_net(nn->idtoname_cache, net);
+destroy_idtoname_cache:
+       cache_destroy_net(nn->idtoname_cache, net);
        return rv;
 }
 
 void
-nfsd_idmap_shutdown(void)
+nfsd_idmap_shutdown(struct net *net)
 {
-       cache_unregister_net(&idtoname_cache, &init_net);
-       cache_unregister_net(&nametoid_cache, &init_net);
+       struct nfsd_net *nn = net_generic(net, nfsd_net_id);
+
+       cache_unregister_net(nn->idtoname_cache, net);
+       cache_unregister_net(nn->nametoid_cache, net);
+       cache_destroy_net(nn->idtoname_cache, net);
+       cache_destroy_net(nn->nametoid_cache, net);
 }
 
 static int
 idmap_lookup(struct svc_rqst *rqstp,
-               struct ent *(*lookup_fn)(struct ent *), struct ent *key,
-               struct cache_detail *detail, struct ent **item)
+               struct ent *(*lookup_fn)(struct cache_detail *, struct ent *),
+               struct ent *key, struct cache_detail *detail, struct ent **item)
 {
        int ret;
 
-       *item = lookup_fn(key);
+       *item = lookup_fn(detail, key);
        if (!*item)
                return -ENOMEM;
  retry:
@@ -506,7 +521,7 @@ idmap_lookup(struct svc_rqst *rqstp,
 
        if (ret == -ETIMEDOUT) {
                struct ent *prev_item = *item;
-               *item = lookup_fn(key);
+               *item = lookup_fn(detail, key);
                if (*item != prev_item)
                        goto retry;
                cache_put(&(*item)->h, detail);
@@ -531,19 +546,20 @@ idmap_name_to_id(struct svc_rqst *rqstp, int type, const char *name, u32 namelen
                .type = type,
        };
        int ret;
+       struct nfsd_net *nn = net_generic(rqstp->rq_xprt->xpt_net, nfsd_net_id);
 
        if (namelen + 1 > sizeof(key.name))
                return nfserr_badowner;
        memcpy(key.name, name, namelen);
        key.name[namelen] = '\0';
        strlcpy(key.authname, rqst_authname(rqstp), sizeof(key.authname));
-       ret = idmap_lookup(rqstp, nametoid_lookup, &key, &nametoid_cache, &item);
+       ret = idmap_lookup(rqstp, nametoid_lookup, &key, nn->nametoid_cache, &item);
        if (ret == -ENOENT)
                return nfserr_badowner;
        if (ret)
                return nfserrno(ret);
        *id = item->id;
-       cache_put(&item->h, &nametoid_cache);
+       cache_put(&item->h, nn->nametoid_cache);
        return 0;
 }
 
@@ -555,9 +571,10 @@ idmap_id_to_name(struct svc_rqst *rqstp, int type, uid_t id, char *name)
                .type = type,
        };
        int ret;
+       struct nfsd_net *nn = net_generic(rqstp->rq_xprt->xpt_net, nfsd_net_id);
 
        strlcpy(key.authname, rqst_authname(rqstp), sizeof(key.authname));
-       ret = idmap_lookup(rqstp, idtoname_lookup, &key, &idtoname_cache, &item);
+       ret = idmap_lookup(rqstp, idtoname_lookup, &key, nn->idtoname_cache, &item);
        if (ret == -ENOENT)
                return sprintf(name, "%u", id);
        if (ret)
@@ -565,7 +582,7 @@ idmap_id_to_name(struct svc_rqst *rqstp, int type, uid_t id, char *name)
        ret = strlen(item->name);
        BUG_ON(ret > IDMAP_NAMESZ);
        memcpy(name, item->name, ret);
-       cache_put(&item->h, &idtoname_cache);
+       cache_put(&item->h, nn->idtoname_cache);
        return ret;
 }
 
@@ -588,7 +605,7 @@ numeric_name_to_id(struct svc_rqst *rqstp, int type, const char *name, u32 namel
 static __be32
 do_name_to_id(struct svc_rqst *rqstp, int type, const char *name, u32 namelen, uid_t *id)
 {
-       if (nfs4_disable_idmapping && rqstp->rq_flavor < RPC_AUTH_GSS)
+       if (nfs4_disable_idmapping && rqstp->rq_cred.cr_flavor < RPC_AUTH_GSS)
                if (numeric_name_to_id(rqstp, type, name, namelen, id))
                        return 0;
                /*
@@ -601,7 +618,7 @@ do_name_to_id(struct svc_rqst *rqstp, int type, const char *name, u32 namelen, u
 static int
 do_id_to_name(struct svc_rqst *rqstp, int type, uid_t id, char *name)
 {
-       if (nfs4_disable_idmapping && rqstp->rq_flavor < RPC_AUTH_GSS)
+       if (nfs4_disable_idmapping && rqstp->rq_cred.cr_flavor < RPC_AUTH_GSS)
                return sprintf(name, "%u", id);
        return idmap_id_to_name(rqstp, type, id, name);
 }
index ed3f9206a0ee87c914f133492f1f6011775bdef8..5ff0b7b9fc08f22f39cc1f2d83062baceb773bdc 100644 (file)
@@ -570,7 +570,7 @@ static ssize_t
 cld_pipe_downcall(struct file *filp, const char __user *src, size_t mlen)
 {
        struct cld_upcall *tmp, *cup;
-       struct cld_msg *cmsg = (struct cld_msg *)src;
+       struct cld_msg __user *cmsg = (struct cld_msg __user *)src;
        uint32_t xid;
        struct nfsd_net *nn = net_generic(filp->f_dentry->d_sb->s_fs_info,
                                                nfsd_net_id);
@@ -1029,7 +1029,7 @@ rpc_pipefs_event(struct notifier_block *nb, unsigned long event, void *ptr)
        return ret;
 }
 
-struct notifier_block nfsd4_cld_block = {
+static struct notifier_block nfsd4_cld_block = {
        .notifier_call = rpc_pipefs_event,
 };
 
index 7f71c69cdcdfdcbd7245a71820b9f13e1a0bb135..8fdc9ec5c5d359f8defb2766e710eb35fc08c3b0 100644 (file)
@@ -42,6 +42,7 @@
 #include <linux/sunrpc/clnt.h>
 #include "xdr4.h"
 #include "vfs.h"
+#include "current_stateid.h"
 
 #define NFSDDBG_FACILITY                NFSDDBG_PROC
 
@@ -447,37 +448,69 @@ static struct list_head close_lru;
  *
  * which we should reject.
  */
-static void
-set_access(unsigned int *access, unsigned long bmap) {
+static unsigned int
+bmap_to_share_mode(unsigned long bmap) {
        int i;
+       unsigned int access = 0;
 
-       *access = 0;
        for (i = 1; i < 4; i++) {
                if (test_bit(i, &bmap))
-                       *access |= i;
-       }
-}
-
-static void
-set_deny(unsigned int *deny, unsigned long bmap) {
-       int i;
-
-       *deny = 0;
-       for (i = 0; i < 4; i++) {
-               if (test_bit(i, &bmap))
-                       *deny |= i ;
+                       access |= i;
        }
+       return access;
 }
 
-static int
+static bool
 test_share(struct nfs4_ol_stateid *stp, struct nfsd4_open *open) {
        unsigned int access, deny;
 
-       set_access(&access, stp->st_access_bmap);
-       set_deny(&deny, stp->st_deny_bmap);
+       access = bmap_to_share_mode(stp->st_access_bmap);
+       deny = bmap_to_share_mode(stp->st_deny_bmap);
        if ((access & open->op_share_deny) || (deny & open->op_share_access))
-               return 0;
-       return 1;
+               return false;
+       return true;
+}
+
+/* set share access for a given stateid */
+static inline void
+set_access(u32 access, struct nfs4_ol_stateid *stp)
+{
+       __set_bit(access, &stp->st_access_bmap);
+}
+
+/* clear share access for a given stateid */
+static inline void
+clear_access(u32 access, struct nfs4_ol_stateid *stp)
+{
+       __clear_bit(access, &stp->st_access_bmap);
+}
+
+/* test whether a given stateid has access */
+static inline bool
+test_access(u32 access, struct nfs4_ol_stateid *stp)
+{
+       return test_bit(access, &stp->st_access_bmap);
+}
+
+/* set share deny for a given stateid */
+static inline void
+set_deny(u32 access, struct nfs4_ol_stateid *stp)
+{
+       __set_bit(access, &stp->st_deny_bmap);
+}
+
+/* clear share deny for a given stateid */
+static inline void
+clear_deny(u32 access, struct nfs4_ol_stateid *stp)
+{
+       __clear_bit(access, &stp->st_deny_bmap);
+}
+
+/* test whether a given stateid is denying specific access */
+static inline bool
+test_deny(u32 access, struct nfs4_ol_stateid *stp)
+{
+       return test_bit(access, &stp->st_deny_bmap);
 }
 
 static int nfs4_access_to_omode(u32 access)
@@ -493,6 +526,20 @@ static int nfs4_access_to_omode(u32 access)
        BUG();
 }
 
+/* release all access and file references for a given stateid */
+static void
+release_all_access(struct nfs4_ol_stateid *stp)
+{
+       int i;
+
+       for (i = 1; i < 4; i++) {
+               if (test_access(i, stp))
+                       nfs4_file_put_access(stp->st_file,
+                                            nfs4_access_to_omode(i));
+               clear_access(i, stp);
+       }
+}
+
 static void unhash_generic_stateid(struct nfs4_ol_stateid *stp)
 {
        list_del(&stp->st_perfile);
@@ -501,16 +548,7 @@ static void unhash_generic_stateid(struct nfs4_ol_stateid *stp)
 
 static void close_generic_stateid(struct nfs4_ol_stateid *stp)
 {
-       int i;
-
-       if (stp->st_access_bmap) {
-               for (i = 1; i < 4; i++) {
-                       if (test_bit(i, &stp->st_access_bmap))
-                               nfs4_file_put_access(stp->st_file,
-                                               nfs4_access_to_omode(i));
-                       __clear_bit(i, &stp->st_access_bmap);
-               }
-       }
+       release_all_access(stp);
        put_nfs4_file(stp->st_file);
        stp->st_file = NULL;
 }
@@ -885,7 +923,7 @@ static struct nfsd4_session *alloc_init_session(struct svc_rqst *rqstp, struct n
        struct nfsd4_session *new;
        struct nfsd4_channel_attrs *fchan = &cses->fore_channel;
        int numslots, slotsize;
-       int status;
+       __be32 status;
        int idx;
 
        /*
@@ -984,7 +1022,8 @@ static inline void
 renew_client_locked(struct nfs4_client *clp)
 {
        if (is_client_expired(clp)) {
-               dprintk("%s: client (clientid %08x/%08x) already expired\n",
+               WARN_ON(1);
+               printk("%s: client (clientid %08x/%08x) already expired\n",
                        __func__,
                        clp->cl_clientid.cl_boot,
                        clp->cl_clientid.cl_id);
@@ -1049,9 +1088,7 @@ free_client(struct nfs4_client *clp)
                list_del(&ses->se_perclnt);
                nfsd4_put_session_locked(ses);
        }
-       if (clp->cl_cred.cr_group_info)
-               put_group_info(clp->cl_cred.cr_group_info);
-       kfree(clp->cl_principal);
+       free_svc_cred(&clp->cl_cred);
        kfree(clp->cl_name.data);
        kfree(clp);
 }
@@ -1132,12 +1169,21 @@ static void copy_clid(struct nfs4_client *target, struct nfs4_client *source)
        target->cl_clientid.cl_id = source->cl_clientid.cl_id; 
 }
 
-static void copy_cred(struct svc_cred *target, struct svc_cred *source)
+static int copy_cred(struct svc_cred *target, struct svc_cred *source)
 {
+       if (source->cr_principal) {
+               target->cr_principal =
+                               kstrdup(source->cr_principal, GFP_KERNEL);
+               if (target->cr_principal == NULL)
+                       return -ENOMEM;
+       } else
+               target->cr_principal = NULL;
+       target->cr_flavor = source->cr_flavor;
        target->cr_uid = source->cr_uid;
        target->cr_gid = source->cr_gid;
        target->cr_group_info = source->cr_group_info;
        get_group_info(target->cr_group_info);
+       return 0;
 }
 
 static int same_name(const char *n1, const char *n2)
@@ -1157,11 +1203,31 @@ same_clid(clientid_t *cl1, clientid_t *cl2)
        return (cl1->cl_boot == cl2->cl_boot) && (cl1->cl_id == cl2->cl_id);
 }
 
-/* XXX what about NGROUP */
+static bool groups_equal(struct group_info *g1, struct group_info *g2)
+{
+       int i;
+
+       if (g1->ngroups != g2->ngroups)
+               return false;
+       for (i=0; i<g1->ngroups; i++)
+               if (GROUP_AT(g1, i) != GROUP_AT(g2, i))
+                       return false;
+       return true;
+}
+
 static int
 same_creds(struct svc_cred *cr1, struct svc_cred *cr2)
 {
-       return cr1->cr_uid == cr2->cr_uid;
+       if ((cr1->cr_flavor != cr2->cr_flavor)
+               || (cr1->cr_uid != cr2->cr_uid)
+               || (cr1->cr_gid != cr2->cr_gid)
+               || !groups_equal(cr1->cr_group_info, cr2->cr_group_info))
+               return false;
+       if (cr1->cr_principal == cr2->cr_principal)
+               return true;
+       if (!cr1->cr_principal || !cr2->cr_principal)
+               return false;
+       return 0 == strcmp(cr1->cr_principal, cr1->cr_principal);
 }
 
 static void gen_clid(struct nfs4_client *clp)
@@ -1204,25 +1270,20 @@ static struct nfs4_client *create_client(struct xdr_netobj name, char *recdir,
 {
        struct nfs4_client *clp;
        struct sockaddr *sa = svc_addr(rqstp);
-       char *princ;
+       int ret;
 
        clp = alloc_client(name);
        if (clp == NULL)
                return NULL;
 
        INIT_LIST_HEAD(&clp->cl_sessions);
-
-       princ = svc_gss_principal(rqstp);
-       if (princ) {
-               clp->cl_principal = kstrdup(princ, GFP_KERNEL);
-               if (clp->cl_principal == NULL) {
-                       spin_lock(&client_lock);
-                       free_client(clp);
-                       spin_unlock(&client_lock);
-                       return NULL;
-               }
+       ret = copy_cred(&clp->cl_cred, &rqstp->rq_cred);
+       if (ret) {
+               spin_lock(&client_lock);
+               free_client(clp);
+               spin_unlock(&client_lock);
+               return NULL;
        }
-
        idr_init(&clp->cl_stateids);
        memcpy(clp->cl_recdir, recdir, HEXDIR_LEN);
        atomic_set(&clp->cl_refcount, 0);
@@ -1240,8 +1301,6 @@ static struct nfs4_client *create_client(struct xdr_netobj name, char *recdir,
        rpc_init_wait_queue(&clp->cl_cb_waitq, "Backchannel slot table");
        copy_verf(clp, verf);
        rpc_copy_addr((struct sockaddr *) &clp->cl_addr, sa);
-       clp->cl_flavor = rqstp->rq_flavor;
-       copy_cred(&clp->cl_cred, &rqstp->rq_cred);
        gen_confirm(clp);
        clp->cl_cb_session = NULL;
        return clp;
@@ -1470,18 +1529,32 @@ nfsd4_set_ex_flags(struct nfs4_client *new, struct nfsd4_exchange_id *clid)
        clid->flags = new->cl_exchange_flags;
 }
 
+static bool client_has_state(struct nfs4_client *clp)
+{
+       /*
+        * Note clp->cl_openowners check isn't quite right: there's no
+        * need to count owners without stateid's.
+        *
+        * Also note we should probably be using this in 4.0 case too.
+        */
+       return !list_empty(&clp->cl_openowners)
+               || !list_empty(&clp->cl_delegations)
+               || !list_empty(&clp->cl_sessions);
+}
+
 __be32
 nfsd4_exchange_id(struct svc_rqst *rqstp,
                  struct nfsd4_compound_state *cstate,
                  struct nfsd4_exchange_id *exid)
 {
        struct nfs4_client *unconf, *conf, *new;
-       int status;
+       __be32 status;
        unsigned int            strhashval;
        char                    dname[HEXDIR_LEN];
        char                    addr_str[INET6_ADDRSTRLEN];
        nfs4_verifier           verf = exid->verifier;
        struct sockaddr         *sa = svc_addr(rqstp);
+       bool    update = exid->flags & EXCHGID4_FLAG_UPD_CONFIRMED_REC_A;
 
        rpc_ntop(sa, addr_str, sizeof(addr_str));
        dprintk("%s rqstp=%p exid=%p clname.len=%u clname.data=%p "
@@ -1507,71 +1580,63 @@ nfsd4_exchange_id(struct svc_rqst *rqstp,
        status = nfs4_make_rec_clidname(dname, &exid->clname);
 
        if (status)
-               goto error;
+               return status;
 
        strhashval = clientstr_hashval(dname);
 
+       /* Cases below refer to rfc 5661 section 18.35.4: */
        nfs4_lock_state();
-       status = nfs_ok;
-
        conf = find_confirmed_client_by_str(dname, strhashval);
        if (conf) {
-               if (!clp_used_exchangeid(conf)) {
-                       status = nfserr_clid_inuse; /* XXX: ? */
-                       goto out;
-               }
-               if (!same_verf(&verf, &conf->cl_verifier)) {
-                       /* 18.35.4 case 8 */
-                       if (exid->flags & EXCHGID4_FLAG_UPD_CONFIRMED_REC_A) {
+               bool creds_match = same_creds(&conf->cl_cred, &rqstp->rq_cred);
+               bool verfs_match = same_verf(&verf, &conf->cl_verifier);
+
+               if (update) {
+                       if (!clp_used_exchangeid(conf)) { /* buggy client */
+                               status = nfserr_inval;
+                               goto out;
+                       }
+                       if (!creds_match) { /* case 9 */
+                               status = nfserr_perm;
+                               goto out;
+                       }
+                       if (!verfs_match) { /* case 8 */
                                status = nfserr_not_same;
                                goto out;
                        }
-                       /* Client reboot: destroy old state */
-                       expire_client(conf);
-                       goto out_new;
+                       /* case 6 */
+                       exid->flags |= EXCHGID4_FLAG_CONFIRMED_R;
+                       new = conf;
+                       goto out_copy;
                }
-               if (!same_creds(&conf->cl_cred, &rqstp->rq_cred)) {
-                       /* 18.35.4 case 9 */
-                       if (exid->flags & EXCHGID4_FLAG_UPD_CONFIRMED_REC_A) {
-                               status = nfserr_perm;
+               if (!creds_match) { /* case 3 */
+                       if (client_has_state(conf)) {
+                               status = nfserr_clid_inuse;
                                goto out;
                        }
                        expire_client(conf);
                        goto out_new;
                }
-               /*
-                * Set bit when the owner id and verifier map to an already
-                * confirmed client id (18.35.3).
-                */
-               exid->flags |= EXCHGID4_FLAG_CONFIRMED_R;
-
-               /*
-                * Falling into 18.35.4 case 2, possible router replay.
-                * Leave confirmed record intact and return same result.
-                */
-               copy_verf(conf, &verf);
-               new = conf;
-               goto out_copy;
+               if (verfs_match) { /* case 2 */
+                       conf->cl_exchange_flags |= EXCHGID4_FLAG_CONFIRMED_R;
+                       new = conf;
+                       goto out_copy;
+               }
+               /* case 5, client reboot */
+               goto out_new;
        }
 
-       /* 18.35.4 case 7 */
-       if (exid->flags & EXCHGID4_FLAG_UPD_CONFIRMED_REC_A) {
+       if (update) { /* case 7 */
                status = nfserr_noent;
                goto out;
        }
 
        unconf  = find_unconfirmed_client_by_str(dname, strhashval);
-       if (unconf) {
-               /*
-                * Possible retry or client restart.  Per 18.35.4 case 4,
-                * a new unconfirmed record should be generated regardless
-                * of whether any properties have changed.
-                */
+       if (unconf) /* case 4, possible retry or client restart */
                expire_client(unconf);
-       }
 
+       /* case 1 (normal case) */
 out_new:
-       /* Normal case */
        new = create_client(exid->clname, dname, rqstp, &verf);
        if (new == NULL) {
                status = nfserr_jukebox;
@@ -1584,7 +1649,7 @@ out_copy:
        exid->clientid.cl_boot = new->cl_clientid.cl_boot;
        exid->clientid.cl_id = new->cl_clientid.cl_id;
 
-       exid->seqid = 1;
+       exid->seqid = new->cl_cs_slot.sl_seqid + 1;
        nfsd4_set_ex_flags(new, exid);
 
        dprintk("nfsd4_exchange_id seqid %d flags %x\n",
@@ -1593,12 +1658,10 @@ out_copy:
 
 out:
        nfs4_unlock_state();
-error:
-       dprintk("nfsd4_exchange_id returns %d\n", ntohl(status));
        return status;
 }
 
-static int
+static __be32
 check_slot_seqid(u32 seqid, u32 slot_seqid, int slot_inuse)
 {
        dprintk("%s enter. seqid %d slot_seqid %d\n", __func__, seqid,
@@ -1626,7 +1689,7 @@ check_slot_seqid(u32 seqid, u32 slot_seqid, int slot_inuse)
  */
 static void
 nfsd4_cache_create_session(struct nfsd4_create_session *cr_ses,
-                          struct nfsd4_clid_slot *slot, int nfserr)
+                          struct nfsd4_clid_slot *slot, __be32 nfserr)
 {
        slot->sl_status = nfserr;
        memcpy(&slot->sl_cr_ses, cr_ses, sizeof(*cr_ses));
@@ -1657,7 +1720,7 @@ nfsd4_replay_create_session(struct nfsd4_create_session *cr_ses,
                                /* seqid, slotID, slotID, slotID, status */ \
                        5 ) * sizeof(__be32))
 
-static __be32 check_forechannel_attrs(struct nfsd4_channel_attrs fchannel)
+static bool check_forechannel_attrs(struct nfsd4_channel_attrs fchannel)
 {
        return fchannel.maxreq_sz < NFSD_MIN_REQ_HDR_SEQ_SZ
                || fchannel.maxresp_sz < NFSD_MIN_RESP_HDR_SEQ_SZ;
@@ -1673,7 +1736,7 @@ nfsd4_create_session(struct svc_rqst *rqstp,
        struct nfsd4_session *new;
        struct nfsd4_clid_slot *cs_slot = NULL;
        bool confirm_me = false;
-       int status = 0;
+       __be32 status = 0;
 
        if (cr_ses->flags & ~SESSION4_FLAG_MASK_A)
                return nfserr_inval;
@@ -1686,16 +1749,10 @@ nfsd4_create_session(struct svc_rqst *rqstp,
                cs_slot = &conf->cl_cs_slot;
                status = check_slot_seqid(cr_ses->seqid, cs_slot->sl_seqid, 0);
                if (status == nfserr_replay_cache) {
-                       dprintk("Got a create_session replay! seqid= %d\n",
-                               cs_slot->sl_seqid);
-                       /* Return the cached reply status */
                        status = nfsd4_replay_create_session(cr_ses, cs_slot);
                        goto out;
                } else if (cr_ses->seqid != cs_slot->sl_seqid + 1) {
                        status = nfserr_seq_misordered;
-                       dprintk("Sequence misordered!\n");
-                       dprintk("Expected seqid= %d but got seqid= %d\n",
-                               cs_slot->sl_seqid, cr_ses->seqid);
                        goto out;
                }
        } else if (unconf) {
@@ -1704,7 +1761,6 @@ nfsd4_create_session(struct svc_rqst *rqstp,
                        status = nfserr_clid_inuse;
                        goto out;
                }
-
                cs_slot = &unconf->cl_cs_slot;
                status = check_slot_seqid(cr_ses->seqid, cs_slot->sl_seqid, 0);
                if (status) {
@@ -1712,7 +1768,6 @@ nfsd4_create_session(struct svc_rqst *rqstp,
                        status = nfserr_seq_misordered;
                        goto out;
                }
-
                confirm_me = true;
                conf = unconf;
        } else {
@@ -1749,8 +1804,14 @@ nfsd4_create_session(struct svc_rqst *rqstp,
 
        /* cache solo and embedded create sessions under the state lock */
        nfsd4_cache_create_session(cr_ses, cs_slot, status);
-       if (confirm_me)
+       if (confirm_me) {
+               unsigned int hash = clientstr_hashval(unconf->cl_recdir);
+               struct nfs4_client *old =
+                       find_confirmed_client_by_str(conf->cl_recdir, hash);
+               if (old)
+                       expire_client(old);
                move_to_confirmed(conf);
+       }
 out:
        nfs4_unlock_state();
        dprintk("%s returns %d\n", __func__, ntohl(status));
@@ -1818,7 +1879,7 @@ nfsd4_destroy_session(struct svc_rqst *r,
                      struct nfsd4_destroy_session *sessionid)
 {
        struct nfsd4_session *ses;
-       u32 status = nfserr_badsession;
+       __be32 status = nfserr_badsession;
 
        /* Notes:
         * - The confirmed nfs4_client->cl_sessionid holds destroyed sessinid
@@ -1914,7 +1975,7 @@ nfsd4_sequence(struct svc_rqst *rqstp,
        struct nfsd4_session *session;
        struct nfsd4_slot *slot;
        struct nfsd4_conn *conn;
-       int status;
+       __be32 status;
 
        if (resp->opcnt != 1)
                return nfserr_sequence_pos;
@@ -2008,18 +2069,11 @@ out:
        return status;
 }
 
-static inline bool has_resources(struct nfs4_client *clp)
-{
-       return !list_empty(&clp->cl_openowners)
-               || !list_empty(&clp->cl_delegations)
-               || !list_empty(&clp->cl_sessions);
-}
-
 __be32
 nfsd4_destroy_clientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfsd4_destroy_clientid *dc)
 {
        struct nfs4_client *conf, *unconf, *clp;
-       int status = 0;
+       __be32 status = 0;
 
        nfs4_lock_state();
        unconf = find_unconfirmed_client(&dc->clientid);
@@ -2028,7 +2082,7 @@ nfsd4_destroy_clientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *csta
        if (conf) {
                clp = conf;
 
-               if (!is_client_expired(conf) && has_resources(conf)) {
+               if (!is_client_expired(conf) && client_has_state(conf)) {
                        status = nfserr_clientid_busy;
                        goto out;
                }
@@ -2055,7 +2109,7 @@ out:
 __be32
 nfsd4_reclaim_complete(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfsd4_reclaim_complete *rc)
 {
-       int status = 0;
+       __be32 status = 0;
 
        if (rc->rca_one_fs) {
                if (!cstate->current_fh.fh_dentry)
@@ -2106,17 +2160,13 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
        if (status)
                return status;
 
-       /* 
-        * XXX The Duplicate Request Cache (DRC) has been checked (??)
-        * We get here on a DRC miss.
-        */
-
        strhashval = clientstr_hashval(dname);
 
+       /* Cases below refer to rfc 3530 section 14.2.33: */
        nfs4_lock_state();
        conf = find_confirmed_client_by_str(dname, strhashval);
        if (conf) {
-               /* RFC 3530 14.2.33 CASE 0: */
+               /* case 0: */
                status = nfserr_clid_inuse;
                if (clp_used_exchangeid(conf))
                        goto out;
@@ -2129,63 +2179,18 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
                        goto out;
                }
        }
-       /*
-        * section 14.2.33 of RFC 3530 (under the heading "IMPLEMENTATION")
-        * has a description of SETCLIENTID request processing consisting
-        * of 5 bullet points, labeled as CASE0 - CASE4 below.
-        */
        unconf = find_unconfirmed_client_by_str(dname, strhashval);
+       if (unconf)
+               expire_client(unconf);
        status = nfserr_jukebox;
-       if (!conf) {
-               /*
-                * RFC 3530 14.2.33 CASE 4:
-                * placed first, because it is the normal case
-                */
-               if (unconf)
-                       expire_client(unconf);
-               new = create_client(clname, dname, rqstp, &clverifier);
-               if (new == NULL)
-                       goto out;
-               gen_clid(new);
-       } else if (same_verf(&conf->cl_verifier, &clverifier)) {
-               /*
-                * RFC 3530 14.2.33 CASE 1:
-                * probable callback update
-                */
-               if (unconf) {
-                       /* Note this is removing unconfirmed {*x***},
-                        * which is stronger than RFC recommended {vxc**}.
-                        * This has the advantage that there is at most
-                        * one {*x***} in either list at any time.
-                        */
-                       expire_client(unconf);
-               }
-               new = create_client(clname, dname, rqstp, &clverifier);
-               if (new == NULL)
-                       goto out;
+       new = create_client(clname, dname, rqstp, &clverifier);
+       if (new == NULL)
+               goto out;
+       if (conf && same_verf(&conf->cl_verifier, &clverifier))
+               /* case 1: probable callback update */
                copy_clid(new, conf);
-       } else if (!unconf) {
-               /*
-                * RFC 3530 14.2.33 CASE 2:
-                * probable client reboot; state will be removed if
-                * confirmed.
-                */
-               new = create_client(clname, dname, rqstp, &clverifier);
-               if (new == NULL)
-                       goto out;
-               gen_clid(new);
-       } else {
-               /*
-                * RFC 3530 14.2.33 CASE 3:
-                * probable client reboot; state will be removed if
-                * confirmed.
-                */
-               expire_client(unconf);
-               new = create_client(clname, dname, rqstp, &clverifier);
-               if (new == NULL)
-                       goto out;
+       else /* case 4 (new client) or cases 2, 3 (client reboot): */
                gen_clid(new);
-       }
        /*
         * XXX: we should probably set this at creation time, and check
         * for consistent minorversion use throughout:
@@ -2203,17 +2208,11 @@ out:
 }
 
 
-/*
- * Section 14.2.34 of RFC 3530 (under the heading "IMPLEMENTATION") has
- * a description of SETCLIENTID_CONFIRM request processing consisting of 4
- * bullets, labeled as CASE1 - CASE4 below.
- */
 __be32
 nfsd4_setclientid_confirm(struct svc_rqst *rqstp,
                         struct nfsd4_compound_state *cstate,
                         struct nfsd4_setclientid_confirm *setclientid_confirm)
 {
-       struct sockaddr *sa = svc_addr(rqstp);
        struct nfs4_client *conf, *unconf;
        nfs4_verifier confirm = setclientid_confirm->sc_confirm; 
        clientid_t * clid = &setclientid_confirm->sc_clientid;
@@ -2221,84 +2220,44 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp,
 
        if (STALE_CLIENTID(clid))
                return nfserr_stale_clientid;
-       /* 
-        * XXX The Duplicate Request Cache (DRC) has been checked (??)
-        * We get here on a DRC miss.
-        */
-
        nfs4_lock_state();
 
        conf = find_confirmed_client(clid);
        unconf = find_unconfirmed_client(clid);
-
-       status = nfserr_clid_inuse;
-       if (conf && !rpc_cmp_addr((struct sockaddr *) &conf->cl_addr, sa))
-               goto out;
-       if (unconf && !rpc_cmp_addr((struct sockaddr *) &unconf->cl_addr, sa))
-               goto out;
-
        /*
-        * section 14.2.34 of RFC 3530 has a description of
-        * SETCLIENTID_CONFIRM request processing consisting
-        * of 4 bullet points, labeled as CASE1 - CASE4 below.
+        * We try hard to give out unique clientid's, so if we get an
+        * attempt to confirm the same clientid with a different cred,
+        * there's a bug somewhere.  Let's charitably assume it's our
+        * bug.
         */
-       if (conf && unconf && same_verf(&confirm, &unconf->cl_confirm)) {
-               /*
-                * RFC 3530 14.2.34 CASE 1:
-                * callback update
-                */
-               if (!same_creds(&conf->cl_cred, &unconf->cl_cred))
-                       status = nfserr_clid_inuse;
-               else {
-                       nfsd4_change_callback(conf, &unconf->cl_cb_conn);
-                       nfsd4_probe_callback(conf);
-                       expire_client(unconf);
+       status = nfserr_serverfault;
+       if (unconf && !same_creds(&unconf->cl_cred, &rqstp->rq_cred))
+               goto out;
+       if (conf && !same_creds(&conf->cl_cred, &rqstp->rq_cred))
+               goto out;
+       /* cases below refer to rfc 3530 section 14.2.34: */
+       if (!unconf || !same_verf(&confirm, &unconf->cl_confirm)) {
+               if (conf && !unconf) /* case 2: probable retransmit */
                        status = nfs_ok;
+               else /* case 4: client hasn't noticed we rebooted yet? */
+                       status = nfserr_stale_clientid;
+               goto out;
+       }
+       status = nfs_ok;
+       if (conf) { /* case 1: callback update */
+               nfsd4_change_callback(conf, &unconf->cl_cb_conn);
+               nfsd4_probe_callback(conf);
+               expire_client(unconf);
+       } else { /* case 3: normal case; new or rebooted client */
+               unsigned int hash = clientstr_hashval(unconf->cl_recdir);
 
+               conf = find_confirmed_client_by_str(unconf->cl_recdir, hash);
+               if (conf) {
+                       nfsd4_client_record_remove(conf);
+                       expire_client(conf);
                }
-       } else if (conf && !unconf) {
-               /*
-                * RFC 3530 14.2.34 CASE 2:
-                * probable retransmitted request; play it safe and
-                * do nothing.
-                */
-               if (!same_creds(&conf->cl_cred, &rqstp->rq_cred))
-                       status = nfserr_clid_inuse;
-               else
-                       status = nfs_ok;
-       } else if (!conf && unconf
-                       && same_verf(&unconf->cl_confirm, &confirm)) {
-               /*
-                * RFC 3530 14.2.34 CASE 3:
-                * Normal case; new or rebooted client:
-                */
-               if (!same_creds(&unconf->cl_cred, &rqstp->rq_cred)) {
-                       status = nfserr_clid_inuse;
-               } else {
-                       unsigned int hash =
-                               clientstr_hashval(unconf->cl_recdir);
-                       conf = find_confirmed_client_by_str(unconf->cl_recdir,
-                                                           hash);
-                       if (conf) {
-                               nfsd4_client_record_remove(conf);
-                               expire_client(conf);
-                       }
-                       move_to_confirmed(unconf);
-                       conf = unconf;
-                       nfsd4_probe_callback(conf);
-                       status = nfs_ok;
-               }
-       } else if ((!conf || (conf && !same_verf(&conf->cl_confirm, &confirm)))
-           && (!unconf || (unconf && !same_verf(&unconf->cl_confirm,
-                                                               &confirm)))) {
-               /*
-                * RFC 3530 14.2.34 CASE 4:
-                * Client probably hasn't noticed that we rebooted yet.
-                */
-               status = nfserr_stale_clientid;
-       } else {
-               /* check that we have hit one of the cases...*/
-               status = nfserr_clid_inuse;
+               move_to_confirmed(unconf);
+               nfsd4_probe_callback(unconf);
        }
 out:
        nfs4_unlock_state();
@@ -2454,8 +2413,8 @@ static void init_open_stateid(struct nfs4_ol_stateid *stp, struct nfs4_file *fp,
        stp->st_file = fp;
        stp->st_access_bmap = 0;
        stp->st_deny_bmap = 0;
-       __set_bit(open->op_share_access, &stp->st_access_bmap);
-       __set_bit(open->op_share_deny, &stp->st_deny_bmap);
+       set_access(open->op_share_access, stp);
+       set_deny(open->op_share_deny, stp);
        stp->st_openstp = NULL;
 }
 
@@ -2534,8 +2493,8 @@ nfs4_share_conflict(struct svc_fh *current_fh, unsigned int deny_type)
        ret = nfserr_locked;
        /* Search for conflicting share reservations */
        list_for_each_entry(stp, &fp->fi_stateids, st_perfile) {
-               if (test_bit(deny_type, &stp->st_deny_bmap) ||
-                   test_bit(NFS4_SHARE_DENY_BOTH, &stp->st_deny_bmap))
+               if (test_deny(deny_type, stp) ||
+                   test_deny(NFS4_SHARE_DENY_BOTH, stp))
                        goto out;
        }
        ret = nfs_ok;
@@ -2791,7 +2750,7 @@ nfs4_upgrade_open(struct svc_rqst *rqstp, struct nfs4_file *fp, struct svc_fh *c
        bool new_access;
        __be32 status;
 
-       new_access = !test_bit(op_share_access, &stp->st_access_bmap);
+       new_access = !test_access(op_share_access, stp);
        if (new_access) {
                status = nfs4_get_vfs_file(rqstp, fp, cur_fh, open);
                if (status)
@@ -2806,8 +2765,8 @@ nfs4_upgrade_open(struct svc_rqst *rqstp, struct nfs4_file *fp, struct svc_fh *c
                return status;
        }
        /* remember the open */
-       __set_bit(op_share_access, &stp->st_access_bmap);
-       __set_bit(open->op_share_deny, &stp->st_deny_bmap);
+       set_access(op_share_access, stp);
+       set_deny(open->op_share_deny, stp);
 
        return nfs_ok;
 }
@@ -3155,10 +3114,17 @@ out:
 static struct lock_manager nfsd4_manager = {
 };
 
+static bool grace_ended;
+
 static void
 nfsd4_end_grace(void)
 {
+       /* do nothing if grace period already ended */
+       if (grace_ended)
+               return;
+
        dprintk("NFSD: end of grace period\n");
+       grace_ended = true;
        nfsd4_record_grace_done(&init_net, boot_time);
        locks_end_grace(&nfsd4_manager);
        /*
@@ -3183,8 +3149,7 @@ nfs4_laundromat(void)
        nfs4_lock_state();
 
        dprintk("NFSD: laundromat service - starting\n");
-       if (locks_in_grace())
-               nfsd4_end_grace();
+       nfsd4_end_grace();
        INIT_LIST_HEAD(&reaplist);
        spin_lock(&client_lock);
        list_for_each_safe(pos, next, &client_lru) {
@@ -3276,18 +3241,18 @@ STALE_STATEID(stateid_t *stateid)
 }
 
 static inline int
-access_permit_read(unsigned long access_bmap)
+access_permit_read(struct nfs4_ol_stateid *stp)
 {
-       return test_bit(NFS4_SHARE_ACCESS_READ, &access_bmap) ||
-               test_bit(NFS4_SHARE_ACCESS_BOTH, &access_bmap) ||
-               test_bit(NFS4_SHARE_ACCESS_WRITE, &access_bmap);
+       return test_access(NFS4_SHARE_ACCESS_READ, stp) ||
+               test_access(NFS4_SHARE_ACCESS_BOTH, stp) ||
+               test_access(NFS4_SHARE_ACCESS_WRITE, stp);
 }
 
 static inline int
-access_permit_write(unsigned long access_bmap)
+access_permit_write(struct nfs4_ol_stateid *stp)
 {
-       return test_bit(NFS4_SHARE_ACCESS_WRITE, &access_bmap) ||
-               test_bit(NFS4_SHARE_ACCESS_BOTH, &access_bmap);
+       return test_access(NFS4_SHARE_ACCESS_WRITE, stp) ||
+               test_access(NFS4_SHARE_ACCESS_BOTH, stp);
 }
 
 static
@@ -3298,9 +3263,9 @@ __be32 nfs4_check_openmode(struct nfs4_ol_stateid *stp, int flags)
        /* For lock stateid's, we test the parent open, not the lock: */
        if (stp->st_openstp)
                stp = stp->st_openstp;
-       if ((flags & WR_STATE) && (!access_permit_write(stp->st_access_bmap)))
+       if ((flags & WR_STATE) && !access_permit_write(stp))
                 goto out;
-       if ((flags & RD_STATE) && (!access_permit_read(stp->st_access_bmap)))
+       if ((flags & RD_STATE) && !access_permit_read(stp))
                 goto out;
        status = nfs_ok;
 out:
@@ -3340,7 +3305,7 @@ static bool stateid_generation_after(stateid_t *a, stateid_t *b)
        return (s32)a->si_generation - (s32)b->si_generation > 0;
 }
 
-static int check_stateid_generation(stateid_t *in, stateid_t *ref, bool has_session)
+static __be32 check_stateid_generation(stateid_t *in, stateid_t *ref, bool has_session)
 {
        /*
         * When sessions are used the stateid generation number is ignored
@@ -3649,10 +3614,10 @@ out:
 
 static inline void nfs4_stateid_downgrade_bit(struct nfs4_ol_stateid *stp, u32 access)
 {
-       if (!test_bit(access, &stp->st_access_bmap))
+       if (!test_access(access, stp))
                return;
        nfs4_file_put_access(stp->st_file, nfs4_access_to_omode(access));
-       __clear_bit(access, &stp->st_access_bmap);
+       clear_access(access, stp);
 }
 
 static inline void nfs4_stateid_downgrade(struct nfs4_ol_stateid *stp, u32 to_access)
@@ -3674,12 +3639,12 @@ static inline void nfs4_stateid_downgrade(struct nfs4_ol_stateid *stp, u32 to_ac
 }
 
 static void
-reset_union_bmap_deny(unsigned long deny, unsigned long *bmap)
+reset_union_bmap_deny(unsigned long deny, struct nfs4_ol_stateid *stp)
 {
        int i;
        for (i = 0; i < 4; i++) {
                if ((i & deny) != i)
-                       __clear_bit(i, bmap);
+                       clear_deny(i, stp);
        }
 }
 
@@ -3706,19 +3671,19 @@ nfsd4_open_downgrade(struct svc_rqst *rqstp,
        if (status)
                goto out; 
        status = nfserr_inval;
-       if (!test_bit(od->od_share_access, &stp->st_access_bmap)) {
-               dprintk("NFSD:access not a subset current bitmap: 0x%lx, input access=%08x\n",
+       if (!test_access(od->od_share_access, stp)) {
+               dprintk("NFSD: access not a subset current bitmap: 0x%lx, input access=%08x\n",
                        stp->st_access_bmap, od->od_share_access);
                goto out;
        }
-       if (!test_bit(od->od_share_deny, &stp->st_deny_bmap)) {
+       if (!test_deny(od->od_share_deny, stp)) {
                dprintk("NFSD:deny not a subset current bitmap: 0x%lx, input deny=%08x\n",
                        stp->st_deny_bmap, od->od_share_deny);
                goto out;
        }
        nfs4_stateid_downgrade(stp, od->od_share_access);
 
-       reset_union_bmap_deny(od->od_share_deny, &stp->st_deny_bmap);
+       reset_union_bmap_deny(od->od_share_deny, stp);
 
        update_stateid(&stp->st_stid.sc_stateid);
        memcpy(&od->od_stateid, &stp->st_stid.sc_stateid, sizeof(stateid_t));
@@ -4008,13 +3973,13 @@ static void get_lock_access(struct nfs4_ol_stateid *lock_stp, u32 access)
        struct nfs4_file *fp = lock_stp->st_file;
        int oflag = nfs4_access_to_omode(access);
 
-       if (test_bit(access, &lock_stp->st_access_bmap))
+       if (test_access(access, lock_stp))
                return;
        nfs4_file_get_access(fp, oflag);
-       __set_bit(access, &lock_stp->st_access_bmap);
+       set_access(access, lock_stp);
 }
 
-__be32 lookup_or_create_lock_state(struct nfsd4_compound_state *cstate, struct nfs4_ol_stateid *ost, struct nfsd4_lock *lock, struct nfs4_ol_stateid **lst, bool *new)
+static __be32 lookup_or_create_lock_state(struct nfsd4_compound_state *cstate, struct nfs4_ol_stateid *ost, struct nfsd4_lock *lock, struct nfs4_ol_stateid **lst, bool *new)
 {
        struct nfs4_file *fi = ost->st_file;
        struct nfs4_openowner *oo = openowner(ost->st_stateowner);
@@ -4055,7 +4020,6 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
        struct nfs4_openowner *open_sop = NULL;
        struct nfs4_lockowner *lock_sop = NULL;
        struct nfs4_ol_stateid *lock_stp;
-       struct nfs4_file *fp;
        struct file *filp = NULL;
        struct file_lock file_lock;
        struct file_lock conflock;
@@ -4123,7 +4087,6 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
                        goto out;
        }
        lock_sop = lockowner(lock_stp->st_stateowner);
-       fp = lock_stp->st_file;
 
        lkflg = setlkflg(lock->lk_type);
        status = nfs4_check_openmode(lock_stp, lkflg);
@@ -4715,6 +4678,7 @@ nfs4_state_start(void)
        nfsd4_client_tracking_init(&init_net);
        boot_time = get_seconds();
        locks_start_grace(&nfsd4_manager);
+       grace_ended = false;
        printk(KERN_INFO "NFSD: starting %ld-second grace period\n",
               nfsd4_grace);
        ret = set_callback_cred();
index 74c00bc92b9af6b01e95e55c119b90d61fbf9d34..4949667c84ea0c3d687a46faf0a455c410c39b6f 100644 (file)
@@ -1674,12 +1674,12 @@ nfsd4_decode_compound(struct nfsd4_compoundargs *argp)
 
 static void write32(__be32 **p, u32 n)
 {
-       *(*p)++ = n;
+       *(*p)++ = htonl(n);
 }
 
 static void write64(__be32 **p, u64 n)
 {
-       write32(p, (u32)(n >> 32));
+       write32(p, (n >> 32));
        write32(p, (u32)n);
 }
 
@@ -1744,15 +1744,16 @@ static void encode_seqid_op_tail(struct nfsd4_compoundres *resp, __be32 *save, _
 }
 
 /* Encode as an array of strings the string given with components
- * separated @sep.
+ * separated @sep, escaped with esc_enter and esc_exit.
  */
-static __be32 nfsd4_encode_components(char sep, char *components,
-                                  __be32 **pp, int *buflen)
+static __be32 nfsd4_encode_components_esc(char sep, char *components,
+                                  __be32 **pp, int *buflen,
+                                  char esc_enter, char esc_exit)
 {
        __be32 *p = *pp;
        __be32 *countp = p;
        int strlen, count=0;
-       char *str, *end;
+       char *str, *end, *next;
 
        dprintk("nfsd4_encode_components(%s)\n", components);
        if ((*buflen -= 4) < 0)
@@ -1760,8 +1761,23 @@ static __be32 nfsd4_encode_components(char sep, char *components,
        WRITE32(0); /* We will fill this in with @count later */
        end = str = components;
        while (*end) {
-               for (; *end && (*end != sep); end++)
-                       ; /* Point to end of component */
+               bool found_esc = false;
+
+               /* try to parse as esc_start, ..., esc_end, sep */
+               if (*str == esc_enter) {
+                       for (; *end && (*end != esc_exit); end++)
+                               /* find esc_exit or end of string */;
+                       next = end + 1;
+                       if (*end && (!*next || *next == sep)) {
+                               str++;
+                               found_esc = true;
+                       }
+               }
+
+               if (!found_esc)
+                       for (; *end && (*end != sep); end++)
+                               /* find sep or end of string */;
+
                strlen = end - str;
                if (strlen) {
                        if ((*buflen -= ((XDR_QUADLEN(strlen) << 2) + 4)) < 0)
@@ -1780,6 +1796,15 @@ static __be32 nfsd4_encode_components(char sep, char *components,
        return 0;
 }
 
+/* Encode as an array of strings the string given with components
+ * separated @sep.
+ */
+static __be32 nfsd4_encode_components(char sep, char *components,
+                                  __be32 **pp, int *buflen)
+{
+       return nfsd4_encode_components_esc(sep, components, pp, buflen, 0, 0);
+}
+
 /*
  * encode a location element of a fs_locations structure
  */
@@ -1789,7 +1814,8 @@ static __be32 nfsd4_encode_fs_location4(struct nfsd4_fs_location *location,
        __be32 status;
        __be32 *p = *pp;
 
-       status = nfsd4_encode_components(':', location->hosts, &p, buflen);
+       status = nfsd4_encode_components_esc(':', location->hosts, &p, buflen,
+                                               '[', ']');
        if (status)
                return status;
        status = nfsd4_encode_components('/', location->path, &p, buflen);
@@ -3251,7 +3277,7 @@ nfsd4_encode_write(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_w
 }
 
 static __be32
-nfsd4_encode_exchange_id(struct nfsd4_compoundres *resp, int nfserr,
+nfsd4_encode_exchange_id(struct nfsd4_compoundres *resp, __be32 nfserr,
                         struct nfsd4_exchange_id *exid)
 {
        __be32 *p;
@@ -3306,7 +3332,7 @@ nfsd4_encode_exchange_id(struct nfsd4_compoundres *resp, int nfserr,
 }
 
 static __be32
-nfsd4_encode_create_session(struct nfsd4_compoundres *resp, int nfserr,
+nfsd4_encode_create_session(struct nfsd4_compoundres *resp, __be32 nfserr,
                            struct nfsd4_create_session *sess)
 {
        __be32 *p;
@@ -3355,14 +3381,14 @@ nfsd4_encode_create_session(struct nfsd4_compoundres *resp, int nfserr,
 }
 
 static __be32
-nfsd4_encode_destroy_session(struct nfsd4_compoundres *resp, int nfserr,
+nfsd4_encode_destroy_session(struct nfsd4_compoundres *resp, __be32 nfserr,
                             struct nfsd4_destroy_session *destroy_session)
 {
        return nfserr;
 }
 
 static __be32
-nfsd4_encode_free_stateid(struct nfsd4_compoundres *resp, int nfserr,
+nfsd4_encode_free_stateid(struct nfsd4_compoundres *resp, __be32 nfserr,
                          struct nfsd4_free_stateid *free_stateid)
 {
        __be32 *p;
@@ -3371,13 +3397,13 @@ nfsd4_encode_free_stateid(struct nfsd4_compoundres *resp, int nfserr,
                return nfserr;
 
        RESERVE_SPACE(4);
-       WRITE32(nfserr);
+       *p++ = nfserr;
        ADJUST_ARGS();
        return nfserr;
 }
 
 static __be32
-nfsd4_encode_sequence(struct nfsd4_compoundres *resp, int nfserr,
+nfsd4_encode_sequence(struct nfsd4_compoundres *resp, __be32 nfserr,
                      struct nfsd4_sequence *seq)
 {
        __be32 *p;
@@ -3399,8 +3425,8 @@ nfsd4_encode_sequence(struct nfsd4_compoundres *resp, int nfserr,
        return 0;
 }
 
-__be32
-nfsd4_encode_test_stateid(struct nfsd4_compoundres *resp, int nfserr,
+static __be32
+nfsd4_encode_test_stateid(struct nfsd4_compoundres *resp, __be32 nfserr,
                          struct nfsd4_test_stateid *test_stateid)
 {
        struct nfsd4_test_stateid_id *stateid, *next;
@@ -3503,7 +3529,7 @@ static nfsd4_enc nfsd4_enc_ops[] = {
  * Our se_fmaxresp_cached will always be a multiple of PAGE_SIZE, and so
  * will be at least a page and will therefore hold the xdr_buf head.
  */
-int nfsd4_check_resp_size(struct nfsd4_compoundres *resp, u32 pad)
+__be32 nfsd4_check_resp_size(struct nfsd4_compoundres *resp, u32 pad)
 {
        struct xdr_buf *xb = &resp->rqstp->rq_res;
        struct nfsd4_session *session = NULL;
index 2c53be6d357957332478ed1d55d62c85cf9f26cc..c55298ed5772577e5afe3bd613c3e5a0df3b69dd 100644 (file)
@@ -127,7 +127,17 @@ static const struct file_operations transaction_ops = {
 
 static int exports_open(struct inode *inode, struct file *file)
 {
-       return seq_open(file, &nfs_exports_op);
+       int err;
+       struct seq_file *seq;
+       struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id);
+
+       err = seq_open(file, &nfs_exports_op);
+       if (err)
+               return err;
+
+       seq = file->private_data;
+       seq->private = nn->svc_export_cache;
+       return 0;
 }
 
 static const struct file_operations exports_operations = {
@@ -345,7 +355,7 @@ static ssize_t write_filehandle(struct file *file, char *buf, size_t size)
        if (!dom)
                return -ENOMEM;
 
-       len = exp_rootfh(dom, path, &fh,  maxsize);
+       len = exp_rootfh(&init_net, dom, path, &fh,  maxsize);
        auth_domain_put(dom);
        if (len)
                return len;
@@ -651,6 +661,7 @@ static ssize_t __write_ports_addfd(char *buf)
 {
        char *mesg = buf;
        int fd, err;
+       struct net *net = &init_net;
 
        err = get_int(&mesg, &fd);
        if (err != 0 || fd < 0)
@@ -662,6 +673,8 @@ static ssize_t __write_ports_addfd(char *buf)
 
        err = svc_addsock(nfsd_serv, fd, buf, SIMPLE_TRANSACTION_LIMIT);
        if (err < 0) {
+               if (nfsd_serv->sv_nrthreads == 1)
+                       svc_shutdown_net(nfsd_serv, net);
                svc_destroy(nfsd_serv);
                return err;
        }
@@ -699,6 +712,7 @@ static ssize_t __write_ports_addxprt(char *buf)
        char transport[16];
        struct svc_xprt *xprt;
        int port, err;
+       struct net *net = &init_net;
 
        if (sscanf(buf, "%15s %4u", transport, &port) != 2)
                return -EINVAL;
@@ -710,12 +724,12 @@ static ssize_t __write_ports_addxprt(char *buf)
        if (err != 0)
                return err;
 
-       err = svc_create_xprt(nfsd_serv, transport, &init_net,
+       err = svc_create_xprt(nfsd_serv, transport, net,
                                PF_INET, port, SVC_SOCK_ANONYMOUS);
        if (err < 0)
                goto out_err;
 
-       err = svc_create_xprt(nfsd_serv, transport, &init_net,
+       err = svc_create_xprt(nfsd_serv, transport, net,
                                PF_INET6, port, SVC_SOCK_ANONYMOUS);
        if (err < 0 && err != -EAFNOSUPPORT)
                goto out_close;
@@ -724,12 +738,14 @@ static ssize_t __write_ports_addxprt(char *buf)
        nfsd_serv->sv_nrthreads--;
        return 0;
 out_close:
-       xprt = svc_find_xprt(nfsd_serv, transport, &init_net, PF_INET, port);
+       xprt = svc_find_xprt(nfsd_serv, transport, net, PF_INET, port);
        if (xprt != NULL) {
                svc_close_xprt(xprt);
                svc_xprt_put(xprt);
        }
 out_err:
+       if (nfsd_serv->sv_nrthreads == 1)
+               svc_shutdown_net(nfsd_serv, net);
        svc_destroy(nfsd_serv);
        return err;
 }
@@ -1127,7 +1143,34 @@ static int create_proc_exports_entry(void)
 #endif
 
 int nfsd_net_id;
+
+static __net_init int nfsd_init_net(struct net *net)
+{
+       int retval;
+
+       retval = nfsd_export_init(net);
+       if (retval)
+               goto out_export_error;
+       retval = nfsd_idmap_init(net);
+       if (retval)
+               goto out_idmap_error;
+       return 0;
+
+out_idmap_error:
+       nfsd_export_shutdown(net);
+out_export_error:
+       return retval;
+}
+
+static __net_exit void nfsd_exit_net(struct net *net)
+{
+       nfsd_idmap_shutdown(net);
+       nfsd_export_shutdown(net);
+}
+
 static struct pernet_operations nfsd_net_ops = {
+       .init = nfsd_init_net,
+       .exit = nfsd_exit_net,
        .id   = &nfsd_net_id,
        .size = sizeof(struct nfsd_net),
 };
@@ -1154,16 +1197,10 @@ static int __init init_nfsd(void)
        retval = nfsd_reply_cache_init();
        if (retval)
                goto out_free_stat;
-       retval = nfsd_export_init();
-       if (retval)
-               goto out_free_cache;
        nfsd_lockd_init();      /* lockd->nfsd callbacks */
-       retval = nfsd_idmap_init();
-       if (retval)
-               goto out_free_lockd;
        retval = create_proc_exports_entry();
        if (retval)
-               goto out_free_idmap;
+               goto out_free_lockd;
        retval = register_filesystem(&nfsd_fs_type);
        if (retval)
                goto out_free_all;
@@ -1171,12 +1208,8 @@ static int __init init_nfsd(void)
 out_free_all:
        remove_proc_entry("fs/nfs/exports", NULL);
        remove_proc_entry("fs/nfs", NULL);
-out_free_idmap:
-       nfsd_idmap_shutdown();
 out_free_lockd:
        nfsd_lockd_shutdown();
-       nfsd_export_shutdown();
-out_free_cache:
        nfsd_reply_cache_shutdown();
 out_free_stat:
        nfsd_stat_shutdown();
@@ -1192,13 +1225,11 @@ out_unregister_notifier:
 
 static void __exit exit_nfsd(void)
 {
-       nfsd_export_shutdown();
        nfsd_reply_cache_shutdown();
        remove_proc_entry("fs/nfs/exports", NULL);
        remove_proc_entry("fs/nfs", NULL);
        nfsd_stat_shutdown();
        nfsd_lockd_shutdown();
-       nfsd_idmap_shutdown();
        nfsd4_free_slabs();
        nfsd_fault_inject_cleanup();
        unregister_filesystem(&nfsd_fs_type);
index 68454e75fce967b95bbb01158def83733de60976..cc793005a87cb4b5a79b7074c4861ca651085d1c 100644 (file)
@@ -636,7 +636,7 @@ fh_put(struct svc_fh *fhp)
 #endif
        }
        if (exp) {
-               cache_put(&exp->h, &svc_export_cache);
+               exp_put(exp);
                fhp->fh_export = NULL;
        }
        return;
index 28dfad39f0c50a626384c4363955e2b9d7e3212f..ee709fc8f58bc0b62a3f7ca64104630fe803b6d0 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/module.h>
 #include <linux/fs_struct.h>
 #include <linux/swap.h>
+#include <linux/nsproxy.h>
 
 #include <linux/sunrpc/stats.h>
 #include <linux/sunrpc/svcsock.h>
@@ -220,7 +221,7 @@ static int nfsd_startup(unsigned short port, int nrservs)
        ret = nfsd_init_socks(port);
        if (ret)
                goto out_racache;
-       ret = lockd_up();
+       ret = lockd_up(&init_net);
        if (ret)
                goto out_racache;
        ret = nfs4_state_start();
@@ -229,7 +230,7 @@ static int nfsd_startup(unsigned short port, int nrservs)
        nfsd_up = true;
        return 0;
 out_lockd:
-       lockd_down();
+       lockd_down(&init_net);
 out_racache:
        nfsd_racache_shutdown();
        return ret;
@@ -246,7 +247,7 @@ static void nfsd_shutdown(void)
        if (!nfsd_up)
                return;
        nfs4_state_shutdown();
-       lockd_down();
+       lockd_down(&init_net);
        nfsd_racache_shutdown();
        nfsd_up = false;
 }
@@ -261,7 +262,7 @@ static void nfsd_last_thread(struct svc_serv *serv, struct net *net)
 
        printk(KERN_WARNING "nfsd: last server has exited, flushing export "
                            "cache\n");
-       nfsd_export_flush();
+       nfsd_export_flush(net);
 }
 
 void nfsd_reset_versions(void)
@@ -330,6 +331,8 @@ static int nfsd_get_default_max_blksize(void)
 
 int nfsd_create_serv(void)
 {
+       int error;
+
        WARN_ON(!mutex_is_locked(&nfsd_mutex));
        if (nfsd_serv) {
                svc_get(nfsd_serv);
@@ -343,6 +346,12 @@ int nfsd_create_serv(void)
        if (nfsd_serv == NULL)
                return -ENOMEM;
 
+       error = svc_bind(nfsd_serv, current->nsproxy->net_ns);
+       if (error < 0) {
+               svc_destroy(nfsd_serv);
+               return error;
+       }
+
        set_max_drc();
        do_gettimeofday(&nfssvc_boot);          /* record boot time */
        return 0;
@@ -373,6 +382,7 @@ int nfsd_set_nrthreads(int n, int *nthreads)
        int i = 0;
        int tot = 0;
        int err = 0;
+       struct net *net = &init_net;
 
        WARN_ON(!mutex_is_locked(&nfsd_mutex));
 
@@ -417,6 +427,9 @@ int nfsd_set_nrthreads(int n, int *nthreads)
                if (err)
                        break;
        }
+
+       if (nfsd_serv->sv_nrthreads == 1)
+               svc_shutdown_net(nfsd_serv, net);
        svc_destroy(nfsd_serv);
 
        return err;
@@ -432,6 +445,7 @@ nfsd_svc(unsigned short port, int nrservs)
 {
        int     error;
        bool    nfsd_up_before;
+       struct net *net = &init_net;
 
        mutex_lock(&nfsd_mutex);
        dprintk("nfsd: creating service\n");
@@ -464,6 +478,8 @@ out_shutdown:
        if (error < 0 && !nfsd_up_before)
                nfsd_shutdown();
 out_destroy:
+       if (nfsd_serv->sv_nrthreads == 1)
+               svc_shutdown_net(nfsd_serv, net);
        svc_destroy(nfsd_serv);         /* Release server */
 out:
        mutex_unlock(&nfsd_mutex);
@@ -547,6 +563,9 @@ nfsd(void *vrqstp)
        nfsdstats.th_cnt --;
 
 out:
+       if (rqstp->rq_server->sv_nrthreads == 1)
+               svc_shutdown_net(rqstp->rq_server, &init_net);
+
        /* Release the thread */
        svc_exit_thread(rqstp);
 
@@ -659,8 +678,12 @@ int nfsd_pool_stats_open(struct inode *inode, struct file *file)
 int nfsd_pool_stats_release(struct inode *inode, struct file *file)
 {
        int ret = seq_release(inode, file);
+       struct net *net = &init_net;
+
        mutex_lock(&nfsd_mutex);
        /* this function really, really should have been called svc_put() */
+       if (nfsd_serv->sv_nrthreads == 1)
+               svc_shutdown_net(nfsd_serv, net);
        svc_destroy(nfsd_serv);
        mutex_unlock(&nfsd_mutex);
        return ret;
index 89ab137d379a3f6756b5b5616083862e8f22d88f..849091e16ea6afd43e4ddd2dbd17962fdd87ad85 100644 (file)
@@ -232,7 +232,6 @@ struct nfs4_client {
        time_t                  cl_time;        /* time of last lease renewal */
        struct sockaddr_storage cl_addr;        /* client ipaddress */
        u32                     cl_flavor;      /* setclientid pseudoflavor */
-       char                    *cl_principal;  /* setclientid principal name */
        struct svc_cred         cl_cred;        /* setclientid principal */
        clientid_t              cl_clientid;    /* generated by server */
        nfs4_verifier           cl_confirm;     /* generated by server */
index 568666156ea4f59525d67207551ee8c45a3b730e..c8bd9c3be7f747410622fd1172b2c7243886f838 100644 (file)
@@ -2039,7 +2039,7 @@ nfsd_readdir(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t *offsetp,
        if (err)
                goto out;
 
-       offset = vfs_llseek(file, offset, 0);
+       offset = vfs_llseek(file, offset, SEEK_SET);
        if (offset < 0) {
                err = nfserrno((int)offset);
                goto out_close;
index 1b3501598ab5dbb4609ba19e4f7c3322b29f70ba..acd127d4ee821660e71fe1e38ef1c804962f6508 100644 (file)
@@ -60,7 +60,7 @@ struct nfsd4_compound_state {
        __be32                  *datap;
        size_t                  iovlen;
        u32                     minorversion;
-       u32                     status;
+       __be32                  status;
        stateid_t       current_stateid;
        stateid_t       save_stateid;
        /* to indicate current and saved state id presents */
@@ -364,7 +364,7 @@ struct nfsd4_test_stateid_id {
 };
 
 struct nfsd4_test_stateid {
-       __be32          ts_num_ids;
+       u32             ts_num_ids;
        struct list_head ts_stateid_list;
 };
 
@@ -549,7 +549,7 @@ int nfs4svc_decode_compoundargs(struct svc_rqst *, __be32 *,
                struct nfsd4_compoundargs *);
 int nfs4svc_encode_compoundres(struct svc_rqst *, __be32 *,
                struct nfsd4_compoundres *);
-int nfsd4_check_resp_size(struct nfsd4_compoundres *, u32);
+__be32 nfsd4_check_resp_size(struct nfsd4_compoundres *, u32);
 void nfsd4_encode_operation(struct nfsd4_compoundres *, struct nfsd4_op *);
 void nfsd4_encode_replay(struct nfsd4_compoundres *resp, struct nfsd4_op *op);
 __be32 nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
index 26601529dc17c7ff9f5bc7d4c249a59371cd14c4..62cebc8e1a1fd49ceec5684de547bd8115eedac2 100644 (file)
@@ -37,6 +37,7 @@ int nilfs_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
         * This function should be implemented when the writeback function
         * will be implemented.
         */
+       struct the_nilfs *nilfs;
        struct inode *inode = file->f_mapping->host;
        int err;
 
@@ -45,18 +46,21 @@ int nilfs_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
                return err;
        mutex_lock(&inode->i_mutex);
 
-       if (!nilfs_inode_dirty(inode)) {
-               mutex_unlock(&inode->i_mutex);
-               return 0;
+       if (nilfs_inode_dirty(inode)) {
+               if (datasync)
+                       err = nilfs_construct_dsync_segment(inode->i_sb, inode,
+                                                           0, LLONG_MAX);
+               else
+                       err = nilfs_construct_segment(inode->i_sb);
        }
-
-       if (datasync)
-               err = nilfs_construct_dsync_segment(inode->i_sb, inode, 0,
-                                                   LLONG_MAX);
-       else
-               err = nilfs_construct_segment(inode->i_sb);
-
        mutex_unlock(&inode->i_mutex);
+
+       nilfs = inode->i_sb->s_fs_info;
+       if (!err && nilfs_test_opt(nilfs, BARRIER)) {
+               err = blkdev_issue_flush(inode->i_sb->s_bdev, GFP_KERNEL, NULL);
+               if (err != -EIO)
+                       err = 0;
+       }
        return err;
 }
 
index 2a70fce70c65be1151783e3aba3c221e39642ba7..06658caa18bd229ab42e01b876538efaf2c48882 100644 (file)
@@ -692,8 +692,14 @@ static int nilfs_ioctl_sync(struct inode *inode, struct file *filp,
        if (ret < 0)
                return ret;
 
+       nilfs = inode->i_sb->s_fs_info;
+       if (nilfs_test_opt(nilfs, BARRIER)) {
+               ret = blkdev_issue_flush(inode->i_sb->s_bdev, GFP_KERNEL, NULL);
+               if (ret == -EIO)
+                       return ret;
+       }
+
        if (argp != NULL) {
-               nilfs = inode->i_sb->s_fs_info;
                down_read(&nilfs->ns_segctor_sem);
                cno = nilfs->ns_cno - 1;
                up_read(&nilfs->ns_segctor_sem);
index 0bb2c2010b9512ba5fd971fdbdc34eba2abab886..b72847988b78d96d99b7571d17fea769e463c6b0 100644 (file)
@@ -508,31 +508,29 @@ static struct dentry *nilfs_fh_to_parent(struct super_block *sb, struct fid *fh,
        return nilfs_get_dentry(sb, fid->cno, fid->parent_ino, fid->parent_gen);
 }
 
-static int nilfs_encode_fh(struct dentry *dentry, __u32 *fh, int *lenp,
-                          int connectable)
+static int nilfs_encode_fh(struct inode *inode, __u32 *fh, int *lenp,
+                          struct inode *parent)
 {
        struct nilfs_fid *fid = (struct nilfs_fid *)fh;
-       struct inode *inode = dentry->d_inode;
        struct nilfs_root *root = NILFS_I(inode)->i_root;
        int type;
 
-       if (*lenp < NILFS_FID_SIZE_NON_CONNECTABLE ||
-           (connectable && *lenp < NILFS_FID_SIZE_CONNECTABLE))
+       if (parent && *lenp < NILFS_FID_SIZE_CONNECTABLE) {
+               *lenp = NILFS_FID_SIZE_CONNECTABLE;
+               return 255;
+       }
+       if (*lenp < NILFS_FID_SIZE_NON_CONNECTABLE) {
+               *lenp = NILFS_FID_SIZE_NON_CONNECTABLE;
                return 255;
+       }
 
        fid->cno = root->cno;
        fid->ino = inode->i_ino;
        fid->gen = inode->i_generation;
 
-       if (connectable && !S_ISDIR(inode->i_mode)) {
-               struct inode *parent;
-
-               spin_lock(&dentry->d_lock);
-               parent = dentry->d_parent->d_inode;
+       if (parent) {
                fid->parent_ino = parent->i_ino;
                fid->parent_gen = parent->i_generation;
-               spin_unlock(&dentry->d_lock);
-
                type = FILEID_NILFS_WITH_PARENT;
                *lenp = NILFS_FID_SIZE_CONNECTABLE;
        } else {
index a39edc41becc29e76c67b366de2032ad3314268a..b5eac98fd7bdbd7f733d5fd998fee1cb53c6ecf1 100644 (file)
@@ -30,7 +30,7 @@ config NLS_DEFAULT
          cp949, cp950, cp1251, cp1255, euc-jp, euc-kr, gb2312, iso8859-1,
          iso8859-2, iso8859-3, iso8859-4, iso8859-5, iso8859-6, iso8859-7,
          iso8859-8, iso8859-9, iso8859-13, iso8859-14, iso8859-15,
-         koi8-r, koi8-ru, koi8-u, sjis, tis-620, utf8.
+         koi8-r, koi8-ru, koi8-u, sjis, tis-620, macroman, utf8.
          If you specify a wrong value, it will use the built-in NLS;
          compatible with iso8859-1.
 
@@ -452,6 +452,161 @@ config NLS_KOI8_U
          input/output character sets. Say Y here for the preferred Ukrainian
          (koi8-u) and Belarusian (koi8-ru) character sets.
 
+config NLS_CODEPAGE_MACROMAN
+       tristate "Codepage macroman"
+       ---help---
+         The Apple HFS file system family can deal with filenames in
+         native language character sets. These character sets are stored in
+         so-called MAC codepages. You need to include the appropriate
+         codepage if you want to be able to read/write these filenames on
+         Mac partitions correctly. This does apply to the filenames
+         only, not to the file contents. You can include several codepages;
+         say Y here if you want to include the Mac codepage that is used for
+         much of Europe -- United Kingdom, Germany, Spain, Italy, and [add
+         more countries here].
+
+         If unsure, say Y.
+
+config NLS_CODEPAGE_MACCELTIC
+       tristate "Codepage macceltic"
+       ---help---
+         The Apple HFS file system family can deal with filenames in
+         native language character sets. These character sets are stored in
+         so-called MAC codepages. You need to include the appropriate
+         codepage if you want to be able to read/write these filenames on
+         Mac partitions correctly. This does apply to the filenames
+         only, not to the file contents. You can include several codepages;
+         say Y here if you want to include the Mac codepage that is used for
+         Celtic.
+
+         If unsure, say Y.
+
+config NLS_CODEPAGE_MACCENTEURO
+       tristate "Codepage maccenteuro"
+       ---help---
+         The Apple HFS file system family can deal with filenames in
+         native language character sets. These character sets are stored in
+         so-called MAC codepages. You need to include the appropriate
+         codepage if you want to be able to read/write these filenames on
+         Mac partitions correctly. This does apply to the filenames
+         only, not to the file contents. You can include several codepages;
+         say Y here if you want to include the Mac codepage that is used for
+         Central Europe.
+
+         If unsure, say Y.
+
+config NLS_CODEPAGE_MACCROATIAN
+       tristate "Codepage maccroatian"
+       ---help---
+         The Apple HFS file system family can deal with filenames in
+         native language character sets. These character sets are stored in
+         so-called MAC codepages. You need to include the appropriate
+         codepage if you want to be able to read/write these filenames on
+         Mac partitions correctly. This does apply to the filenames
+         only, not to the file contents. You can include several codepages;
+         say Y here if you want to include the Mac codepage that is used for
+         Croatian.
+
+         If unsure, say Y.
+
+config NLS_CODEPAGE_MACCYRILLIC
+       tristate "Codepage maccyrillic"
+       ---help---
+         The Apple HFS file system family can deal with filenames in
+         native language character sets. These character sets are stored in
+         so-called MAC codepages. You need to include the appropriate
+         codepage if you want to be able to read/write these filenames on
+         Mac partitions correctly. This does apply to the filenames
+         only, not to the file contents. You can include several codepages;
+         say Y here if you want to include the Mac codepage that is used for
+         Cyrillic.
+
+         If unsure, say Y.
+
+config NLS_CODEPAGE_MACGAELIC
+       tristate "Codepage macgaelic"
+       ---help---
+         The Apple HFS file system family can deal with filenames in
+         native language character sets. These character sets are stored in
+         so-called MAC codepages. You need to include the appropriate
+         codepage if you want to be able to read/write these filenames on
+         Mac partitions correctly. This does apply to the filenames
+         only, not to the file contents. You can include several codepages;
+         say Y here if you want to include the Mac codepage that is used for
+         Gaelic.
+
+         If unsure, say Y.
+
+config NLS_CODEPAGE_MACGREEK
+       tristate "Codepage macgreek"
+       ---help---
+         The Apple HFS file system family can deal with filenames in
+         native language character sets. These character sets are stored in
+         so-called MAC codepages. You need to include the appropriate
+         codepage if you want to be able to read/write these filenames on
+         Mac partitions correctly. This does apply to the filenames
+         only, not to the file contents. You can include several codepages;
+         say Y here if you want to include the Mac codepage that is used for
+         Greek.
+
+         If unsure, say Y.
+
+config NLS_CODEPAGE_MACICELAND
+       tristate "Codepage maciceland"
+       ---help---
+         The Apple HFS file system family can deal with filenames in
+         native language character sets. These character sets are stored in
+         so-called MAC codepages. You need to include the appropriate
+         codepage if you want to be able to read/write these filenames on
+         Mac partitions correctly. This does apply to the filenames
+         only, not to the file contents. You can include several codepages;
+         say Y here if you want to include the Mac codepage that is used for
+         Iceland.
+
+         If unsure, say Y.
+
+config NLS_CODEPAGE_MACINUIT
+       tristate "Codepage macinuit"
+       ---help---
+         The Apple HFS file system family can deal with filenames in
+         native language character sets. These character sets are stored in
+         so-called MAC codepages. You need to include the appropriate
+         codepage if you want to be able to read/write these filenames on
+         Mac partitions correctly. This does apply to the filenames
+         only, not to the file contents. You can include several codepages;
+         say Y here if you want to include the Mac codepage that is used for
+         Inuit.
+
+         If unsure, say Y.
+
+config NLS_CODEPAGE_MACROMANIAN
+       tristate "Codepage macromanian"
+       ---help---
+         The Apple HFS file system family can deal with filenames in
+         native language character sets. These character sets are stored in
+         so-called MAC codepages. You need to include the appropriate
+         codepage if you want to be able to read/write these filenames on
+         Mac partitions correctly. This does apply to the filenames
+         only, not to the file contents. You can include several codepages;
+         say Y here if you want to include the Mac codepage that is used for
+         Romanian.
+
+         If unsure, say Y.
+
+config NLS_CODEPAGE_MACTURKISH
+       tristate "Codepage macturkish"
+       ---help---
+         The Apple HFS file system family can deal with filenames in
+         native language character sets. These character sets are stored in
+         so-called MAC codepages. You need to include the appropriate
+         codepage if you want to be able to read/write these filenames on
+         Mac partitions correctly. This does apply to the filenames
+         only, not to the file contents. You can include several codepages;
+         say Y here if you want to include the Mac codepage that is used for
+         Turkish.
+
+         If unsure, say Y.
+
 config NLS_UTF8
        tristate "NLS UTF-8"
        help
index f499dd7c3905bfcee723ab86bf231b13dc27300a..b6b0550a7c8031c655664b396590c02cb2eb4364 100644 (file)
@@ -2,6 +2,18 @@
 # Makefile for native language support
 #
 
+CONFIG_NLS_MACCELTIC=m
+CONFIG_NLS_MACCENTEURO=m
+CONFIG_NLS_MACCROATIAN=m
+CONFIG_NLS_MACCYRILLIC=m
+CONFIG_NLS_MACGAELIC=m
+CONFIG_NLS_MACGREEK=m
+CONFIG_NLS_MACICELAND=m
+CONFIG_NLS_MACINUIT=m
+CONFIG_NLS_MACROMANIAN=m
+CONFIG_NLS_MACROMAN=m
+CONFIG_NLS_MACTURKISH=m
+
 obj-$(CONFIG_NLS)              += nls_base.o
 
 obj-$(CONFIG_NLS_CODEPAGE_437) += nls_cp437.o
@@ -42,3 +54,14 @@ obj-$(CONFIG_NLS_ISO8859_15) += nls_iso8859-15.o
 obj-$(CONFIG_NLS_KOI8_R)       += nls_koi8-r.o
 obj-$(CONFIG_NLS_KOI8_U)       += nls_koi8-u.o nls_koi8-ru.o
 obj-$(CONFIG_NLS_UTF8)         += nls_utf8.o
+obj-$(CONFIG_NLS_MACCELTIC)     += nls_macceltic.o
+obj-$(CONFIG_NLS_MACCENTEURO)   += nls_maccenteuro.o
+obj-$(CONFIG_NLS_MACCROATIAN)   += nls_maccroatian.o
+obj-$(CONFIG_NLS_MACCYRILLIC)   += nls_maccyrillic.o
+obj-$(CONFIG_NLS_MACGAELIC)     += nls_macgaelic.o
+obj-$(CONFIG_NLS_MACGREEK)      += nls_macgreek.o
+obj-$(CONFIG_NLS_MACICELAND)    += nls_maciceland.o
+obj-$(CONFIG_NLS_MACINUIT)      += nls_macinuit.o
+obj-$(CONFIG_NLS_MACROMANIAN)   += nls_macromanian.o
+obj-$(CONFIG_NLS_MACROMAN)      += nls_macroman.o
+obj-$(CONFIG_NLS_MACTURKISH)    += nls_macturkish.o
diff --git a/fs/nls/nls_macceltic.c b/fs/nls/nls_macceltic.c
new file mode 100644 (file)
index 0000000..95ac5b4
--- /dev/null
@@ -0,0 +1,602 @@
+/*
+ * linux/fs/nls/nls_macceltic.c
+ *
+ * Charset macceltic translation tables.
+ * Generated automatically from the Unicode and charset
+ * tables from the Unicode Organization (www.unicode.org).
+ * The Unicode to charset table has only exact mappings.
+ */
+
+/*
+ * COPYRIGHT AND PERMISSION NOTICE
+ *
+ * Copyright 1991-2012 Unicode, Inc.  All rights reserved.  Distributed under
+ * the Terms of Use in http://www.unicode.org/copyright.html.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of the Unicode data files and any associated documentation (the "Data
+ * Files") or Unicode software and any associated documentation (the
+ * "Software") to deal in the Data Files or Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, and/or sell copies of the Data Files or Software, and
+ * to permit persons to whom the Data Files or Software are furnished to do
+ * so, provided that (a) the above copyright notice(s) and this permission
+ * notice appear with all copies of the Data Files or Software, (b) both the
+ * above copyright notice(s) and this permission notice appear in associated
+ * documentation, and (c) there is clear notice in each modified Data File or
+ * in the Software as well as in the documentation associated with the Data
+ * File(s) or Software that the data or software has been modified.
+ *
+ * THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ * KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
+ * THIRD PARTY RIGHTS.  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
+ * INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
+ * OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
+ * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THE DATA FILES OR SOFTWARE.
+ *
+ * Except as contained in this notice, the name of a copyright holder shall
+ * not be used in advertising or otherwise to promote the sale, use or other
+ * dealings in these Data Files or Software without prior written
+ * authorization of the copyright holder.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/nls.h>
+#include <linux/errno.h>
+
+static const wchar_t charset2uni[256] = {
+       /* 0x00 */
+       0x0000, 0x0001, 0x0002, 0x0003,
+       0x0004, 0x0005, 0x0006, 0x0007,
+       0x0008, 0x0009, 0x000a, 0x000b,
+       0x000c, 0x000d, 0x000e, 0x000f,
+       /* 0x10 */
+       0x0010, 0x0011, 0x0012, 0x0013,
+       0x0014, 0x0015, 0x0016, 0x0017,
+       0x0018, 0x0019, 0x001a, 0x001b,
+       0x001c, 0x001d, 0x001e, 0x001f,
+       /* 0x20 */
+       0x0020, 0x0021, 0x0022, 0x0023,
+       0x0024, 0x0025, 0x0026, 0x0027,
+       0x0028, 0x0029, 0x002a, 0x002b,
+       0x002c, 0x002d, 0x002e, 0x002f,
+       /* 0x30 */
+       0x0030, 0x0031, 0x0032, 0x0033,
+       0x0034, 0x0035, 0x0036, 0x0037,
+       0x0038, 0x0039, 0x003a, 0x003b,
+       0x003c, 0x003d, 0x003e, 0x003f,
+       /* 0x40 */
+       0x0040, 0x0041, 0x0042, 0x0043,
+       0x0044, 0x0045, 0x0046, 0x0047,
+       0x0048, 0x0049, 0x004a, 0x004b,
+       0x004c, 0x004d, 0x004e, 0x004f,
+       /* 0x50 */
+       0x0050, 0x0051, 0x0052, 0x0053,
+       0x0054, 0x0055, 0x0056, 0x0057,
+       0x0058, 0x0059, 0x005a, 0x005b,
+       0x005c, 0x005d, 0x005e, 0x005f,
+       /* 0x60 */
+       0x0060, 0x0061, 0x0062, 0x0063,
+       0x0064, 0x0065, 0x0066, 0x0067,
+       0x0068, 0x0069, 0x006a, 0x006b,
+       0x006c, 0x006d, 0x006e, 0x006f,
+       /* 0x70 */
+       0x0070, 0x0071, 0x0072, 0x0073,
+       0x0074, 0x0075, 0x0076, 0x0077,
+       0x0078, 0x0079, 0x007a, 0x007b,
+       0x007c, 0x007d, 0x007e, 0x007f,
+       /* 0x80 */
+       0x00c4, 0x00c5, 0x00c7, 0x00c9,
+       0x00d1, 0x00d6, 0x00dc, 0x00e1,
+       0x00e0, 0x00e2, 0x00e4, 0x00e3,
+       0x00e5, 0x00e7, 0x00e9, 0x00e8,
+       /* 0x90 */
+       0x00ea, 0x00eb, 0x00ed, 0x00ec,
+       0x00ee, 0x00ef, 0x00f1, 0x00f3,
+       0x00f2, 0x00f4, 0x00f6, 0x00f5,
+       0x00fa, 0x00f9, 0x00fb, 0x00fc,
+       /* 0xa0 */
+       0x2020, 0x00b0, 0x00a2, 0x00a3,
+       0x00a7, 0x2022, 0x00b6, 0x00df,
+       0x00ae, 0x00a9, 0x2122, 0x00b4,
+       0x00a8, 0x2260, 0x00c6, 0x00d8,
+       /* 0xb0 */
+       0x221e, 0x00b1, 0x2264, 0x2265,
+       0x00a5, 0x00b5, 0x2202, 0x2211,
+       0x220f, 0x03c0, 0x222b, 0x00aa,
+       0x00ba, 0x03a9, 0x00e6, 0x00f8,
+       /* 0xc0 */
+       0x00bf, 0x00a1, 0x00ac, 0x221a,
+       0x0192, 0x2248, 0x2206, 0x00ab,
+       0x00bb, 0x2026, 0x00a0, 0x00c0,
+       0x00c3, 0x00d5, 0x0152, 0x0153,
+       /* 0xd0 */
+       0x2013, 0x2014, 0x201c, 0x201d,
+       0x2018, 0x2019, 0x00f7, 0x25ca,
+       0x00ff, 0x0178, 0x2044, 0x20ac,
+       0x2039, 0x203a, 0x0176, 0x0177,
+       /* 0xe0 */
+       0x2021, 0x00b7, 0x1ef2, 0x1ef3,
+       0x2030, 0x00c2, 0x00ca, 0x00c1,
+       0x00cb, 0x00c8, 0x00cd, 0x00ce,
+       0x00cf, 0x00cc, 0x00d3, 0x00d4,
+       /* 0xf0 */
+       0x2663, 0x00d2, 0x00da, 0x00db,
+       0x00d9, 0x0131, 0x00dd, 0x00fd,
+       0x0174, 0x0175, 0x1e84, 0x1e85,
+       0x1e80, 0x1e81, 0x1e82, 0x1e83,
+};
+
+static const unsigned char page00[256] = {
+       0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+       0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+       0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+       0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+       0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+       0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+       0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+       0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+       0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+       0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+       0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+       0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+       0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+       0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+       0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+       0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0xca, 0xc1, 0xa2, 0xa3, 0x00, 0xb4, 0x00, 0xa4, /* 0xa0-0xa7 */
+       0xac, 0xa9, 0xbb, 0xc7, 0xc2, 0x00, 0xa8, 0x00, /* 0xa8-0xaf */
+       0xa1, 0xb1, 0x00, 0x00, 0xab, 0xb5, 0xa6, 0xe1, /* 0xb0-0xb7 */
+       0x00, 0x00, 0xbc, 0xc8, 0x00, 0x00, 0x00, 0xc0, /* 0xb8-0xbf */
+       0xcb, 0xe7, 0xe5, 0xcc, 0x80, 0x81, 0xae, 0x82, /* 0xc0-0xc7 */
+       0xe9, 0x83, 0xe6, 0xe8, 0xed, 0xea, 0xeb, 0xec, /* 0xc8-0xcf */
+       0x00, 0x84, 0xf1, 0xee, 0xef, 0xcd, 0x85, 0x00, /* 0xd0-0xd7 */
+       0xaf, 0xf4, 0xf2, 0xf3, 0x86, 0xf6, 0x00, 0xa7, /* 0xd8-0xdf */
+       0x88, 0x87, 0x89, 0x8b, 0x8a, 0x8c, 0xbe, 0x8d, /* 0xe0-0xe7 */
+       0x8f, 0x8e, 0x90, 0x91, 0x93, 0x92, 0x94, 0x95, /* 0xe8-0xef */
+       0x00, 0x96, 0x98, 0x97, 0x99, 0x9b, 0x9a, 0xd6, /* 0xf0-0xf7 */
+       0xbf, 0x9d, 0x9c, 0x9e, 0x9f, 0xf7, 0x00, 0xd8, /* 0xf8-0xff */
+};
+
+static const unsigned char page01[256] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0x00, 0xf5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0x00, 0x00, 0xce, 0xcf, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0xf8, 0xf9, 0xde, 0xdf, /* 0x70-0x77 */
+       0xd9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0xc4, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char page03[256] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0xbd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0xb9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char page1e[256] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+       0xfc, 0xfd, 0xfe, 0xff, 0xfa, 0xfb, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0xe2, 0xe3, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char page20[256] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+       0x00, 0x00, 0x00, 0xd0, 0xd1, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+       0xd4, 0xd5, 0x00, 0x00, 0xd2, 0xd3, 0x00, 0x00, /* 0x18-0x1f */
+       0xa0, 0xe0, 0xa5, 0x00, 0x00, 0x00, 0xc9, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0xe4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0xdc, 0xdd, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0x00, 0x00, 0x00, 0x00, 0xda, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0x00, 0x00, 0x00, 0xdb, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char page21[256] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+       0x00, 0x00, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char page22[256] = {
+       0x00, 0x00, 0xb6, 0x00, 0x00, 0x00, 0xc6, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb8, /* 0x08-0x0f */
+       0x00, 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+       0x00, 0x00, 0xc3, 0x00, 0x00, 0x00, 0xb0, 0x00, /* 0x18-0x1f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0xba, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0xc5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0xad, 0x00, 0x00, 0x00, 0xb2, 0xb3, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char page25[256] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+       0x00, 0x00, 0xd7, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char page26[256] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char *const page_uni2charset[256] = {
+       page00, page01, NULL,   page03, NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   page1e, NULL,
+       page20, page21, page22, NULL,   NULL,   page25, page26, NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+};
+
+static const unsigned char charset2lower[256] = {
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x00-0x07 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x08-0x0f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x10-0x17 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x18-0x1f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x20-0x27 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x28-0x2f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x30-0x37 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x38-0x3f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x40-0x47 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x48-0x4f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x50-0x57 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x58-0x5f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x60-0x67 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x68-0x6f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x70-0x77 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x78-0x7f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x80-0x87 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x88-0x8f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x90-0x97 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x98-0x9f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xa0-0xa7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xa8-0xaf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xb0-0xb7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xb8-0xbf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xc0-0xc7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xc8-0xcf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xd0-0xd7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xd8-0xdf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xe0-0xe7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xe8-0xef */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xf0-0xf7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xf8-0xff */
+};
+
+static const unsigned char charset2upper[256] = {
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0x00-0x07 */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0x08-0x0f */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0x10-0x17 */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0x18-0x1f */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0x20-0x27 */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0x28-0x2f */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0x30-0x37 */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0x38-0x3f */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0x40-0x47 */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0x48-0x4f */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0x50-0x57 */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0x58-0x5f */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0x60-0x67 */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0x68-0x6f */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0x70-0x77 */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0x78-0x7f */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0x80-0x87 */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0x88-0x8f */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0x90-0x97 */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0x98-0x9f */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0xa0-0xa7 */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0xa8-0xaf */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0xb0-0xb7 */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0xb8-0xbf */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0xc0-0xc7 */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0xc8-0xcf */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0xd0-0xd7 */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0xd8-0xdf */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0xe0-0xe7 */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0xe8-0xef */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0xf0-0xf7 */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0xf8-0xff */
+};
+
+static int uni2char(wchar_t uni, unsigned char *out, int boundlen)
+{
+       const unsigned char *uni2charset;
+       unsigned char cl = uni & 0x00ff;
+       unsigned char ch = (uni & 0xff00) >> 8;
+
+       if (boundlen <= 0)
+               return -ENAMETOOLONG;
+
+       uni2charset = page_uni2charset[ch];
+       if (uni2charset && uni2charset[cl])
+               out[0] = uni2charset[cl];
+       else
+               return -EINVAL;
+       return 1;
+}
+
+static int char2uni(const unsigned char *rawstring, int boundlen, wchar_t *uni)
+{
+       *uni = charset2uni[*rawstring];
+       if (*uni == 0x0000)
+               return -EINVAL;
+       return 1;
+}
+
+static struct nls_table table = {
+       .charset        = "macceltic",
+       .uni2char       = uni2char,
+       .char2uni       = char2uni,
+       .charset2lower  = charset2lower,
+       .charset2upper  = charset2upper,
+       .owner          = THIS_MODULE,
+};
+
+static int __init init_nls_macceltic(void)
+{
+       return register_nls(&table);
+}
+
+static void __exit exit_nls_macceltic(void)
+{
+       unregister_nls(&table);
+}
+
+module_init(init_nls_macceltic)
+module_exit(exit_nls_macceltic)
+
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/fs/nls/nls_maccenteuro.c b/fs/nls/nls_maccenteuro.c
new file mode 100644 (file)
index 0000000..ce0d57e
--- /dev/null
@@ -0,0 +1,532 @@
+/*
+ * linux/fs/nls/nls_maccenteuro.c
+ *
+ * Charset maccenteuro translation tables.
+ * Generated automatically from the Unicode and charset
+ * tables from the Unicode Organization (www.unicode.org).
+ * The Unicode to charset table has only exact mappings.
+ */
+
+/*
+ * COPYRIGHT AND PERMISSION NOTICE
+ *
+ * Copyright 1991-2012 Unicode, Inc.  All rights reserved.  Distributed under
+ * the Terms of Use in http://www.unicode.org/copyright.html.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of the Unicode data files and any associated documentation (the "Data
+ * Files") or Unicode software and any associated documentation (the
+ * "Software") to deal in the Data Files or Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, and/or sell copies of the Data Files or Software, and
+ * to permit persons to whom the Data Files or Software are furnished to do
+ * so, provided that (a) the above copyright notice(s) and this permission
+ * notice appear with all copies of the Data Files or Software, (b) both the
+ * above copyright notice(s) and this permission notice appear in associated
+ * documentation, and (c) there is clear notice in each modified Data File or
+ * in the Software as well as in the documentation associated with the Data
+ * File(s) or Software that the data or software has been modified.
+ *
+ * THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ * KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
+ * THIRD PARTY RIGHTS.  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
+ * INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
+ * OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
+ * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THE DATA FILES OR SOFTWARE.
+ *
+ * Except as contained in this notice, the name of a copyright holder shall
+ * not be used in advertising or otherwise to promote the sale, use or other
+ * dealings in these Data Files or Software without prior written
+ * authorization of the copyright holder.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/nls.h>
+#include <linux/errno.h>
+
+static const wchar_t charset2uni[256] = {
+       /* 0x00 */
+       0x0000, 0x0001, 0x0002, 0x0003,
+       0x0004, 0x0005, 0x0006, 0x0007,
+       0x0008, 0x0009, 0x000a, 0x000b,
+       0x000c, 0x000d, 0x000e, 0x000f,
+       /* 0x10 */
+       0x0010, 0x0011, 0x0012, 0x0013,
+       0x0014, 0x0015, 0x0016, 0x0017,
+       0x0018, 0x0019, 0x001a, 0x001b,
+       0x001c, 0x001d, 0x001e, 0x001f,
+       /* 0x20 */
+       0x0020, 0x0021, 0x0022, 0x0023,
+       0x0024, 0x0025, 0x0026, 0x0027,
+       0x0028, 0x0029, 0x002a, 0x002b,
+       0x002c, 0x002d, 0x002e, 0x002f,
+       /* 0x30 */
+       0x0030, 0x0031, 0x0032, 0x0033,
+       0x0034, 0x0035, 0x0036, 0x0037,
+       0x0038, 0x0039, 0x003a, 0x003b,
+       0x003c, 0x003d, 0x003e, 0x003f,
+       /* 0x40 */
+       0x0040, 0x0041, 0x0042, 0x0043,
+       0x0044, 0x0045, 0x0046, 0x0047,
+       0x0048, 0x0049, 0x004a, 0x004b,
+       0x004c, 0x004d, 0x004e, 0x004f,
+       /* 0x50 */
+       0x0050, 0x0051, 0x0052, 0x0053,
+       0x0054, 0x0055, 0x0056, 0x0057,
+       0x0058, 0x0059, 0x005a, 0x005b,
+       0x005c, 0x005d, 0x005e, 0x005f,
+       /* 0x60 */
+       0x0060, 0x0061, 0x0062, 0x0063,
+       0x0064, 0x0065, 0x0066, 0x0067,
+       0x0068, 0x0069, 0x006a, 0x006b,
+       0x006c, 0x006d, 0x006e, 0x006f,
+       /* 0x70 */
+       0x0070, 0x0071, 0x0072, 0x0073,
+       0x0074, 0x0075, 0x0076, 0x0077,
+       0x0078, 0x0079, 0x007a, 0x007b,
+       0x007c, 0x007d, 0x007e, 0x007f,
+       /* 0x80 */
+       0x00c4, 0x0100, 0x0101, 0x00c9,
+       0x0104, 0x00d6, 0x00dc, 0x00e1,
+       0x0105, 0x010c, 0x00e4, 0x010d,
+       0x0106, 0x0107, 0x00e9, 0x0179,
+       /* 0x90 */
+       0x017a, 0x010e, 0x00ed, 0x010f,
+       0x0112, 0x0113, 0x0116, 0x00f3,
+       0x0117, 0x00f4, 0x00f6, 0x00f5,
+       0x00fa, 0x011a, 0x011b, 0x00fc,
+       /* 0xa0 */
+       0x2020, 0x00b0, 0x0118, 0x00a3,
+       0x00a7, 0x2022, 0x00b6, 0x00df,
+       0x00ae, 0x00a9, 0x2122, 0x0119,
+       0x00a8, 0x2260, 0x0123, 0x012e,
+       /* 0xb0 */
+       0x012f, 0x012a, 0x2264, 0x2265,
+       0x012b, 0x0136, 0x2202, 0x2211,
+       0x0142, 0x013b, 0x013c, 0x013d,
+       0x013e, 0x0139, 0x013a, 0x0145,
+       /* 0xc0 */
+       0x0146, 0x0143, 0x00ac, 0x221a,
+       0x0144, 0x0147, 0x2206, 0x00ab,
+       0x00bb, 0x2026, 0x00a0, 0x0148,
+       0x0150, 0x00d5, 0x0151, 0x014c,
+       /* 0xd0 */
+       0x2013, 0x2014, 0x201c, 0x201d,
+       0x2018, 0x2019, 0x00f7, 0x25ca,
+       0x014d, 0x0154, 0x0155, 0x0158,
+       0x2039, 0x203a, 0x0159, 0x0156,
+       /* 0xe0 */
+       0x0157, 0x0160, 0x201a, 0x201e,
+       0x0161, 0x015a, 0x015b, 0x00c1,
+       0x0164, 0x0165, 0x00cd, 0x017d,
+       0x017e, 0x016a, 0x00d3, 0x00d4,
+       /* 0xf0 */
+       0x016b, 0x016e, 0x00da, 0x016f,
+       0x0170, 0x0171, 0x0172, 0x0173,
+       0x00dd, 0x00fd, 0x0137, 0x017b,
+       0x0141, 0x017c, 0x0122, 0x02c7,
+};
+
+static const unsigned char page00[256] = {
+       0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+       0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+       0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+       0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+       0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+       0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+       0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+       0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+       0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+       0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+       0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+       0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+       0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+       0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+       0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+       0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0xca, 0x00, 0x00, 0xa3, 0x00, 0x00, 0x00, 0xa4, /* 0xa0-0xa7 */
+       0xac, 0xa9, 0x00, 0xc7, 0xc2, 0x00, 0xa8, 0x00, /* 0xa8-0xaf */
+       0xa1, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa6, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0xc8, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0xe7, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+       0x00, 0x83, 0x00, 0x00, 0x00, 0xea, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0xee, 0xef, 0xcd, 0x85, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0xf2, 0x00, 0x86, 0xf8, 0x00, 0xa7, /* 0xd8-0xdf */
+       0x00, 0x87, 0x00, 0x00, 0x8a, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x8e, 0x00, 0x00, 0x00, 0x92, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x97, 0x99, 0x9b, 0x9a, 0xd6, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x9c, 0x00, 0x9f, 0xf9, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char page01[256] = {
+       0x81, 0x82, 0x00, 0x00, 0x84, 0x88, 0x8c, 0x8d, /* 0x00-0x07 */
+       0x00, 0x00, 0x00, 0x00, 0x89, 0x8b, 0x91, 0x93, /* 0x08-0x0f */
+       0x00, 0x00, 0x94, 0x95, 0x00, 0x00, 0x96, 0x98, /* 0x10-0x17 */
+       0xa2, 0xab, 0x9d, 0x9e, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+       0x00, 0x00, 0xfe, 0xae, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0xb1, 0xb4, 0x00, 0x00, 0xaf, 0xb0, /* 0x28-0x2f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb5, 0xfa, /* 0x30-0x37 */
+       0x00, 0xbd, 0xbe, 0xb9, 0xba, 0xbb, 0xbc, 0x00, /* 0x38-0x3f */
+       0x00, 0xfc, 0xb8, 0xc1, 0xc4, 0xbf, 0xc0, 0xc5, /* 0x40-0x47 */
+       0xcb, 0x00, 0x00, 0x00, 0xcf, 0xd8, 0x00, 0x00, /* 0x48-0x4f */
+       0xcc, 0xce, 0x00, 0x00, 0xd9, 0xda, 0xdf, 0xe0, /* 0x50-0x57 */
+       0xdb, 0xde, 0xe5, 0xe6, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0xe1, 0xe4, 0x00, 0x00, 0xe8, 0xe9, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0xed, 0xf0, 0x00, 0x00, 0xf1, 0xf3, /* 0x68-0x6f */
+       0xf4, 0xf5, 0xf6, 0xf7, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0x00, 0x8f, 0x90, 0xfb, 0xfd, 0xeb, 0xec, 0x00, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char page02[256] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, /* 0xc0-0xc7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char page20[256] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+       0x00, 0x00, 0x00, 0xd0, 0xd1, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+       0xd4, 0xd5, 0xe2, 0x00, 0xd2, 0xd3, 0xe3, 0x00, /* 0x18-0x1f */
+       0xa0, 0x00, 0xa5, 0x00, 0x00, 0x00, 0xc9, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0xdc, 0xdd, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char page21[256] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+       0x00, 0x00, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char page22[256] = {
+       0x00, 0x00, 0xb6, 0x00, 0x00, 0x00, 0xc6, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+       0x00, 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+       0x00, 0x00, 0xc3, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0xad, 0x00, 0x00, 0x00, 0xb2, 0xb3, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char page25[256] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+       0x00, 0x00, 0xd7, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char *const page_uni2charset[256] = {
+       page00, page01, page02, NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       page20, page21, page22, NULL,   NULL,   page25, NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+};
+
+static const unsigned char charset2lower[256] = {
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x00-0x07 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x08-0x0f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x10-0x17 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x18-0x1f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x20-0x27 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x28-0x2f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x30-0x37 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x38-0x3f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x40-0x47 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x48-0x4f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x50-0x57 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x58-0x5f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x60-0x67 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x68-0x6f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x70-0x77 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x78-0x7f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x80-0x87 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x88-0x8f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x90-0x97 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x98-0x9f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xa0-0xa7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xa8-0xaf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xb0-0xb7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xb8-0xbf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xc0-0xc7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xc8-0xcf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xd0-0xd7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xd8-0xdf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xe0-0xe7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xe8-0xef */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xf0-0xf7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xf8-0xff */
+};
+
+static const unsigned char charset2upper[256] = {
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x00-0x07 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x08-0x0f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x10-0x17 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x18-0x1f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x20-0x27 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x28-0x2f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x30-0x37 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x38-0x3f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x40-0x47 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x48-0x4f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x50-0x57 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x58-0x5f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x60-0x67 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x68-0x6f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x70-0x77 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x78-0x7f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x80-0x87 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x88-0x8f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x90-0x97 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x98-0x9f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xa0-0xa7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xa8-0xaf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xb0-0xb7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xb8-0xbf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xc0-0xc7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xc8-0xcf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xd0-0xd7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xd8-0xdf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xe0-0xe7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xe8-0xef */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xf0-0xf7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xf8-0xff */
+};
+
+static int uni2char(wchar_t uni, unsigned char *out, int boundlen)
+{
+       const unsigned char *uni2charset;
+       unsigned char cl = uni & 0x00ff;
+       unsigned char ch = (uni & 0xff00) >> 8;
+
+       if (boundlen <= 0)
+               return -ENAMETOOLONG;
+
+       uni2charset = page_uni2charset[ch];
+       if (uni2charset && uni2charset[cl])
+               out[0] = uni2charset[cl];
+       else
+               return -EINVAL;
+       return 1;
+}
+
+static int char2uni(const unsigned char *rawstring, int boundlen, wchar_t *uni)
+{
+       *uni = charset2uni[*rawstring];
+       if (*uni == 0x0000)
+               return -EINVAL;
+       return 1;
+}
+
+static struct nls_table table = {
+       .charset        = "maccenteuro",
+       .uni2char       = uni2char,
+       .char2uni       = char2uni,
+       .charset2lower  = charset2lower,
+       .charset2upper  = charset2upper,
+       .owner          = THIS_MODULE,
+};
+
+static int __init init_nls_maccenteuro(void)
+{
+       return register_nls(&table);
+}
+
+static void __exit exit_nls_maccenteuro(void)
+{
+       unregister_nls(&table);
+}
+
+module_init(init_nls_maccenteuro)
+module_exit(exit_nls_maccenteuro)
+
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/fs/nls/nls_maccroatian.c b/fs/nls/nls_maccroatian.c
new file mode 100644 (file)
index 0000000..10b01c3
--- /dev/null
@@ -0,0 +1,602 @@
+/*
+ * linux/fs/nls/nls_maccroatian.c
+ *
+ * Charset maccroatian translation tables.
+ * Generated automatically from the Unicode and charset
+ * tables from the Unicode Organization (www.unicode.org).
+ * The Unicode to charset table has only exact mappings.
+ */
+
+/*
+ * COPYRIGHT AND PERMISSION NOTICE
+ *
+ * Copyright 1991-2012 Unicode, Inc.  All rights reserved.  Distributed under
+ * the Terms of Use in http://www.unicode.org/copyright.html.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of the Unicode data files and any associated documentation (the "Data
+ * Files") or Unicode software and any associated documentation (the
+ * "Software") to deal in the Data Files or Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, and/or sell copies of the Data Files or Software, and
+ * to permit persons to whom the Data Files or Software are furnished to do
+ * so, provided that (a) the above copyright notice(s) and this permission
+ * notice appear with all copies of the Data Files or Software, (b) both the
+ * above copyright notice(s) and this permission notice appear in associated
+ * documentation, and (c) there is clear notice in each modified Data File or
+ * in the Software as well as in the documentation associated with the Data
+ * File(s) or Software that the data or software has been modified.
+ *
+ * THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ * KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
+ * THIRD PARTY RIGHTS.  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
+ * INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
+ * OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
+ * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THE DATA FILES OR SOFTWARE.
+ *
+ * Except as contained in this notice, the name of a copyright holder shall
+ * not be used in advertising or otherwise to promote the sale, use or other
+ * dealings in these Data Files or Software without prior written
+ * authorization of the copyright holder.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/nls.h>
+#include <linux/errno.h>
+
+static const wchar_t charset2uni[256] = {
+       /* 0x00 */
+       0x0000, 0x0001, 0x0002, 0x0003,
+       0x0004, 0x0005, 0x0006, 0x0007,
+       0x0008, 0x0009, 0x000a, 0x000b,
+       0x000c, 0x000d, 0x000e, 0x000f,
+       /* 0x10 */
+       0x0010, 0x0011, 0x0012, 0x0013,
+       0x0014, 0x0015, 0x0016, 0x0017,
+       0x0018, 0x0019, 0x001a, 0x001b,
+       0x001c, 0x001d, 0x001e, 0x001f,
+       /* 0x20 */
+       0x0020, 0x0021, 0x0022, 0x0023,
+       0x0024, 0x0025, 0x0026, 0x0027,
+       0x0028, 0x0029, 0x002a, 0x002b,
+       0x002c, 0x002d, 0x002e, 0x002f,
+       /* 0x30 */
+       0x0030, 0x0031, 0x0032, 0x0033,
+       0x0034, 0x0035, 0x0036, 0x0037,
+       0x0038, 0x0039, 0x003a, 0x003b,
+       0x003c, 0x003d, 0x003e, 0x003f,
+       /* 0x40 */
+       0x0040, 0x0041, 0x0042, 0x0043,
+       0x0044, 0x0045, 0x0046, 0x0047,
+       0x0048, 0x0049, 0x004a, 0x004b,
+       0x004c, 0x004d, 0x004e, 0x004f,
+       /* 0x50 */
+       0x0050, 0x0051, 0x0052, 0x0053,
+       0x0054, 0x0055, 0x0056, 0x0057,
+       0x0058, 0x0059, 0x005a, 0x005b,
+       0x005c, 0x005d, 0x005e, 0x005f,
+       /* 0x60 */
+       0x0060, 0x0061, 0x0062, 0x0063,
+       0x0064, 0x0065, 0x0066, 0x0067,
+       0x0068, 0x0069, 0x006a, 0x006b,
+       0x006c, 0x006d, 0x006e, 0x006f,
+       /* 0x70 */
+       0x0070, 0x0071, 0x0072, 0x0073,
+       0x0074, 0x0075, 0x0076, 0x0077,
+       0x0078, 0x0079, 0x007a, 0x007b,
+       0x007c, 0x007d, 0x007e, 0x007f,
+       /* 0x80 */
+       0x00c4, 0x00c5, 0x00c7, 0x00c9,
+       0x00d1, 0x00d6, 0x00dc, 0x00e1,
+       0x00e0, 0x00e2, 0x00e4, 0x00e3,
+       0x00e5, 0x00e7, 0x00e9, 0x00e8,
+       /* 0x90 */
+       0x00ea, 0x00eb, 0x00ed, 0x00ec,
+       0x00ee, 0x00ef, 0x00f1, 0x00f3,
+       0x00f2, 0x00f4, 0x00f6, 0x00f5,
+       0x00fa, 0x00f9, 0x00fb, 0x00fc,
+       /* 0xa0 */
+       0x2020, 0x00b0, 0x00a2, 0x00a3,
+       0x00a7, 0x2022, 0x00b6, 0x00df,
+       0x00ae, 0x0160, 0x2122, 0x00b4,
+       0x00a8, 0x2260, 0x017d, 0x00d8,
+       /* 0xb0 */
+       0x221e, 0x00b1, 0x2264, 0x2265,
+       0x2206, 0x00b5, 0x2202, 0x2211,
+       0x220f, 0x0161, 0x222b, 0x00aa,
+       0x00ba, 0x03a9, 0x017e, 0x00f8,
+       /* 0xc0 */
+       0x00bf, 0x00a1, 0x00ac, 0x221a,
+       0x0192, 0x2248, 0x0106, 0x00ab,
+       0x010c, 0x2026, 0x00a0, 0x00c0,
+       0x00c3, 0x00d5, 0x0152, 0x0153,
+       /* 0xd0 */
+       0x0110, 0x2014, 0x201c, 0x201d,
+       0x2018, 0x2019, 0x00f7, 0x25ca,
+       0xf8ff, 0x00a9, 0x2044, 0x20ac,
+       0x2039, 0x203a, 0x00c6, 0x00bb,
+       /* 0xe0 */
+       0x2013, 0x00b7, 0x201a, 0x201e,
+       0x2030, 0x00c2, 0x0107, 0x00c1,
+       0x010d, 0x00c8, 0x00cd, 0x00ce,
+       0x00cf, 0x00cc, 0x00d3, 0x00d4,
+       /* 0xf0 */
+       0x0111, 0x00d2, 0x00da, 0x00db,
+       0x00d9, 0x0131, 0x02c6, 0x02dc,
+       0x00af, 0x03c0, 0x00cb, 0x02da,
+       0x00b8, 0x00ca, 0x00e6, 0x02c7,
+};
+
+static const unsigned char page00[256] = {
+       0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+       0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+       0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+       0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+       0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+       0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+       0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+       0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+       0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+       0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+       0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+       0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+       0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+       0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+       0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+       0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0xca, 0xc1, 0xa2, 0xa3, 0x00, 0x00, 0x00, 0xa4, /* 0xa0-0xa7 */
+       0xac, 0xd9, 0xbb, 0xc7, 0xc2, 0x00, 0xa8, 0xf8, /* 0xa8-0xaf */
+       0xa1, 0xb1, 0x00, 0x00, 0xab, 0xb5, 0xa6, 0xe1, /* 0xb0-0xb7 */
+       0xfc, 0x00, 0xbc, 0xdf, 0x00, 0x00, 0x00, 0xc0, /* 0xb8-0xbf */
+       0xcb, 0xe7, 0xe5, 0xcc, 0x80, 0x81, 0xde, 0x82, /* 0xc0-0xc7 */
+       0xe9, 0x83, 0xfd, 0xfa, 0xed, 0xea, 0xeb, 0xec, /* 0xc8-0xcf */
+       0x00, 0x84, 0xf1, 0xee, 0xef, 0xcd, 0x85, 0x00, /* 0xd0-0xd7 */
+       0xaf, 0xf4, 0xf2, 0xf3, 0x86, 0x00, 0x00, 0xa7, /* 0xd8-0xdf */
+       0x88, 0x87, 0x89, 0x8b, 0x8a, 0x8c, 0xfe, 0x8d, /* 0xe0-0xe7 */
+       0x8f, 0x8e, 0x90, 0x91, 0x93, 0x92, 0x94, 0x95, /* 0xe8-0xef */
+       0x00, 0x96, 0x98, 0x97, 0x99, 0x9b, 0x9a, 0xd6, /* 0xf0-0xf7 */
+       0xbf, 0x9d, 0x9c, 0x9e, 0x9f, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char page01[256] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xe6, /* 0x00-0x07 */
+       0x00, 0x00, 0x00, 0x00, 0xc8, 0xe8, 0x00, 0x00, /* 0x08-0x0f */
+       0xd0, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0x00, 0xf5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0x00, 0x00, 0xce, 0xcf, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0xa9, 0xb9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0xae, 0xbe, 0x00, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0xc4, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char page02[256] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xff, /* 0xc0-0xc7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0xfb, 0x00, 0xf7, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char page03[256] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0xbd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char page20[256] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+       0x00, 0x00, 0x00, 0xe0, 0xd1, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+       0xd4, 0xd5, 0xe2, 0x00, 0xd2, 0xd3, 0xe3, 0x00, /* 0x18-0x1f */
+       0xa0, 0x00, 0xa5, 0x00, 0x00, 0x00, 0xc9, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0xe4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0xdc, 0xdd, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0x00, 0x00, 0x00, 0x00, 0xda, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0x00, 0x00, 0x00, 0xdb, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char page21[256] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+       0x00, 0x00, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char page22[256] = {
+       0x00, 0x00, 0xb6, 0x00, 0x00, 0x00, 0xb4, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb8, /* 0x08-0x0f */
+       0x00, 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+       0x00, 0x00, 0xc3, 0x00, 0x00, 0x00, 0xb0, 0x00, /* 0x18-0x1f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0xba, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0xc5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0xad, 0x00, 0x00, 0x00, 0xb2, 0xb3, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char page25[256] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+       0x00, 0x00, 0xd7, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char pagef8[256] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, /* 0xf8-0xff */
+};
+
+static const unsigned char *const page_uni2charset[256] = {
+       page00, page01, page02, page03, NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       page20, page21, page22, NULL,   NULL,   page25, NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       pagef8, NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+};
+
+static const unsigned char charset2lower[256] = {
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x00-0x07 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x08-0x0f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x10-0x17 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x18-0x1f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x20-0x27 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x28-0x2f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x30-0x37 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x38-0x3f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x40-0x47 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x48-0x4f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x50-0x57 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x58-0x5f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x60-0x67 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x68-0x6f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x70-0x77 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x78-0x7f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x80-0x87 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x88-0x8f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x90-0x97 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x98-0x9f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xa0-0xa7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xa8-0xaf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xb0-0xb7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xb8-0xbf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xc0-0xc7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xc8-0xcf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xd0-0xd7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xd8-0xdf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xe0-0xe7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xe8-0xef */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xf0-0xf7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xf8-0xff */
+};
+
+static const unsigned char charset2upper[256] = {
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x00-0x07 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x08-0x0f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x10-0x17 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x18-0x1f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x20-0x27 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x28-0x2f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x30-0x37 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x38-0x3f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x40-0x47 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x48-0x4f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x50-0x57 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x58-0x5f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x60-0x67 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x68-0x6f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x70-0x77 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x78-0x7f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x80-0x87 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x88-0x8f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x90-0x97 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x98-0x9f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xa0-0xa7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xa8-0xaf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xb0-0xb7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xb8-0xbf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xc0-0xc7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xc8-0xcf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xd0-0xd7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xd8-0xdf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xe0-0xe7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xe8-0xef */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xf0-0xf7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xf8-0xff */
+};
+
+static int uni2char(wchar_t uni, unsigned char *out, int boundlen)
+{
+       const unsigned char *uni2charset;
+       unsigned char cl = uni & 0x00ff;
+       unsigned char ch = (uni & 0xff00) >> 8;
+
+       if (boundlen <= 0)
+               return -ENAMETOOLONG;
+
+       uni2charset = page_uni2charset[ch];
+       if (uni2charset && uni2charset[cl])
+               out[0] = uni2charset[cl];
+       else
+               return -EINVAL;
+       return 1;
+}
+
+static int char2uni(const unsigned char *rawstring, int boundlen, wchar_t *uni)
+{
+       *uni = charset2uni[*rawstring];
+       if (*uni == 0x0000)
+               return -EINVAL;
+       return 1;
+}
+
+static struct nls_table table = {
+       .charset        = "maccroatian",
+       .uni2char       = uni2char,
+       .char2uni       = char2uni,
+       .charset2lower  = charset2lower,
+       .charset2upper  = charset2upper,
+       .owner          = THIS_MODULE,
+};
+
+static int __init init_nls_maccroatian(void)
+{
+       return register_nls(&table);
+}
+
+static void __exit exit_nls_maccroatian(void)
+{
+       unregister_nls(&table);
+}
+
+module_init(init_nls_maccroatian)
+module_exit(exit_nls_maccroatian)
+
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/fs/nls/nls_maccyrillic.c b/fs/nls/nls_maccyrillic.c
new file mode 100644 (file)
index 0000000..318473f
--- /dev/null
@@ -0,0 +1,497 @@
+/*
+ * linux/fs/nls/nls_maccyrillic.c
+ *
+ * Charset maccyrillic translation tables.
+ * Generated automatically from the Unicode and charset
+ * tables from the Unicode Organization (www.unicode.org).
+ * The Unicode to charset table has only exact mappings.
+ */
+
+/*
+ * COPYRIGHT AND PERMISSION NOTICE
+ *
+ * Copyright 1991-2012 Unicode, Inc.  All rights reserved.  Distributed under
+ * the Terms of Use in http://www.unicode.org/copyright.html.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of the Unicode data files and any associated documentation (the "Data
+ * Files") or Unicode software and any associated documentation (the
+ * "Software") to deal in the Data Files or Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, and/or sell copies of the Data Files or Software, and
+ * to permit persons to whom the Data Files or Software are furnished to do
+ * so, provided that (a) the above copyright notice(s) and this permission
+ * notice appear with all copies of the Data Files or Software, (b) both the
+ * above copyright notice(s) and this permission notice appear in associated
+ * documentation, and (c) there is clear notice in each modified Data File or
+ * in the Software as well as in the documentation associated with the Data
+ * File(s) or Software that the data or software has been modified.
+ *
+ * THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ * KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
+ * THIRD PARTY RIGHTS.  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
+ * INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
+ * OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
+ * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THE DATA FILES OR SOFTWARE.
+ *
+ * Except as contained in this notice, the name of a copyright holder shall
+ * not be used in advertising or otherwise to promote the sale, use or other
+ * dealings in these Data Files or Software without prior written
+ * authorization of the copyright holder.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/nls.h>
+#include <linux/errno.h>
+
+static const wchar_t charset2uni[256] = {
+       /* 0x00 */
+       0x0000, 0x0001, 0x0002, 0x0003,
+       0x0004, 0x0005, 0x0006, 0x0007,
+       0x0008, 0x0009, 0x000a, 0x000b,
+       0x000c, 0x000d, 0x000e, 0x000f,
+       /* 0x10 */
+       0x0010, 0x0011, 0x0012, 0x0013,
+       0x0014, 0x0015, 0x0016, 0x0017,
+       0x0018, 0x0019, 0x001a, 0x001b,
+       0x001c, 0x001d, 0x001e, 0x001f,
+       /* 0x20 */
+       0x0020, 0x0021, 0x0022, 0x0023,
+       0x0024, 0x0025, 0x0026, 0x0027,
+       0x0028, 0x0029, 0x002a, 0x002b,
+       0x002c, 0x002d, 0x002e, 0x002f,
+       /* 0x30 */
+       0x0030, 0x0031, 0x0032, 0x0033,
+       0x0034, 0x0035, 0x0036, 0x0037,
+       0x0038, 0x0039, 0x003a, 0x003b,
+       0x003c, 0x003d, 0x003e, 0x003f,
+       /* 0x40 */
+       0x0040, 0x0041, 0x0042, 0x0043,
+       0x0044, 0x0045, 0x0046, 0x0047,
+       0x0048, 0x0049, 0x004a, 0x004b,
+       0x004c, 0x004d, 0x004e, 0x004f,
+       /* 0x50 */
+       0x0050, 0x0051, 0x0052, 0x0053,
+       0x0054, 0x0055, 0x0056, 0x0057,
+       0x0058, 0x0059, 0x005a, 0x005b,
+       0x005c, 0x005d, 0x005e, 0x005f,
+       /* 0x60 */
+       0x0060, 0x0061, 0x0062, 0x0063,
+       0x0064, 0x0065, 0x0066, 0x0067,
+       0x0068, 0x0069, 0x006a, 0x006b,
+       0x006c, 0x006d, 0x006e, 0x006f,
+       /* 0x70 */
+       0x0070, 0x0071, 0x0072, 0x0073,
+       0x0074, 0x0075, 0x0076, 0x0077,
+       0x0078, 0x0079, 0x007a, 0x007b,
+       0x007c, 0x007d, 0x007e, 0x007f,
+       /* 0x80 */
+       0x0410, 0x0411, 0x0412, 0x0413,
+       0x0414, 0x0415, 0x0416, 0x0417,
+       0x0418, 0x0419, 0x041a, 0x041b,
+       0x041c, 0x041d, 0x041e, 0x041f,
+       /* 0x90 */
+       0x0420, 0x0421, 0x0422, 0x0423,
+       0x0424, 0x0425, 0x0426, 0x0427,
+       0x0428, 0x0429, 0x042a, 0x042b,
+       0x042c, 0x042d, 0x042e, 0x042f,
+       /* 0xa0 */
+       0x2020, 0x00b0, 0x0490, 0x00a3,
+       0x00a7, 0x2022, 0x00b6, 0x0406,
+       0x00ae, 0x00a9, 0x2122, 0x0402,
+       0x0452, 0x2260, 0x0403, 0x0453,
+       /* 0xb0 */
+       0x221e, 0x00b1, 0x2264, 0x2265,
+       0x0456, 0x00b5, 0x0491, 0x0408,
+       0x0404, 0x0454, 0x0407, 0x0457,
+       0x0409, 0x0459, 0x040a, 0x045a,
+       /* 0xc0 */
+       0x0458, 0x0405, 0x00ac, 0x221a,
+       0x0192, 0x2248, 0x2206, 0x00ab,
+       0x00bb, 0x2026, 0x00a0, 0x040b,
+       0x045b, 0x040c, 0x045c, 0x0455,
+       /* 0xd0 */
+       0x2013, 0x2014, 0x201c, 0x201d,
+       0x2018, 0x2019, 0x00f7, 0x201e,
+       0x040e, 0x045e, 0x040f, 0x045f,
+       0x2116, 0x0401, 0x0451, 0x044f,
+       /* 0xe0 */
+       0x0430, 0x0431, 0x0432, 0x0433,
+       0x0434, 0x0435, 0x0436, 0x0437,
+       0x0438, 0x0439, 0x043a, 0x043b,
+       0x043c, 0x043d, 0x043e, 0x043f,
+       /* 0xf0 */
+       0x0440, 0x0441, 0x0442, 0x0443,
+       0x0444, 0x0445, 0x0446, 0x0447,
+       0x0448, 0x0449, 0x044a, 0x044b,
+       0x044c, 0x044d, 0x044e, 0x20ac,
+};
+
+static const unsigned char page00[256] = {
+       0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+       0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+       0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+       0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+       0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+       0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+       0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+       0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+       0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+       0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+       0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+       0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+       0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+       0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+       0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+       0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0xca, 0x00, 0x00, 0xa3, 0x00, 0x00, 0x00, 0xa4, /* 0xa0-0xa7 */
+       0x00, 0xa9, 0x00, 0xc7, 0xc2, 0x00, 0xa8, 0x00, /* 0xa8-0xaf */
+       0xa1, 0xb1, 0x00, 0x00, 0x00, 0xb5, 0xa6, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0xc8, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd6, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char page01[256] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0xc4, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char page04[256] = {
+       0x00, 0xdd, 0xab, 0xae, 0xb8, 0xc1, 0xa7, 0xba, /* 0x00-0x07 */
+       0xb7, 0xbc, 0xbe, 0xcb, 0xcd, 0x00, 0xd8, 0xda, /* 0x08-0x0f */
+       0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, /* 0x10-0x17 */
+       0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, /* 0x18-0x1f */
+       0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, /* 0x20-0x27 */
+       0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, /* 0x28-0x2f */
+       0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* 0x30-0x37 */
+       0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0x38-0x3f */
+       0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0x40-0x47 */
+       0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xdf, /* 0x48-0x4f */
+       0x00, 0xde, 0xac, 0xaf, 0xb9, 0xcf, 0xb4, 0xbb, /* 0x50-0x57 */
+       0xc0, 0xbd, 0xbf, 0xcc, 0xce, 0x00, 0xd9, 0xdb, /* 0x58-0x5f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0xa2, 0xb6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char page20[256] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+       0x00, 0x00, 0x00, 0xd0, 0xd1, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+       0xd4, 0xd5, 0x00, 0x00, 0xd2, 0xd3, 0xd7, 0x00, /* 0x18-0x1f */
+       0xa0, 0x00, 0xa5, 0x00, 0x00, 0x00, 0xc9, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char page21[256] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x00, /* 0x10-0x17 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+       0x00, 0x00, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char page22[256] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+       0x00, 0x00, 0xc3, 0x00, 0x00, 0x00, 0xb0, 0x00, /* 0x18-0x1f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0xc5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0xad, 0x00, 0x00, 0x00, 0xb2, 0xb3, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char *const page_uni2charset[256] = {
+       page00, page01, NULL,   NULL,   page04, NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       page20, page21, page22, NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+};
+
+static const unsigned char charset2lower[256] = {
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x00-0x07 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x08-0x0f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x10-0x17 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x18-0x1f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x20-0x27 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x28-0x2f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x30-0x37 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x38-0x3f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x40-0x47 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x48-0x4f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x50-0x57 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x58-0x5f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x60-0x67 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x68-0x6f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x70-0x77 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x78-0x7f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x80-0x87 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x88-0x8f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x90-0x97 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x98-0x9f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xa0-0xa7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xa8-0xaf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xb0-0xb7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xb8-0xbf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xc0-0xc7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xc8-0xcf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xd0-0xd7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xd8-0xdf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xe0-0xe7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xe8-0xef */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xf0-0xf7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xf8-0xff */
+};
+
+static const unsigned char charset2upper[256] = {
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x00-0x07 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x08-0x0f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x10-0x17 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x18-0x1f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x20-0x27 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x28-0x2f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x30-0x37 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x38-0x3f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x40-0x47 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x48-0x4f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x50-0x57 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x58-0x5f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x60-0x67 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x68-0x6f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x70-0x77 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x78-0x7f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x80-0x87 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x88-0x8f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x90-0x97 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x98-0x9f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xa0-0xa7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xa8-0xaf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xb0-0xb7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xb8-0xbf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xc0-0xc7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xc8-0xcf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xd0-0xd7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xd8-0xdf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xe0-0xe7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xe8-0xef */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xf0-0xf7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xf8-0xff */
+};
+
+static int uni2char(wchar_t uni, unsigned char *out, int boundlen)
+{
+       const unsigned char *uni2charset;
+       unsigned char cl = uni & 0x00ff;
+       unsigned char ch = (uni & 0xff00) >> 8;
+
+       if (boundlen <= 0)
+               return -ENAMETOOLONG;
+
+       uni2charset = page_uni2charset[ch];
+       if (uni2charset && uni2charset[cl])
+               out[0] = uni2charset[cl];
+       else
+               return -EINVAL;
+       return 1;
+}
+
+static int char2uni(const unsigned char *rawstring, int boundlen, wchar_t *uni)
+{
+       *uni = charset2uni[*rawstring];
+       if (*uni == 0x0000)
+               return -EINVAL;
+       return 1;
+}
+
+static struct nls_table table = {
+       .charset        = "maccyrillic",
+       .uni2char       = uni2char,
+       .char2uni       = char2uni,
+       .charset2lower  = charset2lower,
+       .charset2upper  = charset2upper,
+       .owner          = THIS_MODULE,
+};
+
+static int __init init_nls_maccyrillic(void)
+{
+       return register_nls(&table);
+}
+
+static void __exit exit_nls_maccyrillic(void)
+{
+       unregister_nls(&table);
+}
+
+module_init(init_nls_maccyrillic)
+module_exit(exit_nls_maccyrillic)
+
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/fs/nls/nls_macgaelic.c b/fs/nls/nls_macgaelic.c
new file mode 100644 (file)
index 0000000..615d8e1
--- /dev/null
@@ -0,0 +1,567 @@
+/*
+ * linux/fs/nls/nls_macgaelic.c
+ *
+ * Charset macgaelic translation tables.
+ * Generated automatically from the Unicode and charset
+ * tables from the Unicode Organization (www.unicode.org).
+ * The Unicode to charset table has only exact mappings.
+ */
+
+/*
+ * COPYRIGHT AND PERMISSION NOTICE
+ *
+ * Copyright 1991-2012 Unicode, Inc.  All rights reserved.  Distributed under
+ * the Terms of Use in http://www.unicode.org/copyright.html.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of the Unicode data files and any associated documentation (the "Data
+ * Files") or Unicode software and any associated documentation (the
+ * "Software") to deal in the Data Files or Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, and/or sell copies of the Data Files or Software, and
+ * to permit persons to whom the Data Files or Software are furnished to do
+ * so, provided that (a) the above copyright notice(s) and this permission
+ * notice appear with all copies of the Data Files or Software, (b) both the
+ * above copyright notice(s) and this permission notice appear in associated
+ * documentation, and (c) there is clear notice in each modified Data File or
+ * in the Software as well as in the documentation associated with the Data
+ * File(s) or Software that the data or software has been modified.
+ *
+ * THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ * KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
+ * THIRD PARTY RIGHTS.  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
+ * INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
+ * OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
+ * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THE DATA FILES OR SOFTWARE.
+ *
+ * Except as contained in this notice, the name of a copyright holder shall
+ * not be used in advertising or otherwise to promote the sale, use or other
+ * dealings in these Data Files or Software without prior written
+ * authorization of the copyright holder.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/nls.h>
+#include <linux/errno.h>
+
+static const wchar_t charset2uni[256] = {
+       /* 0x00 */
+       0x0000, 0x0001, 0x0002, 0x0003,
+       0x0004, 0x0005, 0x0006, 0x0007,
+       0x0008, 0x0009, 0x000a, 0x000b,
+       0x000c, 0x000d, 0x000e, 0x000f,
+       /* 0x10 */
+       0x0010, 0x0011, 0x0012, 0x0013,
+       0x0014, 0x0015, 0x0016, 0x0017,
+       0x0018, 0x0019, 0x001a, 0x001b,
+       0x001c, 0x001d, 0x001e, 0x001f,
+       /* 0x20 */
+       0x0020, 0x0021, 0x0022, 0x0023,
+       0x0024, 0x0025, 0x0026, 0x0027,
+       0x0028, 0x0029, 0x002a, 0x002b,
+       0x002c, 0x002d, 0x002e, 0x002f,
+       /* 0x30 */
+       0x0030, 0x0031, 0x0032, 0x0033,
+       0x0034, 0x0035, 0x0036, 0x0037,
+       0x0038, 0x0039, 0x003a, 0x003b,
+       0x003c, 0x003d, 0x003e, 0x003f,
+       /* 0x40 */
+       0x0040, 0x0041, 0x0042, 0x0043,
+       0x0044, 0x0045, 0x0046, 0x0047,
+       0x0048, 0x0049, 0x004a, 0x004b,
+       0x004c, 0x004d, 0x004e, 0x004f,
+       /* 0x50 */
+       0x0050, 0x0051, 0x0052, 0x0053,
+       0x0054, 0x0055, 0x0056, 0x0057,
+       0x0058, 0x0059, 0x005a, 0x005b,
+       0x005c, 0x005d, 0x005e, 0x005f,
+       /* 0x60 */
+       0x0060, 0x0061, 0x0062, 0x0063,
+       0x0064, 0x0065, 0x0066, 0x0067,
+       0x0068, 0x0069, 0x006a, 0x006b,
+       0x006c, 0x006d, 0x006e, 0x006f,
+       /* 0x70 */
+       0x0070, 0x0071, 0x0072, 0x0073,
+       0x0074, 0x0075, 0x0076, 0x0077,
+       0x0078, 0x0079, 0x007a, 0x007b,
+       0x007c, 0x007d, 0x007e, 0x007f,
+       /* 0x80 */
+       0x00c4, 0x00c5, 0x00c7, 0x00c9,
+       0x00d1, 0x00d6, 0x00dc, 0x00e1,
+       0x00e0, 0x00e2, 0x00e4, 0x00e3,
+       0x00e5, 0x00e7, 0x00e9, 0x00e8,
+       /* 0x90 */
+       0x00ea, 0x00eb, 0x00ed, 0x00ec,
+       0x00ee, 0x00ef, 0x00f1, 0x00f3,
+       0x00f2, 0x00f4, 0x00f6, 0x00f5,
+       0x00fa, 0x00f9, 0x00fb, 0x00fc,
+       /* 0xa0 */
+       0x2020, 0x00b0, 0x00a2, 0x00a3,
+       0x00a7, 0x2022, 0x00b6, 0x00df,
+       0x00ae, 0x00a9, 0x2122, 0x00b4,
+       0x00a8, 0x2260, 0x00c6, 0x00d8,
+       /* 0xb0 */
+       0x1e02, 0x00b1, 0x2264, 0x2265,
+       0x1e03, 0x010a, 0x010b, 0x1e0a,
+       0x1e0b, 0x1e1e, 0x1e1f, 0x0120,
+       0x0121, 0x1e40, 0x00e6, 0x00f8,
+       /* 0xc0 */
+       0x1e41, 0x1e56, 0x1e57, 0x027c,
+       0x0192, 0x017f, 0x1e60, 0x00ab,
+       0x00bb, 0x2026, 0x00a0, 0x00c0,
+       0x00c3, 0x00d5, 0x0152, 0x0153,
+       /* 0xd0 */
+       0x2013, 0x2014, 0x201c, 0x201d,
+       0x2018, 0x2019, 0x1e61, 0x1e9b,
+       0x00ff, 0x0178, 0x1e6a, 0x20ac,
+       0x2039, 0x203a, 0x0176, 0x0177,
+       /* 0xe0 */
+       0x1e6b, 0x00b7, 0x1ef2, 0x1ef3,
+       0x204a, 0x00c2, 0x00ca, 0x00c1,
+       0x00cb, 0x00c8, 0x00cd, 0x00ce,
+       0x00cf, 0x00cc, 0x00d3, 0x00d4,
+       /* 0xf0 */
+       0x2663, 0x00d2, 0x00da, 0x00db,
+       0x00d9, 0x0131, 0x00dd, 0x00fd,
+       0x0174, 0x0175, 0x1e84, 0x1e85,
+       0x1e80, 0x1e81, 0x1e82, 0x1e83,
+};
+
+static const unsigned char page00[256] = {
+       0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+       0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+       0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+       0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+       0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+       0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+       0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+       0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+       0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+       0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+       0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+       0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+       0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+       0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+       0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+       0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0xca, 0x00, 0xa2, 0xa3, 0x00, 0x00, 0x00, 0xa4, /* 0xa0-0xa7 */
+       0xac, 0xa9, 0x00, 0xc7, 0x00, 0x00, 0xa8, 0x00, /* 0xa8-0xaf */
+       0xa1, 0xb1, 0x00, 0x00, 0xab, 0x00, 0xa6, 0xe1, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0xc8, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0xcb, 0xe7, 0xe5, 0xcc, 0x80, 0x81, 0xae, 0x82, /* 0xc0-0xc7 */
+       0xe9, 0x83, 0xe6, 0xe8, 0xed, 0xea, 0xeb, 0xec, /* 0xc8-0xcf */
+       0x00, 0x84, 0xf1, 0xee, 0xef, 0xcd, 0x85, 0x00, /* 0xd0-0xd7 */
+       0xaf, 0xf4, 0xf2, 0xf3, 0x86, 0xf6, 0x00, 0xa7, /* 0xd8-0xdf */
+       0x88, 0x87, 0x89, 0x8b, 0x8a, 0x8c, 0xbe, 0x8d, /* 0xe0-0xe7 */
+       0x8f, 0x8e, 0x90, 0x91, 0x93, 0x92, 0x94, 0x95, /* 0xe8-0xef */
+       0x00, 0x96, 0x98, 0x97, 0x99, 0x9b, 0x9a, 0x00, /* 0xf0-0xf7 */
+       0xbf, 0x9d, 0x9c, 0x9e, 0x9f, 0xf7, 0x00, 0xd8, /* 0xf8-0xff */
+};
+
+static const unsigned char page01[256] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0xb5, 0xb6, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+       0xbb, 0xbc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0x00, 0xf5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0x00, 0x00, 0xce, 0xcf, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0xf8, 0xf9, 0xde, 0xdf, /* 0x70-0x77 */
+       0xd9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc5, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0xc4, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char page02[256] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0x00, 0x00, 0x00, 0x00, 0xc3, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char page1e[256] = {
+       0x00, 0x00, 0xb0, 0xb4, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0xb7, 0xb8, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb9, 0xba, /* 0x18-0x1f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0xbd, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc1, 0xc2, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0xc6, 0xd6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0xda, 0xe0, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+       0xfc, 0xfd, 0xfe, 0xff, 0xfa, 0xfb, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0xd7, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0xe2, 0xe3, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char page20[256] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+       0x00, 0x00, 0x00, 0xd0, 0xd1, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+       0xd4, 0xd5, 0x00, 0x00, 0xd2, 0xd3, 0x00, 0x00, /* 0x18-0x1f */
+       0xa0, 0x00, 0xa5, 0x00, 0x00, 0x00, 0xc9, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0xdc, 0xdd, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0x00, 0x00, 0xe4, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0x00, 0x00, 0x00, 0xdb, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char page21[256] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+       0x00, 0x00, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char page22[256] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0xad, 0x00, 0x00, 0x00, 0xb2, 0xb3, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char page26[256] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char *const page_uni2charset[256] = {
+       page00, page01, page02, NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   page1e, NULL,
+       page20, page21, page22, NULL,   NULL,   NULL,   page26, NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+};
+
+static const unsigned char charset2lower[256] = {
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x00-0x07 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x08-0x0f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x10-0x17 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x18-0x1f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x20-0x27 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x28-0x2f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x30-0x37 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x38-0x3f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x40-0x47 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x48-0x4f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x50-0x57 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x58-0x5f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x60-0x67 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x68-0x6f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x70-0x77 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x78-0x7f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x80-0x87 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x88-0x8f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x90-0x97 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x98-0x9f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xa0-0xa7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xa8-0xaf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xb0-0xb7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xb8-0xbf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xc0-0xc7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xc8-0xcf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xd0-0xd7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xd8-0xdf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xe0-0xe7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xe8-0xef */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xf0-0xf7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xf8-0xff */
+};
+
+static const unsigned char charset2upper[256] = {
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0x00-0x07 */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0x08-0x0f */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0x10-0x17 */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0x18-0x1f */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0x20-0x27 */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0x28-0x2f */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0x30-0x37 */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0x38-0x3f */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0x40-0x47 */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0x48-0x4f */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0x50-0x57 */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0x58-0x5f */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0x60-0x67 */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0x68-0x6f */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0x70-0x77 */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0x78-0x7f */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0x80-0x87 */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0x88-0x8f */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0x90-0x97 */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0x98-0x9f */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0xa0-0xa7 */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0xa8-0xaf */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0xb0-0xb7 */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0xb8-0xbf */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0xc0-0xc7 */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0xc8-0xcf */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0xd0-0xd7 */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0xd8-0xdf */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0xe0-0xe7 */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0xe8-0xef */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0xf0-0xf7 */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0xf8-0xff */
+};
+
+static int uni2char(wchar_t uni, unsigned char *out, int boundlen)
+{
+       const unsigned char *uni2charset;
+       unsigned char cl = uni & 0x00ff;
+       unsigned char ch = (uni & 0xff00) >> 8;
+
+       if (boundlen <= 0)
+               return -ENAMETOOLONG;
+
+       uni2charset = page_uni2charset[ch];
+       if (uni2charset && uni2charset[cl])
+               out[0] = uni2charset[cl];
+       else
+               return -EINVAL;
+       return 1;
+}
+
+static int char2uni(const unsigned char *rawstring, int boundlen, wchar_t *uni)
+{
+       *uni = charset2uni[*rawstring];
+       if (*uni == 0x0000)
+               return -EINVAL;
+       return 1;
+}
+
+static struct nls_table table = {
+       .charset        = "macgaelic",
+       .uni2char       = uni2char,
+       .char2uni       = char2uni,
+       .charset2lower  = charset2lower,
+       .charset2upper  = charset2upper,
+       .owner          = THIS_MODULE,
+};
+
+static int __init init_nls_macgaelic(void)
+{
+       return register_nls(&table);
+}
+
+static void __exit exit_nls_macgaelic(void)
+{
+       unregister_nls(&table);
+}
+
+module_init(init_nls_macgaelic)
+module_exit(exit_nls_macgaelic)
+
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/fs/nls/nls_macgreek.c b/fs/nls/nls_macgreek.c
new file mode 100644 (file)
index 0000000..79880f3
--- /dev/null
@@ -0,0 +1,497 @@
+/*
+ * linux/fs/nls/nls_macgreek.c
+ *
+ * Charset macgreek translation tables.
+ * Generated automatically from the Unicode and charset
+ * tables from the Unicode Organization (www.unicode.org).
+ * The Unicode to charset table has only exact mappings.
+ */
+
+/*
+ * COPYRIGHT AND PERMISSION NOTICE
+ *
+ * Copyright 1991-2012 Unicode, Inc.  All rights reserved.  Distributed under
+ * the Terms of Use in http://www.unicode.org/copyright.html.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of the Unicode data files and any associated documentation (the "Data
+ * Files") or Unicode software and any associated documentation (the
+ * "Software") to deal in the Data Files or Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, and/or sell copies of the Data Files or Software, and
+ * to permit persons to whom the Data Files or Software are furnished to do
+ * so, provided that (a) the above copyright notice(s) and this permission
+ * notice appear with all copies of the Data Files or Software, (b) both the
+ * above copyright notice(s) and this permission notice appear in associated
+ * documentation, and (c) there is clear notice in each modified Data File or
+ * in the Software as well as in the documentation associated with the Data
+ * File(s) or Software that the data or software has been modified.
+ *
+ * THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ * KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
+ * THIRD PARTY RIGHTS.  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
+ * INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
+ * OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
+ * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THE DATA FILES OR SOFTWARE.
+ *
+ * Except as contained in this notice, the name of a copyright holder shall
+ * not be used in advertising or otherwise to promote the sale, use or other
+ * dealings in these Data Files or Software without prior written
+ * authorization of the copyright holder.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/nls.h>
+#include <linux/errno.h>
+
+static const wchar_t charset2uni[256] = {
+       /* 0x00 */
+       0x0000, 0x0001, 0x0002, 0x0003,
+       0x0004, 0x0005, 0x0006, 0x0007,
+       0x0008, 0x0009, 0x000a, 0x000b,
+       0x000c, 0x000d, 0x000e, 0x000f,
+       /* 0x10 */
+       0x0010, 0x0011, 0x0012, 0x0013,
+       0x0014, 0x0015, 0x0016, 0x0017,
+       0x0018, 0x0019, 0x001a, 0x001b,
+       0x001c, 0x001d, 0x001e, 0x001f,
+       /* 0x20 */
+       0x0020, 0x0021, 0x0022, 0x0023,
+       0x0024, 0x0025, 0x0026, 0x0027,
+       0x0028, 0x0029, 0x002a, 0x002b,
+       0x002c, 0x002d, 0x002e, 0x002f,
+       /* 0x30 */
+       0x0030, 0x0031, 0x0032, 0x0033,
+       0x0034, 0x0035, 0x0036, 0x0037,
+       0x0038, 0x0039, 0x003a, 0x003b,
+       0x003c, 0x003d, 0x003e, 0x003f,
+       /* 0x40 */
+       0x0040, 0x0041, 0x0042, 0x0043,
+       0x0044, 0x0045, 0x0046, 0x0047,
+       0x0048, 0x0049, 0x004a, 0x004b,
+       0x004c, 0x004d, 0x004e, 0x004f,
+       /* 0x50 */
+       0x0050, 0x0051, 0x0052, 0x0053,
+       0x0054, 0x0055, 0x0056, 0x0057,
+       0x0058, 0x0059, 0x005a, 0x005b,
+       0x005c, 0x005d, 0x005e, 0x005f,
+       /* 0x60 */
+       0x0060, 0x0061, 0x0062, 0x0063,
+       0x0064, 0x0065, 0x0066, 0x0067,
+       0x0068, 0x0069, 0x006a, 0x006b,
+       0x006c, 0x006d, 0x006e, 0x006f,
+       /* 0x70 */
+       0x0070, 0x0071, 0x0072, 0x0073,
+       0x0074, 0x0075, 0x0076, 0x0077,
+       0x0078, 0x0079, 0x007a, 0x007b,
+       0x007c, 0x007d, 0x007e, 0x007f,
+       /* 0x80 */
+       0x00c4, 0x00b9, 0x00b2, 0x00c9,
+       0x00b3, 0x00d6, 0x00dc, 0x0385,
+       0x00e0, 0x00e2, 0x00e4, 0x0384,
+       0x00a8, 0x00e7, 0x00e9, 0x00e8,
+       /* 0x90 */
+       0x00ea, 0x00eb, 0x00a3, 0x2122,
+       0x00ee, 0x00ef, 0x2022, 0x00bd,
+       0x2030, 0x00f4, 0x00f6, 0x00a6,
+       0x20ac, 0x00f9, 0x00fb, 0x00fc,
+       /* 0xa0 */
+       0x2020, 0x0393, 0x0394, 0x0398,
+       0x039b, 0x039e, 0x03a0, 0x00df,
+       0x00ae, 0x00a9, 0x03a3, 0x03aa,
+       0x00a7, 0x2260, 0x00b0, 0x00b7,
+       /* 0xb0 */
+       0x0391, 0x00b1, 0x2264, 0x2265,
+       0x00a5, 0x0392, 0x0395, 0x0396,
+       0x0397, 0x0399, 0x039a, 0x039c,
+       0x03a6, 0x03ab, 0x03a8, 0x03a9,
+       /* 0xc0 */
+       0x03ac, 0x039d, 0x00ac, 0x039f,
+       0x03a1, 0x2248, 0x03a4, 0x00ab,
+       0x00bb, 0x2026, 0x00a0, 0x03a5,
+       0x03a7, 0x0386, 0x0388, 0x0153,
+       /* 0xd0 */
+       0x2013, 0x2015, 0x201c, 0x201d,
+       0x2018, 0x2019, 0x00f7, 0x0389,
+       0x038a, 0x038c, 0x038e, 0x03ad,
+       0x03ae, 0x03af, 0x03cc, 0x038f,
+       /* 0xe0 */
+       0x03cd, 0x03b1, 0x03b2, 0x03c8,
+       0x03b4, 0x03b5, 0x03c6, 0x03b3,
+       0x03b7, 0x03b9, 0x03be, 0x03ba,
+       0x03bb, 0x03bc, 0x03bd, 0x03bf,
+       /* 0xf0 */
+       0x03c0, 0x03ce, 0x03c1, 0x03c3,
+       0x03c4, 0x03b8, 0x03c9, 0x03c2,
+       0x03c7, 0x03c5, 0x03b6, 0x03ca,
+       0x03cb, 0x0390, 0x03b0, 0x00ad,
+};
+
+static const unsigned char page00[256] = {
+       0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+       0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+       0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+       0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+       0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+       0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+       0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+       0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+       0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+       0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+       0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+       0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+       0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+       0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+       0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+       0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0xca, 0x00, 0x00, 0x92, 0x00, 0xb4, 0x9b, 0xac, /* 0xa0-0xa7 */
+       0x8c, 0xa9, 0x00, 0xc7, 0xc2, 0xff, 0xa8, 0x00, /* 0xa8-0xaf */
+       0xae, 0xb1, 0x82, 0x84, 0x00, 0x00, 0x00, 0xaf, /* 0xb0-0xb7 */
+       0x00, 0x81, 0x00, 0xc8, 0x00, 0x97, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+       0x00, 0x83, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, 0xa7, /* 0xd8-0xdf */
+       0x88, 0x00, 0x89, 0x00, 0x8a, 0x00, 0x00, 0x8d, /* 0xe0-0xe7 */
+       0x8f, 0x8e, 0x90, 0x91, 0x00, 0x00, 0x94, 0x95, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x99, 0x00, 0x9a, 0xd6, /* 0xf0-0xf7 */
+       0x00, 0x9d, 0x00, 0x9e, 0x9f, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char page01[256] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0x00, 0x00, 0x00, 0xcf, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char page03[256] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x8b, 0x87, 0xcd, 0x00, /* 0x80-0x87 */
+       0xce, 0xd7, 0xd8, 0x00, 0xd9, 0x00, 0xda, 0xdf, /* 0x88-0x8f */
+       0xfd, 0xb0, 0xb5, 0xa1, 0xa2, 0xb6, 0xb7, 0xb8, /* 0x90-0x97 */
+       0xa3, 0xb9, 0xba, 0xa4, 0xbb, 0xc1, 0xa5, 0xc3, /* 0x98-0x9f */
+       0xa6, 0xc4, 0x00, 0xaa, 0xc6, 0xcb, 0xbc, 0xcc, /* 0xa0-0xa7 */
+       0xbe, 0xbf, 0xab, 0xbd, 0xc0, 0xdb, 0xdc, 0xdd, /* 0xa8-0xaf */
+       0xfe, 0xe1, 0xe2, 0xe7, 0xe4, 0xe5, 0xfa, 0xe8, /* 0xb0-0xb7 */
+       0xf5, 0xe9, 0xeb, 0xec, 0xed, 0xee, 0xea, 0xef, /* 0xb8-0xbf */
+       0xf0, 0xf2, 0xf7, 0xf3, 0xf4, 0xf9, 0xe6, 0xf8, /* 0xc0-0xc7 */
+       0xe3, 0xf6, 0xfb, 0xfc, 0xde, 0xe0, 0xf1, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char page20[256] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+       0x00, 0x00, 0x00, 0xd0, 0x00, 0xd1, 0x00, 0x00, /* 0x10-0x17 */
+       0xd4, 0xd5, 0x00, 0x00, 0xd2, 0xd3, 0x00, 0x00, /* 0x18-0x1f */
+       0xa0, 0x00, 0x96, 0x00, 0x00, 0x00, 0xc9, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char page21[256] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+       0x00, 0x00, 0x93, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char page22[256] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0xc5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0xad, 0x00, 0x00, 0x00, 0xb2, 0xb3, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char *const page_uni2charset[256] = {
+       page00, page01, NULL,   page03, NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       page20, page21, page22, NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+};
+
+static const unsigned char charset2lower[256] = {
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x00-0x07 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x08-0x0f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x10-0x17 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x18-0x1f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x20-0x27 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x28-0x2f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x30-0x37 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x38-0x3f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x40-0x47 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x48-0x4f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x50-0x57 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x58-0x5f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x60-0x67 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x68-0x6f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x70-0x77 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x78-0x7f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x80-0x87 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x88-0x8f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x90-0x97 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x98-0x9f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xa0-0xa7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xa8-0xaf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xb0-0xb7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xb8-0xbf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xc0-0xc7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xc8-0xcf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xd0-0xd7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xd8-0xdf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xe0-0xe7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xe8-0xef */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xf0-0xf7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xf8-0xff */
+};
+
+static const unsigned char charset2upper[256] = {
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x00-0x07 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x08-0x0f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x10-0x17 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x18-0x1f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x20-0x27 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x28-0x2f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x30-0x37 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x38-0x3f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x40-0x47 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x48-0x4f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x50-0x57 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x58-0x5f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x60-0x67 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x68-0x6f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x70-0x77 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x78-0x7f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x80-0x87 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x88-0x8f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x90-0x97 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x98-0x9f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xa0-0xa7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xa8-0xaf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xb0-0xb7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xb8-0xbf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xc0-0xc7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xc8-0xcf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xd0-0xd7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xd8-0xdf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xe0-0xe7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xe8-0xef */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xf0-0xf7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xf8-0xff */
+};
+
+static int uni2char(wchar_t uni, unsigned char *out, int boundlen)
+{
+       const unsigned char *uni2charset;
+       unsigned char cl = uni & 0x00ff;
+       unsigned char ch = (uni & 0xff00) >> 8;
+
+       if (boundlen <= 0)
+               return -ENAMETOOLONG;
+
+       uni2charset = page_uni2charset[ch];
+       if (uni2charset && uni2charset[cl])
+               out[0] = uni2charset[cl];
+       else
+               return -EINVAL;
+       return 1;
+}
+
+static int char2uni(const unsigned char *rawstring, int boundlen, wchar_t *uni)
+{
+       *uni = charset2uni[*rawstring];
+       if (*uni == 0x0000)
+               return -EINVAL;
+       return 1;
+}
+
+static struct nls_table table = {
+       .charset        = "macgreek",
+       .uni2char       = uni2char,
+       .char2uni       = char2uni,
+       .charset2lower  = charset2lower,
+       .charset2upper  = charset2upper,
+       .owner          = THIS_MODULE,
+};
+
+static int __init init_nls_macgreek(void)
+{
+       return register_nls(&table);
+}
+
+static void __exit exit_nls_macgreek(void)
+{
+       unregister_nls(&table);
+}
+
+module_init(init_nls_macgreek)
+module_exit(exit_nls_macgreek)
+
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/fs/nls/nls_maciceland.c b/fs/nls/nls_maciceland.c
new file mode 100644 (file)
index 0000000..1e688c5
--- /dev/null
@@ -0,0 +1,602 @@
+/*
+ * linux/fs/nls/nls_maciceland.c
+ *
+ * Charset maciceland translation tables.
+ * Generated automatically from the Unicode and charset
+ * tables from the Unicode Organization (www.unicode.org).
+ * The Unicode to charset table has only exact mappings.
+ */
+
+/*
+ * COPYRIGHT AND PERMISSION NOTICE
+ *
+ * Copyright 1991-2012 Unicode, Inc.  All rights reserved.  Distributed under
+ * the Terms of Use in http://www.unicode.org/copyright.html.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of the Unicode data files and any associated documentation (the "Data
+ * Files") or Unicode software and any associated documentation (the
+ * "Software") to deal in the Data Files or Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, and/or sell copies of the Data Files or Software, and
+ * to permit persons to whom the Data Files or Software are furnished to do
+ * so, provided that (a) the above copyright notice(s) and this permission
+ * notice appear with all copies of the Data Files or Software, (b) both the
+ * above copyright notice(s) and this permission notice appear in associated
+ * documentation, and (c) there is clear notice in each modified Data File or
+ * in the Software as well as in the documentation associated with the Data
+ * File(s) or Software that the data or software has been modified.
+ *
+ * THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ * KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
+ * THIRD PARTY RIGHTS.  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
+ * INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
+ * OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
+ * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THE DATA FILES OR SOFTWARE.
+ *
+ * Except as contained in this notice, the name of a copyright holder shall
+ * not be used in advertising or otherwise to promote the sale, use or other
+ * dealings in these Data Files or Software without prior written
+ * authorization of the copyright holder.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/nls.h>
+#include <linux/errno.h>
+
+static const wchar_t charset2uni[256] = {
+       /* 0x00 */
+       0x0000, 0x0001, 0x0002, 0x0003,
+       0x0004, 0x0005, 0x0006, 0x0007,
+       0x0008, 0x0009, 0x000a, 0x000b,
+       0x000c, 0x000d, 0x000e, 0x000f,
+       /* 0x10 */
+       0x0010, 0x0011, 0x0012, 0x0013,
+       0x0014, 0x0015, 0x0016, 0x0017,
+       0x0018, 0x0019, 0x001a, 0x001b,
+       0x001c, 0x001d, 0x001e, 0x001f,
+       /* 0x20 */
+       0x0020, 0x0021, 0x0022, 0x0023,
+       0x0024, 0x0025, 0x0026, 0x0027,
+       0x0028, 0x0029, 0x002a, 0x002b,
+       0x002c, 0x002d, 0x002e, 0x002f,
+       /* 0x30 */
+       0x0030, 0x0031, 0x0032, 0x0033,
+       0x0034, 0x0035, 0x0036, 0x0037,
+       0x0038, 0x0039, 0x003a, 0x003b,
+       0x003c, 0x003d, 0x003e, 0x003f,
+       /* 0x40 */
+       0x0040, 0x0041, 0x0042, 0x0043,
+       0x0044, 0x0045, 0x0046, 0x0047,
+       0x0048, 0x0049, 0x004a, 0x004b,
+       0x004c, 0x004d, 0x004e, 0x004f,
+       /* 0x50 */
+       0x0050, 0x0051, 0x0052, 0x0053,
+       0x0054, 0x0055, 0x0056, 0x0057,
+       0x0058, 0x0059, 0x005a, 0x005b,
+       0x005c, 0x005d, 0x005e, 0x005f,
+       /* 0x60 */
+       0x0060, 0x0061, 0x0062, 0x0063,
+       0x0064, 0x0065, 0x0066, 0x0067,
+       0x0068, 0x0069, 0x006a, 0x006b,
+       0x006c, 0x006d, 0x006e, 0x006f,
+       /* 0x70 */
+       0x0070, 0x0071, 0x0072, 0x0073,
+       0x0074, 0x0075, 0x0076, 0x0077,
+       0x0078, 0x0079, 0x007a, 0x007b,
+       0x007c, 0x007d, 0x007e, 0x007f,
+       /* 0x80 */
+       0x00c4, 0x00c5, 0x00c7, 0x00c9,
+       0x00d1, 0x00d6, 0x00dc, 0x00e1,
+       0x00e0, 0x00e2, 0x00e4, 0x00e3,
+       0x00e5, 0x00e7, 0x00e9, 0x00e8,
+       /* 0x90 */
+       0x00ea, 0x00eb, 0x00ed, 0x00ec,
+       0x00ee, 0x00ef, 0x00f1, 0x00f3,
+       0x00f2, 0x00f4, 0x00f6, 0x00f5,
+       0x00fa, 0x00f9, 0x00fb, 0x00fc,
+       /* 0xa0 */
+       0x00dd, 0x00b0, 0x00a2, 0x00a3,
+       0x00a7, 0x2022, 0x00b6, 0x00df,
+       0x00ae, 0x00a9, 0x2122, 0x00b4,
+       0x00a8, 0x2260, 0x00c6, 0x00d8,
+       /* 0xb0 */
+       0x221e, 0x00b1, 0x2264, 0x2265,
+       0x00a5, 0x00b5, 0x2202, 0x2211,
+       0x220f, 0x03c0, 0x222b, 0x00aa,
+       0x00ba, 0x03a9, 0x00e6, 0x00f8,
+       /* 0xc0 */
+       0x00bf, 0x00a1, 0x00ac, 0x221a,
+       0x0192, 0x2248, 0x2206, 0x00ab,
+       0x00bb, 0x2026, 0x00a0, 0x00c0,
+       0x00c3, 0x00d5, 0x0152, 0x0153,
+       /* 0xd0 */
+       0x2013, 0x2014, 0x201c, 0x201d,
+       0x2018, 0x2019, 0x00f7, 0x25ca,
+       0x00ff, 0x0178, 0x2044, 0x20ac,
+       0x00d0, 0x00f0, 0x00de, 0x00fe,
+       /* 0xe0 */
+       0x00fd, 0x00b7, 0x201a, 0x201e,
+       0x2030, 0x00c2, 0x00ca, 0x00c1,
+       0x00cb, 0x00c8, 0x00cd, 0x00ce,
+       0x00cf, 0x00cc, 0x00d3, 0x00d4,
+       /* 0xf0 */
+       0xf8ff, 0x00d2, 0x00da, 0x00db,
+       0x00d9, 0x0131, 0x02c6, 0x02dc,
+       0x00af, 0x02d8, 0x02d9, 0x02da,
+       0x00b8, 0x02dd, 0x02db, 0x02c7,
+};
+
+static const unsigned char page00[256] = {
+       0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+       0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+       0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+       0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+       0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+       0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+       0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+       0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+       0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+       0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+       0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+       0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+       0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+       0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+       0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+       0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0xca, 0xc1, 0xa2, 0xa3, 0x00, 0xb4, 0x00, 0xa4, /* 0xa0-0xa7 */
+       0xac, 0xa9, 0xbb, 0xc7, 0xc2, 0x00, 0xa8, 0xf8, /* 0xa8-0xaf */
+       0xa1, 0xb1, 0x00, 0x00, 0xab, 0xb5, 0xa6, 0xe1, /* 0xb0-0xb7 */
+       0xfc, 0x00, 0xbc, 0xc8, 0x00, 0x00, 0x00, 0xc0, /* 0xb8-0xbf */
+       0xcb, 0xe7, 0xe5, 0xcc, 0x80, 0x81, 0xae, 0x82, /* 0xc0-0xc7 */
+       0xe9, 0x83, 0xe6, 0xe8, 0xed, 0xea, 0xeb, 0xec, /* 0xc8-0xcf */
+       0xdc, 0x84, 0xf1, 0xee, 0xef, 0xcd, 0x85, 0x00, /* 0xd0-0xd7 */
+       0xaf, 0xf4, 0xf2, 0xf3, 0x86, 0xa0, 0xde, 0xa7, /* 0xd8-0xdf */
+       0x88, 0x87, 0x89, 0x8b, 0x8a, 0x8c, 0xbe, 0x8d, /* 0xe0-0xe7 */
+       0x8f, 0x8e, 0x90, 0x91, 0x93, 0x92, 0x94, 0x95, /* 0xe8-0xef */
+       0xdd, 0x96, 0x98, 0x97, 0x99, 0x9b, 0x9a, 0xd6, /* 0xf0-0xf7 */
+       0xbf, 0x9d, 0x9c, 0x9e, 0x9f, 0xe0, 0xdf, 0xd8, /* 0xf8-0xff */
+};
+
+static const unsigned char page01[256] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0x00, 0xf5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0x00, 0x00, 0xce, 0xcf, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0xd9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0xc4, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char page02[256] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xff, /* 0xc0-0xc7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0xf9, 0xfa, 0xfb, 0xfe, 0xf7, 0xfd, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char page03[256] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0xbd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0xb9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char page20[256] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+       0x00, 0x00, 0x00, 0xd0, 0xd1, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+       0xd4, 0xd5, 0xe2, 0x00, 0xd2, 0xd3, 0xe3, 0x00, /* 0x18-0x1f */
+       0x00, 0x00, 0xa5, 0x00, 0x00, 0x00, 0xc9, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0xe4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0x00, 0x00, 0x00, 0x00, 0xda, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0x00, 0x00, 0x00, 0xdb, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char page21[256] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+       0x00, 0x00, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char page22[256] = {
+       0x00, 0x00, 0xb6, 0x00, 0x00, 0x00, 0xc6, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb8, /* 0x08-0x0f */
+       0x00, 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+       0x00, 0x00, 0xc3, 0x00, 0x00, 0x00, 0xb0, 0x00, /* 0x18-0x1f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0xba, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0xc5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0xad, 0x00, 0x00, 0x00, 0xb2, 0xb3, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char page25[256] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+       0x00, 0x00, 0xd7, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char pagef8[256] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, /* 0xf8-0xff */
+};
+
+static const unsigned char *const page_uni2charset[256] = {
+       page00, page01, page02, page03, NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       page20, page21, page22, NULL,   NULL,   page25, NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       pagef8, NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+};
+
+static const unsigned char charset2lower[256] = {
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x00-0x07 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x08-0x0f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x10-0x17 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x18-0x1f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x20-0x27 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x28-0x2f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x30-0x37 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x38-0x3f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x40-0x47 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x48-0x4f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x50-0x57 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x58-0x5f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x60-0x67 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x68-0x6f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x70-0x77 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x78-0x7f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x80-0x87 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x88-0x8f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x90-0x97 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x98-0x9f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xa0-0xa7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xa8-0xaf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xb0-0xb7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xb8-0xbf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xc0-0xc7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xc8-0xcf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xd0-0xd7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xd8-0xdf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xe0-0xe7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xe8-0xef */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xf0-0xf7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xf8-0xff */
+};
+
+static const unsigned char charset2upper[256] = {
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x00-0x07 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x08-0x0f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x10-0x17 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x18-0x1f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x20-0x27 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x28-0x2f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x30-0x37 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x38-0x3f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x40-0x47 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x48-0x4f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x50-0x57 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x58-0x5f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x60-0x67 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x68-0x6f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x70-0x77 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x78-0x7f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x80-0x87 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x88-0x8f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x90-0x97 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x98-0x9f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xa0-0xa7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xa8-0xaf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xb0-0xb7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xb8-0xbf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xc0-0xc7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xc8-0xcf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xd0-0xd7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xd8-0xdf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xe0-0xe7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xe8-0xef */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xf0-0xf7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xf8-0xff */
+};
+
+static int uni2char(wchar_t uni, unsigned char *out, int boundlen)
+{
+       const unsigned char *uni2charset;
+       unsigned char cl = uni & 0x00ff;
+       unsigned char ch = (uni & 0xff00) >> 8;
+
+       if (boundlen <= 0)
+               return -ENAMETOOLONG;
+
+       uni2charset = page_uni2charset[ch];
+       if (uni2charset && uni2charset[cl])
+               out[0] = uni2charset[cl];
+       else
+               return -EINVAL;
+       return 1;
+}
+
+static int char2uni(const unsigned char *rawstring, int boundlen, wchar_t *uni)
+{
+       *uni = charset2uni[*rawstring];
+       if (*uni == 0x0000)
+               return -EINVAL;
+       return 1;
+}
+
+static struct nls_table table = {
+       .charset        = "maciceland",
+       .uni2char       = uni2char,
+       .char2uni       = char2uni,
+       .charset2lower  = charset2lower,
+       .charset2upper  = charset2upper,
+       .owner          = THIS_MODULE,
+};
+
+static int __init init_nls_maciceland(void)
+{
+       return register_nls(&table);
+}
+
+static void __exit exit_nls_maciceland(void)
+{
+       unregister_nls(&table);
+}
+
+module_init(init_nls_maciceland)
+module_exit(exit_nls_maciceland)
+
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/fs/nls/nls_macinuit.c b/fs/nls/nls_macinuit.c
new file mode 100644 (file)
index 0000000..f333d98
--- /dev/null
@@ -0,0 +1,532 @@
+/*
+ * linux/fs/nls/nls_macinuit.c
+ *
+ * Charset macinuit translation tables.
+ * Generated automatically from the Unicode and charset
+ * tables from the Unicode Organization (www.unicode.org).
+ * The Unicode to charset table has only exact mappings.
+ */
+
+/*
+ * COPYRIGHT AND PERMISSION NOTICE
+ *
+ * Copyright 1991-2012 Unicode, Inc.  All rights reserved.  Distributed under
+ * the Terms of Use in http://www.unicode.org/copyright.html.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of the Unicode data files and any associated documentation (the "Data
+ * Files") or Unicode software and any associated documentation (the
+ * "Software") to deal in the Data Files or Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, and/or sell copies of the Data Files or Software, and
+ * to permit persons to whom the Data Files or Software are furnished to do
+ * so, provided that (a) the above copyright notice(s) and this permission
+ * notice appear with all copies of the Data Files or Software, (b) both the
+ * above copyright notice(s) and this permission notice appear in associated
+ * documentation, and (c) there is clear notice in each modified Data File or
+ * in the Software as well as in the documentation associated with the Data
+ * File(s) or Software that the data or software has been modified.
+ *
+ * THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ * KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
+ * THIRD PARTY RIGHTS.  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
+ * INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
+ * OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
+ * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THE DATA FILES OR SOFTWARE.
+ *
+ * Except as contained in this notice, the name of a copyright holder shall
+ * not be used in advertising or otherwise to promote the sale, use or other
+ * dealings in these Data Files or Software without prior written
+ * authorization of the copyright holder.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/nls.h>
+#include <linux/errno.h>
+
+static const wchar_t charset2uni[256] = {
+       /* 0x00 */
+       0x0000, 0x0001, 0x0002, 0x0003,
+       0x0004, 0x0005, 0x0006, 0x0007,
+       0x0008, 0x0009, 0x000a, 0x000b,
+       0x000c, 0x000d, 0x000e, 0x000f,
+       /* 0x10 */
+       0x0010, 0x0011, 0x0012, 0x0013,
+       0x0014, 0x0015, 0x0016, 0x0017,
+       0x0018, 0x0019, 0x001a, 0x001b,
+       0x001c, 0x001d, 0x001e, 0x001f,
+       /* 0x20 */
+       0x0020, 0x0021, 0x0022, 0x0023,
+       0x0024, 0x0025, 0x0026, 0x0027,
+       0x0028, 0x0029, 0x002a, 0x002b,
+       0x002c, 0x002d, 0x002e, 0x002f,
+       /* 0x30 */
+       0x0030, 0x0031, 0x0032, 0x0033,
+       0x0034, 0x0035, 0x0036, 0x0037,
+       0x0038, 0x0039, 0x003a, 0x003b,
+       0x003c, 0x003d, 0x003e, 0x003f,
+       /* 0x40 */
+       0x0040, 0x0041, 0x0042, 0x0043,
+       0x0044, 0x0045, 0x0046, 0x0047,
+       0x0048, 0x0049, 0x004a, 0x004b,
+       0x004c, 0x004d, 0x004e, 0x004f,
+       /* 0x50 */
+       0x0050, 0x0051, 0x0052, 0x0053,
+       0x0054, 0x0055, 0x0056, 0x0057,
+       0x0058, 0x0059, 0x005a, 0x005b,
+       0x005c, 0x005d, 0x005e, 0x005f,
+       /* 0x60 */
+       0x0060, 0x0061, 0x0062, 0x0063,
+       0x0064, 0x0065, 0x0066, 0x0067,
+       0x0068, 0x0069, 0x006a, 0x006b,
+       0x006c, 0x006d, 0x006e, 0x006f,
+       /* 0x70 */
+       0x0070, 0x0071, 0x0072, 0x0073,
+       0x0074, 0x0075, 0x0076, 0x0077,
+       0x0078, 0x0079, 0x007a, 0x007b,
+       0x007c, 0x007d, 0x007e, 0x007f,
+       /* 0x80 */
+       0x1403, 0x1404, 0x1405, 0x1406,
+       0x140a, 0x140b, 0x1431, 0x1432,
+       0x1433, 0x1434, 0x1438, 0x1439,
+       0x1449, 0x144e, 0x144f, 0x1450,
+       /* 0x90 */
+       0x1451, 0x1455, 0x1456, 0x1466,
+       0x146d, 0x146e, 0x146f, 0x1470,
+       0x1472, 0x1473, 0x1483, 0x148b,
+       0x148c, 0x148d, 0x148e, 0x1490,
+       /* 0xa0 */
+       0x1491, 0x00b0, 0x14a1, 0x14a5,
+       0x14a6, 0x2022, 0x00b6, 0x14a7,
+       0x00ae, 0x00a9, 0x2122, 0x14a8,
+       0x14aa, 0x14ab, 0x14bb, 0x14c2,
+       /* 0xb0 */
+       0x14c3, 0x14c4, 0x14c5, 0x14c7,
+       0x14c8, 0x14d0, 0x14ef, 0x14f0,
+       0x14f1, 0x14f2, 0x14f4, 0x14f5,
+       0x1505, 0x14d5, 0x14d6, 0x14d7,
+       /* 0xc0 */
+       0x14d8, 0x14da, 0x14db, 0x14ea,
+       0x1528, 0x1529, 0x152a, 0x152b,
+       0x152d, 0x2026, 0x00a0, 0x152e,
+       0x153e, 0x1555, 0x1556, 0x1557,
+       /* 0xd0 */
+       0x2013, 0x2014, 0x201c, 0x201d,
+       0x2018, 0x2019, 0x1558, 0x1559,
+       0x155a, 0x155d, 0x1546, 0x1547,
+       0x1548, 0x1549, 0x154b, 0x154c,
+       /* 0xe0 */
+       0x1550, 0x157f, 0x1580, 0x1581,
+       0x1582, 0x1583, 0x1584, 0x1585,
+       0x158f, 0x1590, 0x1591, 0x1592,
+       0x1593, 0x1594, 0x1595, 0x1671,
+       /* 0xf0 */
+       0x1672, 0x1673, 0x1674, 0x1675,
+       0x1676, 0x1596, 0x15a0, 0x15a1,
+       0x15a2, 0x15a3, 0x15a4, 0x15a5,
+       0x15a6, 0x157c, 0x0141, 0x0142,
+};
+
+static const unsigned char page00[256] = {
+       0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+       0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+       0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+       0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+       0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+       0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+       0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+       0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+       0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+       0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+       0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+       0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+       0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+       0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+       0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+       0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0xca, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0xa9, 0x00, 0x00, 0x00, 0x00, 0xa8, 0x00, /* 0xa8-0xaf */
+       0xa1, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa6, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char page01[256] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0x00, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char page14[256] = {
+       0x00, 0x00, 0x00, 0x80, 0x81, 0x82, 0x83, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0x84, 0x85, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0x00, 0x86, 0x87, 0x88, 0x89, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x8a, 0x8b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0x00, 0x8c, 0x00, 0x00, 0x00, 0x00, 0x8d, 0x8e, /* 0x48-0x4f */
+       0x8f, 0x90, 0x00, 0x00, 0x00, 0x91, 0x92, 0x00, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x93, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x94, 0x95, 0x96, /* 0x68-0x6f */
+       0x97, 0x00, 0x98, 0x99, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x9a, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x9b, 0x9c, 0x9d, 0x9e, 0x00, /* 0x88-0x8f */
+       0x9f, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0xa2, 0x00, 0x00, 0x00, 0xa3, 0xa4, 0xa7, /* 0xa0-0xa7 */
+       0xab, 0x00, 0xac, 0xad, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0xae, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0x00, 0xaf, 0xb0, 0xb1, 0xb2, 0x00, 0xb3, /* 0xc0-0xc7 */
+       0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0xb5, 0x00, 0x00, 0x00, 0x00, 0xbd, 0xbe, 0xbf, /* 0xd0-0xd7 */
+       0xc0, 0x00, 0xc1, 0xc2, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0xc3, 0x00, 0x00, 0x00, 0x00, 0xb6, /* 0xe8-0xef */
+       0xb7, 0xb8, 0xb9, 0x00, 0xba, 0xbb, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char page15[256] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0xbc, 0x00, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+       0xc4, 0xc5, 0xc6, 0xc7, 0x00, 0xc8, 0xcb, 0x00, /* 0x28-0x2f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0x00, /* 0x38-0x3f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xda, 0xdb, /* 0x40-0x47 */
+       0xdc, 0xdd, 0x00, 0xde, 0xdf, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0xe0, 0x00, 0x00, 0x00, 0x00, 0xcd, 0xce, 0xcf, /* 0x50-0x57 */
+       0xd6, 0xd7, 0xd8, 0x00, 0x00, 0xd9, 0x00, 0x00, /* 0x58-0x5f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0x00, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x00, 0xe1, /* 0x78-0x7f */
+       0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe8, /* 0x88-0x8f */
+       0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xf5, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0x00, /* 0xa0-0xa7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char page16[256] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0x00, /* 0x70-0x77 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char page20[256] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+       0x00, 0x00, 0x00, 0xd0, 0xd1, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+       0xd4, 0xd5, 0x00, 0x00, 0xd2, 0xd3, 0x00, 0x00, /* 0x18-0x1f */
+       0x00, 0x00, 0xa5, 0x00, 0x00, 0x00, 0xc9, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char page21[256] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+       0x00, 0x00, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char *const page_uni2charset[256] = {
+       page00, page01, NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   page14, page15, page16, NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       page20, page21, NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+};
+
+static const unsigned char charset2lower[256] = {
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x00-0x07 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x08-0x0f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x10-0x17 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x18-0x1f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x20-0x27 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x28-0x2f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x30-0x37 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x38-0x3f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x40-0x47 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x48-0x4f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x50-0x57 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x58-0x5f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x60-0x67 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x68-0x6f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x70-0x77 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x78-0x7f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x80-0x87 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x88-0x8f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x90-0x97 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x98-0x9f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xa0-0xa7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xa8-0xaf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xb0-0xb7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xb8-0xbf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xc0-0xc7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xc8-0xcf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xd0-0xd7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xd8-0xdf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xe0-0xe7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xe8-0xef */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xf0-0xf7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xf8-0xff */
+};
+
+static const unsigned char charset2upper[256] = {
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0x00-0x07 */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0x08-0x0f */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0x10-0x17 */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0x18-0x1f */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0x20-0x27 */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0x28-0x2f */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0x30-0x37 */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0x38-0x3f */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0x40-0x47 */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0x48-0x4f */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0x50-0x57 */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0x58-0x5f */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0x60-0x67 */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0x68-0x6f */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0x70-0x77 */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0x78-0x7f */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0x80-0x87 */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0x88-0x8f */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0x90-0x97 */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0x98-0x9f */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0xa0-0xa7 */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0xa8-0xaf */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0xb0-0xb7 */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0xb8-0xbf */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0xc0-0xc7 */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0xc8-0xcf */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0xd0-0xd7 */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0xd8-0xdf */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0xe0-0xe7 */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0xe8-0xef */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0xf0-0xf7 */
+       0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, /* 0xf8-0xff */
+};
+
+static int uni2char(wchar_t uni, unsigned char *out, int boundlen)
+{
+       const unsigned char *uni2charset;
+       unsigned char cl = uni & 0x00ff;
+       unsigned char ch = (uni & 0xff00) >> 8;
+
+       if (boundlen <= 0)
+               return -ENAMETOOLONG;
+
+       uni2charset = page_uni2charset[ch];
+       if (uni2charset && uni2charset[cl])
+               out[0] = uni2charset[cl];
+       else
+               return -EINVAL;
+       return 1;
+}
+
+static int char2uni(const unsigned char *rawstring, int boundlen, wchar_t *uni)
+{
+       *uni = charset2uni[*rawstring];
+       if (*uni == 0x0000)
+               return -EINVAL;
+       return 1;
+}
+
+static struct nls_table table = {
+       .charset        = "macinuit",
+       .uni2char       = uni2char,
+       .char2uni       = char2uni,
+       .charset2lower  = charset2lower,
+       .charset2upper  = charset2upper,
+       .owner          = THIS_MODULE,
+};
+
+static int __init init_nls_macinuit(void)
+{
+       return register_nls(&table);
+}
+
+static void __exit exit_nls_macinuit(void)
+{
+       unregister_nls(&table);
+}
+
+module_init(init_nls_macinuit)
+module_exit(exit_nls_macinuit)
+
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/fs/nls/nls_macroman.c b/fs/nls/nls_macroman.c
new file mode 100644 (file)
index 0000000..6315a85
--- /dev/null
@@ -0,0 +1,637 @@
+/*
+ * linux/fs/nls/nls_macroman.c
+ *
+ * Charset macroman translation tables.
+ * Generated automatically from the Unicode and charset
+ * tables from the Unicode Organization (www.unicode.org).
+ * The Unicode to charset table has only exact mappings.
+ */
+
+/*
+ * COPYRIGHT AND PERMISSION NOTICE
+ *
+ * Copyright 1991-2012 Unicode, Inc.  All rights reserved.  Distributed under
+ * the Terms of Use in http://www.unicode.org/copyright.html.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of the Unicode data files and any associated documentation (the "Data
+ * Files") or Unicode software and any associated documentation (the
+ * "Software") to deal in the Data Files or Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, and/or sell copies of the Data Files or Software, and
+ * to permit persons to whom the Data Files or Software are furnished to do
+ * so, provided that (a) the above copyright notice(s) and this permission
+ * notice appear with all copies of the Data Files or Software, (b) both the
+ * above copyright notice(s) and this permission notice appear in associated
+ * documentation, and (c) there is clear notice in each modified Data File or
+ * in the Software as well as in the documentation associated with the Data
+ * File(s) or Software that the data or software has been modified.
+ *
+ * THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ * KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
+ * THIRD PARTY RIGHTS.  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
+ * INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
+ * OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
+ * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THE DATA FILES OR SOFTWARE.
+ *
+ * Except as contained in this notice, the name of a copyright holder shall
+ * not be used in advertising or otherwise to promote the sale, use or other
+ * dealings in these Data Files or Software without prior written
+ * authorization of the copyright holder.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/nls.h>
+#include <linux/errno.h>
+
+static const wchar_t charset2uni[256] = {
+       /* 0x00 */
+       0x0000, 0x0001, 0x0002, 0x0003,
+       0x0004, 0x0005, 0x0006, 0x0007,
+       0x0008, 0x0009, 0x000a, 0x000b,
+       0x000c, 0x000d, 0x000e, 0x000f,
+       /* 0x10 */
+       0x0010, 0x0011, 0x0012, 0x0013,
+       0x0014, 0x0015, 0x0016, 0x0017,
+       0x0018, 0x0019, 0x001a, 0x001b,
+       0x001c, 0x001d, 0x001e, 0x001f,
+       /* 0x20 */
+       0x0020, 0x0021, 0x0022, 0x0023,
+       0x0024, 0x0025, 0x0026, 0x0027,
+       0x0028, 0x0029, 0x002a, 0x002b,
+       0x002c, 0x002d, 0x002e, 0x002f,
+       /* 0x30 */
+       0x0030, 0x0031, 0x0032, 0x0033,
+       0x0034, 0x0035, 0x0036, 0x0037,
+       0x0038, 0x0039, 0x003a, 0x003b,
+       0x003c, 0x003d, 0x003e, 0x003f,
+       /* 0x40 */
+       0x0040, 0x0041, 0x0042, 0x0043,
+       0x0044, 0x0045, 0x0046, 0x0047,
+       0x0048, 0x0049, 0x004a, 0x004b,
+       0x004c, 0x004d, 0x004e, 0x004f,
+       /* 0x50 */
+       0x0050, 0x0051, 0x0052, 0x0053,
+       0x0054, 0x0055, 0x0056, 0x0057,
+       0x0058, 0x0059, 0x005a, 0x005b,
+       0x005c, 0x005d, 0x005e, 0x005f,
+       /* 0x60 */
+       0x0060, 0x0061, 0x0062, 0x0063,
+       0x0064, 0x0065, 0x0066, 0x0067,
+       0x0068, 0x0069, 0x006a, 0x006b,
+       0x006c, 0x006d, 0x006e, 0x006f,
+       /* 0x70 */
+       0x0070, 0x0071, 0x0072, 0x0073,
+       0x0074, 0x0075, 0x0076, 0x0077,
+       0x0078, 0x0079, 0x007a, 0x007b,
+       0x007c, 0x007d, 0x007e, 0x007f,
+       /* 0x80 */
+       0x00c4, 0x00c5, 0x00c7, 0x00c9,
+       0x00d1, 0x00d6, 0x00dc, 0x00e1,
+       0x00e0, 0x00e2, 0x00e4, 0x00e3,
+       0x00e5, 0x00e7, 0x00e9, 0x00e8,
+       /* 0x90 */
+       0x00ea, 0x00eb, 0x00ed, 0x00ec,
+       0x00ee, 0x00ef, 0x00f1, 0x00f3,
+       0x00f2, 0x00f4, 0x00f6, 0x00f5,
+       0x00fa, 0x00f9, 0x00fb, 0x00fc,
+       /* 0xa0 */
+       0x2020, 0x00b0, 0x00a2, 0x00a3,
+       0x00a7, 0x2022, 0x00b6, 0x00df,
+       0x00ae, 0x00a9, 0x2122, 0x00b4,
+       0x00a8, 0x2260, 0x00c6, 0x00d8,
+       /* 0xb0 */
+       0x221e, 0x00b1, 0x2264, 0x2265,
+       0x00a5, 0x00b5, 0x2202, 0x2211,
+       0x220f, 0x03c0, 0x222b, 0x00aa,
+       0x00ba, 0x03a9, 0x00e6, 0x00f8,
+       /* 0xc0 */
+       0x00bf, 0x00a1, 0x00ac, 0x221a,
+       0x0192, 0x2248, 0x2206, 0x00ab,
+       0x00bb, 0x2026, 0x00a0, 0x00c0,
+       0x00c3, 0x00d5, 0x0152, 0x0153,
+       /* 0xd0 */
+       0x2013, 0x2014, 0x201c, 0x201d,
+       0x2018, 0x2019, 0x00f7, 0x25ca,
+       0x00ff, 0x0178, 0x2044, 0x20ac,
+       0x2039, 0x203a, 0xfb01, 0xfb02,
+       /* 0xe0 */
+       0x2021, 0x00b7, 0x201a, 0x201e,
+       0x2030, 0x00c2, 0x00ca, 0x00c1,
+       0x00cb, 0x00c8, 0x00cd, 0x00ce,
+       0x00cf, 0x00cc, 0x00d3, 0x00d4,
+       /* 0xf0 */
+       0xf8ff, 0x00d2, 0x00da, 0x00db,
+       0x00d9, 0x0131, 0x02c6, 0x02dc,
+       0x00af, 0x02d8, 0x02d9, 0x02da,
+       0x00b8, 0x02dd, 0x02db, 0x02c7,
+};
+
+static const unsigned char page00[256] = {
+       0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+       0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+       0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+       0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+       0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+       0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+       0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+       0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+       0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+       0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+       0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+       0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+       0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+       0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+       0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+       0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0xca, 0xc1, 0xa2, 0xa3, 0x00, 0xb4, 0x00, 0xa4, /* 0xa0-0xa7 */
+       0xac, 0xa9, 0xbb, 0xc7, 0xc2, 0x00, 0xa8, 0xf8, /* 0xa8-0xaf */
+       0xa1, 0xb1, 0x00, 0x00, 0xab, 0xb5, 0xa6, 0xe1, /* 0xb0-0xb7 */
+       0xfc, 0x00, 0xbc, 0xc8, 0x00, 0x00, 0x00, 0xc0, /* 0xb8-0xbf */
+       0xcb, 0xe7, 0xe5, 0xcc, 0x80, 0x81, 0xae, 0x82, /* 0xc0-0xc7 */
+       0xe9, 0x83, 0xe6, 0xe8, 0xed, 0xea, 0xeb, 0xec, /* 0xc8-0xcf */
+       0x00, 0x84, 0xf1, 0xee, 0xef, 0xcd, 0x85, 0x00, /* 0xd0-0xd7 */
+       0xaf, 0xf4, 0xf2, 0xf3, 0x86, 0x00, 0x00, 0xa7, /* 0xd8-0xdf */
+       0x88, 0x87, 0x89, 0x8b, 0x8a, 0x8c, 0xbe, 0x8d, /* 0xe0-0xe7 */
+       0x8f, 0x8e, 0x90, 0x91, 0x93, 0x92, 0x94, 0x95, /* 0xe8-0xef */
+       0x00, 0x96, 0x98, 0x97, 0x99, 0x9b, 0x9a, 0xd6, /* 0xf0-0xf7 */
+       0xbf, 0x9d, 0x9c, 0x9e, 0x9f, 0x00, 0x00, 0xd8, /* 0xf8-0xff */
+};
+
+static const unsigned char page01[256] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0x00, 0xf5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0x00, 0x00, 0xce, 0xcf, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0xd9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0xc4, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char page02[256] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xff, /* 0xc0-0xc7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0xf9, 0xfa, 0xfb, 0xfe, 0xf7, 0xfd, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char page03[256] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0xbd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0xb9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char page20[256] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+       0x00, 0x00, 0x00, 0xd0, 0xd1, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+       0xd4, 0xd5, 0xe2, 0x00, 0xd2, 0xd3, 0xe3, 0x00, /* 0x18-0x1f */
+       0xa0, 0xe0, 0xa5, 0x00, 0x00, 0x00, 0xc9, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0xe4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0xdc, 0xdd, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0x00, 0x00, 0x00, 0x00, 0xda, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0x00, 0x00, 0x00, 0xdb, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char page21[256] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+       0x00, 0x00, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char page22[256] = {
+       0x00, 0x00, 0xb6, 0x00, 0x00, 0x00, 0xc6, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb8, /* 0x08-0x0f */
+       0x00, 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+       0x00, 0x00, 0xc3, 0x00, 0x00, 0x00, 0xb0, 0x00, /* 0x18-0x1f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0xba, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0xc5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0xad, 0x00, 0x00, 0x00, 0xb2, 0xb3, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char page25[256] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+       0x00, 0x00, 0xd7, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char pagef8[256] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, /* 0xf8-0xff */
+};
+
+static const unsigned char pagefb[256] = {
+       0x00, 0xde, 0xdf, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char *const page_uni2charset[256] = {
+       page00, page01, page02, page03, NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       page20, page21, page22, NULL,   NULL,   page25, NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       pagef8, NULL,   NULL,   pagefb, NULL,   NULL,   NULL,   NULL,
+};
+
+static const unsigned char charset2lower[256] = {
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x00-0x07 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x08-0x0f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x10-0x17 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x18-0x1f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x20-0x27 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x28-0x2f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x30-0x37 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x38-0x3f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x40-0x47 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x48-0x4f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x50-0x57 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x58-0x5f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x60-0x67 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x68-0x6f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x70-0x77 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x78-0x7f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x80-0x87 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x88-0x8f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x90-0x97 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x98-0x9f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xa0-0xa7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xa8-0xaf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xb0-0xb7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xb8-0xbf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xc0-0xc7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xc8-0xcf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xd0-0xd7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xd8-0xdf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xe0-0xe7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xe8-0xef */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xf0-0xf7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xf8-0xff */
+};
+
+static const unsigned char charset2upper[256] = {
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x00-0x07 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x08-0x0f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x10-0x17 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x18-0x1f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x20-0x27 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x28-0x2f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x30-0x37 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x38-0x3f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x40-0x47 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x48-0x4f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x50-0x57 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x58-0x5f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x60-0x67 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x68-0x6f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x70-0x77 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x78-0x7f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x80-0x87 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x88-0x8f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x90-0x97 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x98-0x9f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xa0-0xa7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xa8-0xaf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xb0-0xb7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xb8-0xbf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xc0-0xc7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xc8-0xcf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xd0-0xd7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xd8-0xdf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xe0-0xe7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xe8-0xef */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xf0-0xf7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xf8-0xff */
+};
+
+static int uni2char(wchar_t uni, unsigned char *out, int boundlen)
+{
+       const unsigned char *uni2charset;
+       unsigned char cl = uni & 0x00ff;
+       unsigned char ch = (uni & 0xff00) >> 8;
+
+       if (boundlen <= 0)
+               return -ENAMETOOLONG;
+
+       uni2charset = page_uni2charset[ch];
+       if (uni2charset && uni2charset[cl])
+               out[0] = uni2charset[cl];
+       else
+               return -EINVAL;
+       return 1;
+}
+
+static int char2uni(const unsigned char *rawstring, int boundlen, wchar_t *uni)
+{
+       *uni = charset2uni[*rawstring];
+       if (*uni == 0x0000)
+               return -EINVAL;
+       return 1;
+}
+
+static struct nls_table table = {
+       .charset        = "macroman",
+       .uni2char       = uni2char,
+       .char2uni       = char2uni,
+       .charset2lower  = charset2lower,
+       .charset2upper  = charset2upper,
+       .owner          = THIS_MODULE,
+};
+
+static int __init init_nls_macroman(void)
+{
+       return register_nls(&table);
+}
+
+static void __exit exit_nls_macroman(void)
+{
+       unregister_nls(&table);
+}
+
+module_init(init_nls_macroman)
+module_exit(exit_nls_macroman)
+
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/fs/nls/nls_macromanian.c b/fs/nls/nls_macromanian.c
new file mode 100644 (file)
index 0000000..b83c07a
--- /dev/null
@@ -0,0 +1,602 @@
+/*
+ * linux/fs/nls/nls_macromanian.c
+ *
+ * Charset macromanian translation tables.
+ * Generated automatically from the Unicode and charset
+ * tables from the Unicode Organization (www.unicode.org).
+ * The Unicode to charset table has only exact mappings.
+ */
+
+/*
+ * COPYRIGHT AND PERMISSION NOTICE
+ *
+ * Copyright 1991-2012 Unicode, Inc.  All rights reserved.  Distributed under
+ * the Terms of Use in http://www.unicode.org/copyright.html.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of the Unicode data files and any associated documentation (the "Data
+ * Files") or Unicode software and any associated documentation (the
+ * "Software") to deal in the Data Files or Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, and/or sell copies of the Data Files or Software, and
+ * to permit persons to whom the Data Files or Software are furnished to do
+ * so, provided that (a) the above copyright notice(s) and this permission
+ * notice appear with all copies of the Data Files or Software, (b) both the
+ * above copyright notice(s) and this permission notice appear in associated
+ * documentation, and (c) there is clear notice in each modified Data File or
+ * in the Software as well as in the documentation associated with the Data
+ * File(s) or Software that the data or software has been modified.
+ *
+ * THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ * KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
+ * THIRD PARTY RIGHTS.  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
+ * INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
+ * OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
+ * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THE DATA FILES OR SOFTWARE.
+ *
+ * Except as contained in this notice, the name of a copyright holder shall
+ * not be used in advertising or otherwise to promote the sale, use or other
+ * dealings in these Data Files or Software without prior written
+ * authorization of the copyright holder.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/nls.h>
+#include <linux/errno.h>
+
+static const wchar_t charset2uni[256] = {
+       /* 0x00 */
+       0x0000, 0x0001, 0x0002, 0x0003,
+       0x0004, 0x0005, 0x0006, 0x0007,
+       0x0008, 0x0009, 0x000a, 0x000b,
+       0x000c, 0x000d, 0x000e, 0x000f,
+       /* 0x10 */
+       0x0010, 0x0011, 0x0012, 0x0013,
+       0x0014, 0x0015, 0x0016, 0x0017,
+       0x0018, 0x0019, 0x001a, 0x001b,
+       0x001c, 0x001d, 0x001e, 0x001f,
+       /* 0x20 */
+       0x0020, 0x0021, 0x0022, 0x0023,
+       0x0024, 0x0025, 0x0026, 0x0027,
+       0x0028, 0x0029, 0x002a, 0x002b,
+       0x002c, 0x002d, 0x002e, 0x002f,
+       /* 0x30 */
+       0x0030, 0x0031, 0x0032, 0x0033,
+       0x0034, 0x0035, 0x0036, 0x0037,
+       0x0038, 0x0039, 0x003a, 0x003b,
+       0x003c, 0x003d, 0x003e, 0x003f,
+       /* 0x40 */
+       0x0040, 0x0041, 0x0042, 0x0043,
+       0x0044, 0x0045, 0x0046, 0x0047,
+       0x0048, 0x0049, 0x004a, 0x004b,
+       0x004c, 0x004d, 0x004e, 0x004f,
+       /* 0x50 */
+       0x0050, 0x0051, 0x0052, 0x0053,
+       0x0054, 0x0055, 0x0056, 0x0057,
+       0x0058, 0x0059, 0x005a, 0x005b,
+       0x005c, 0x005d, 0x005e, 0x005f,
+       /* 0x60 */
+       0x0060, 0x0061, 0x0062, 0x0063,
+       0x0064, 0x0065, 0x0066, 0x0067,
+       0x0068, 0x0069, 0x006a, 0x006b,
+       0x006c, 0x006d, 0x006e, 0x006f,
+       /* 0x70 */
+       0x0070, 0x0071, 0x0072, 0x0073,
+       0x0074, 0x0075, 0x0076, 0x0077,
+       0x0078, 0x0079, 0x007a, 0x007b,
+       0x007c, 0x007d, 0x007e, 0x007f,
+       /* 0x80 */
+       0x00c4, 0x00c5, 0x00c7, 0x00c9,
+       0x00d1, 0x00d6, 0x00dc, 0x00e1,
+       0x00e0, 0x00e2, 0x00e4, 0x00e3,
+       0x00e5, 0x00e7, 0x00e9, 0x00e8,
+       /* 0x90 */
+       0x00ea, 0x00eb, 0x00ed, 0x00ec,
+       0x00ee, 0x00ef, 0x00f1, 0x00f3,
+       0x00f2, 0x00f4, 0x00f6, 0x00f5,
+       0x00fa, 0x00f9, 0x00fb, 0x00fc,
+       /* 0xa0 */
+       0x2020, 0x00b0, 0x00a2, 0x00a3,
+       0x00a7, 0x2022, 0x00b6, 0x00df,
+       0x00ae, 0x00a9, 0x2122, 0x00b4,
+       0x00a8, 0x2260, 0x0102, 0x0218,
+       /* 0xb0 */
+       0x221e, 0x00b1, 0x2264, 0x2265,
+       0x00a5, 0x00b5, 0x2202, 0x2211,
+       0x220f, 0x03c0, 0x222b, 0x00aa,
+       0x00ba, 0x03a9, 0x0103, 0x0219,
+       /* 0xc0 */
+       0x00bf, 0x00a1, 0x00ac, 0x221a,
+       0x0192, 0x2248, 0x2206, 0x00ab,
+       0x00bb, 0x2026, 0x00a0, 0x00c0,
+       0x00c3, 0x00d5, 0x0152, 0x0153,
+       /* 0xd0 */
+       0x2013, 0x2014, 0x201c, 0x201d,
+       0x2018, 0x2019, 0x00f7, 0x25ca,
+       0x00ff, 0x0178, 0x2044, 0x20ac,
+       0x2039, 0x203a, 0x021a, 0x021b,
+       /* 0xe0 */
+       0x2021, 0x00b7, 0x201a, 0x201e,
+       0x2030, 0x00c2, 0x00ca, 0x00c1,
+       0x00cb, 0x00c8, 0x00cd, 0x00ce,
+       0x00cf, 0x00cc, 0x00d3, 0x00d4,
+       /* 0xf0 */
+       0xf8ff, 0x00d2, 0x00da, 0x00db,
+       0x00d9, 0x0131, 0x02c6, 0x02dc,
+       0x00af, 0x02d8, 0x02d9, 0x02da,
+       0x00b8, 0x02dd, 0x02db, 0x02c7,
+};
+
+static const unsigned char page00[256] = {
+       0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+       0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+       0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+       0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+       0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+       0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+       0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+       0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+       0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+       0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+       0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+       0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+       0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+       0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+       0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+       0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0xca, 0xc1, 0xa2, 0xa3, 0x00, 0xb4, 0x00, 0xa4, /* 0xa0-0xa7 */
+       0xac, 0xa9, 0xbb, 0xc7, 0xc2, 0x00, 0xa8, 0xf8, /* 0xa8-0xaf */
+       0xa1, 0xb1, 0x00, 0x00, 0xab, 0xb5, 0xa6, 0xe1, /* 0xb0-0xb7 */
+       0xfc, 0x00, 0xbc, 0xc8, 0x00, 0x00, 0x00, 0xc0, /* 0xb8-0xbf */
+       0xcb, 0xe7, 0xe5, 0xcc, 0x80, 0x81, 0x00, 0x82, /* 0xc0-0xc7 */
+       0xe9, 0x83, 0xe6, 0xe8, 0xed, 0xea, 0xeb, 0xec, /* 0xc8-0xcf */
+       0x00, 0x84, 0xf1, 0xee, 0xef, 0xcd, 0x85, 0x00, /* 0xd0-0xd7 */
+       0x00, 0xf4, 0xf2, 0xf3, 0x86, 0x00, 0x00, 0xa7, /* 0xd8-0xdf */
+       0x88, 0x87, 0x89, 0x8b, 0x8a, 0x8c, 0x00, 0x8d, /* 0xe0-0xe7 */
+       0x8f, 0x8e, 0x90, 0x91, 0x93, 0x92, 0x94, 0x95, /* 0xe8-0xef */
+       0x00, 0x96, 0x98, 0x97, 0x99, 0x9b, 0x9a, 0xd6, /* 0xf0-0xf7 */
+       0x00, 0x9d, 0x9c, 0x9e, 0x9f, 0x00, 0x00, 0xd8, /* 0xf8-0xff */
+};
+
+static const unsigned char page01[256] = {
+       0x00, 0x00, 0xae, 0xbe, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0x00, 0xf5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0x00, 0x00, 0xce, 0xcf, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0xd9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0xc4, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char page02[256] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+       0xaf, 0xbf, 0xde, 0xdf, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xff, /* 0xc0-0xc7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0xf9, 0xfa, 0xfb, 0xfe, 0xf7, 0xfd, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char page03[256] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0xbd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0xb9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char page20[256] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+       0x00, 0x00, 0x00, 0xd0, 0xd1, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+       0xd4, 0xd5, 0xe2, 0x00, 0xd2, 0xd3, 0xe3, 0x00, /* 0x18-0x1f */
+       0xa0, 0xe0, 0xa5, 0x00, 0x00, 0x00, 0xc9, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0xe4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0xdc, 0xdd, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0x00, 0x00, 0x00, 0x00, 0xda, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0x00, 0x00, 0x00, 0xdb, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char page21[256] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+       0x00, 0x00, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char page22[256] = {
+       0x00, 0x00, 0xb6, 0x00, 0x00, 0x00, 0xc6, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb8, /* 0x08-0x0f */
+       0x00, 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+       0x00, 0x00, 0xc3, 0x00, 0x00, 0x00, 0xb0, 0x00, /* 0x18-0x1f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0xba, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0xc5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0xad, 0x00, 0x00, 0x00, 0xb2, 0xb3, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char page25[256] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+       0x00, 0x00, 0xd7, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char pagef8[256] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, /* 0xf8-0xff */
+};
+
+static const unsigned char *const page_uni2charset[256] = {
+       page00, page01, page02, page03, NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       page20, page21, page22, NULL,   NULL,   page25, NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       pagef8, NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+};
+
+static const unsigned char charset2lower[256] = {
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x00-0x07 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x08-0x0f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x10-0x17 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x18-0x1f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x20-0x27 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x28-0x2f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x30-0x37 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x38-0x3f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x40-0x47 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x48-0x4f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x50-0x57 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x58-0x5f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x60-0x67 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x68-0x6f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x70-0x77 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x78-0x7f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x80-0x87 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x88-0x8f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x90-0x97 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x98-0x9f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xa0-0xa7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xa8-0xaf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xb0-0xb7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xb8-0xbf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xc0-0xc7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xc8-0xcf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xd0-0xd7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xd8-0xdf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xe0-0xe7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xe8-0xef */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xf0-0xf7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xf8-0xff */
+};
+
+static const unsigned char charset2upper[256] = {
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x00-0x07 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x08-0x0f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x10-0x17 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x18-0x1f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x20-0x27 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x28-0x2f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x30-0x37 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x38-0x3f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x40-0x47 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x48-0x4f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x50-0x57 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x58-0x5f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x60-0x67 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x68-0x6f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x70-0x77 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x78-0x7f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x80-0x87 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x88-0x8f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x90-0x97 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x98-0x9f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xa0-0xa7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xa8-0xaf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xb0-0xb7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xb8-0xbf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xc0-0xc7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xc8-0xcf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xd0-0xd7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xd8-0xdf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xe0-0xe7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xe8-0xef */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xf0-0xf7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xf8-0xff */
+};
+
+static int uni2char(wchar_t uni, unsigned char *out, int boundlen)
+{
+       const unsigned char *uni2charset;
+       unsigned char cl = uni & 0x00ff;
+       unsigned char ch = (uni & 0xff00) >> 8;
+
+       if (boundlen <= 0)
+               return -ENAMETOOLONG;
+
+       uni2charset = page_uni2charset[ch];
+       if (uni2charset && uni2charset[cl])
+               out[0] = uni2charset[cl];
+       else
+               return -EINVAL;
+       return 1;
+}
+
+static int char2uni(const unsigned char *rawstring, int boundlen, wchar_t *uni)
+{
+       *uni = charset2uni[*rawstring];
+       if (*uni == 0x0000)
+               return -EINVAL;
+       return 1;
+}
+
+static struct nls_table table = {
+       .charset        = "macromanian",
+       .uni2char       = uni2char,
+       .char2uni       = char2uni,
+       .charset2lower  = charset2lower,
+       .charset2upper  = charset2upper,
+       .owner          = THIS_MODULE,
+};
+
+static int __init init_nls_macromanian(void)
+{
+       return register_nls(&table);
+}
+
+static void __exit exit_nls_macromanian(void)
+{
+       unregister_nls(&table);
+}
+
+module_init(init_nls_macromanian)
+module_exit(exit_nls_macromanian)
+
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/fs/nls/nls_macturkish.c b/fs/nls/nls_macturkish.c
new file mode 100644 (file)
index 0000000..0cc2c65
--- /dev/null
@@ -0,0 +1,602 @@
+/*
+ * linux/fs/nls/nls_macturkish.c
+ *
+ * Charset macturkish translation tables.
+ * Generated automatically from the Unicode and charset
+ * tables from the Unicode Organization (www.unicode.org).
+ * The Unicode to charset table has only exact mappings.
+ */
+
+/*
+ * COPYRIGHT AND PERMISSION NOTICE
+ *
+ * Copyright 1991-2012 Unicode, Inc.  All rights reserved.  Distributed under
+ * the Terms of Use in http://www.unicode.org/copyright.html.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of the Unicode data files and any associated documentation (the "Data
+ * Files") or Unicode software and any associated documentation (the
+ * "Software") to deal in the Data Files or Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, and/or sell copies of the Data Files or Software, and
+ * to permit persons to whom the Data Files or Software are furnished to do
+ * so, provided that (a) the above copyright notice(s) and this permission
+ * notice appear with all copies of the Data Files or Software, (b) both the
+ * above copyright notice(s) and this permission notice appear in associated
+ * documentation, and (c) there is clear notice in each modified Data File or
+ * in the Software as well as in the documentation associated with the Data
+ * File(s) or Software that the data or software has been modified.
+ *
+ * THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ * KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
+ * THIRD PARTY RIGHTS.  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
+ * INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
+ * OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
+ * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THE DATA FILES OR SOFTWARE.
+ *
+ * Except as contained in this notice, the name of a copyright holder shall
+ * not be used in advertising or otherwise to promote the sale, use or other
+ * dealings in these Data Files or Software without prior written
+ * authorization of the copyright holder.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/nls.h>
+#include <linux/errno.h>
+
+static const wchar_t charset2uni[256] = {
+       /* 0x00 */
+       0x0000, 0x0001, 0x0002, 0x0003,
+       0x0004, 0x0005, 0x0006, 0x0007,
+       0x0008, 0x0009, 0x000a, 0x000b,
+       0x000c, 0x000d, 0x000e, 0x000f,
+       /* 0x10 */
+       0x0010, 0x0011, 0x0012, 0x0013,
+       0x0014, 0x0015, 0x0016, 0x0017,
+       0x0018, 0x0019, 0x001a, 0x001b,
+       0x001c, 0x001d, 0x001e, 0x001f,
+       /* 0x20 */
+       0x0020, 0x0021, 0x0022, 0x0023,
+       0x0024, 0x0025, 0x0026, 0x0027,
+       0x0028, 0x0029, 0x002a, 0x002b,
+       0x002c, 0x002d, 0x002e, 0x002f,
+       /* 0x30 */
+       0x0030, 0x0031, 0x0032, 0x0033,
+       0x0034, 0x0035, 0x0036, 0x0037,
+       0x0038, 0x0039, 0x003a, 0x003b,
+       0x003c, 0x003d, 0x003e, 0x003f,
+       /* 0x40 */
+       0x0040, 0x0041, 0x0042, 0x0043,
+       0x0044, 0x0045, 0x0046, 0x0047,
+       0x0048, 0x0049, 0x004a, 0x004b,
+       0x004c, 0x004d, 0x004e, 0x004f,
+       /* 0x50 */
+       0x0050, 0x0051, 0x0052, 0x0053,
+       0x0054, 0x0055, 0x0056, 0x0057,
+       0x0058, 0x0059, 0x005a, 0x005b,
+       0x005c, 0x005d, 0x005e, 0x005f,
+       /* 0x60 */
+       0x0060, 0x0061, 0x0062, 0x0063,
+       0x0064, 0x0065, 0x0066, 0x0067,
+       0x0068, 0x0069, 0x006a, 0x006b,
+       0x006c, 0x006d, 0x006e, 0x006f,
+       /* 0x70 */
+       0x0070, 0x0071, 0x0072, 0x0073,
+       0x0074, 0x0075, 0x0076, 0x0077,
+       0x0078, 0x0079, 0x007a, 0x007b,
+       0x007c, 0x007d, 0x007e, 0x007f,
+       /* 0x80 */
+       0x00c4, 0x00c5, 0x00c7, 0x00c9,
+       0x00d1, 0x00d6, 0x00dc, 0x00e1,
+       0x00e0, 0x00e2, 0x00e4, 0x00e3,
+       0x00e5, 0x00e7, 0x00e9, 0x00e8,
+       /* 0x90 */
+       0x00ea, 0x00eb, 0x00ed, 0x00ec,
+       0x00ee, 0x00ef, 0x00f1, 0x00f3,
+       0x00f2, 0x00f4, 0x00f6, 0x00f5,
+       0x00fa, 0x00f9, 0x00fb, 0x00fc,
+       /* 0xa0 */
+       0x2020, 0x00b0, 0x00a2, 0x00a3,
+       0x00a7, 0x2022, 0x00b6, 0x00df,
+       0x00ae, 0x00a9, 0x2122, 0x00b4,
+       0x00a8, 0x2260, 0x00c6, 0x00d8,
+       /* 0xb0 */
+       0x221e, 0x00b1, 0x2264, 0x2265,
+       0x00a5, 0x00b5, 0x2202, 0x2211,
+       0x220f, 0x03c0, 0x222b, 0x00aa,
+       0x00ba, 0x03a9, 0x00e6, 0x00f8,
+       /* 0xc0 */
+       0x00bf, 0x00a1, 0x00ac, 0x221a,
+       0x0192, 0x2248, 0x2206, 0x00ab,
+       0x00bb, 0x2026, 0x00a0, 0x00c0,
+       0x00c3, 0x00d5, 0x0152, 0x0153,
+       /* 0xd0 */
+       0x2013, 0x2014, 0x201c, 0x201d,
+       0x2018, 0x2019, 0x00f7, 0x25ca,
+       0x00ff, 0x0178, 0x011e, 0x011f,
+       0x0130, 0x0131, 0x015e, 0x015f,
+       /* 0xe0 */
+       0x2021, 0x00b7, 0x201a, 0x201e,
+       0x2030, 0x00c2, 0x00ca, 0x00c1,
+       0x00cb, 0x00c8, 0x00cd, 0x00ce,
+       0x00cf, 0x00cc, 0x00d3, 0x00d4,
+       /* 0xf0 */
+       0xf8ff, 0x00d2, 0x00da, 0x00db,
+       0x00d9, 0xf8a0, 0x02c6, 0x02dc,
+       0x00af, 0x02d8, 0x02d9, 0x02da,
+       0x00b8, 0x02dd, 0x02db, 0x02c7,
+};
+
+static const unsigned char page00[256] = {
+       0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+       0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+       0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+       0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+       0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+       0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+       0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+       0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+       0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+       0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+       0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+       0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+       0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+       0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+       0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+       0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0xca, 0xc1, 0xa2, 0xa3, 0x00, 0xb4, 0x00, 0xa4, /* 0xa0-0xa7 */
+       0xac, 0xa9, 0xbb, 0xc7, 0xc2, 0x00, 0xa8, 0xf8, /* 0xa8-0xaf */
+       0xa1, 0xb1, 0x00, 0x00, 0xab, 0xb5, 0xa6, 0xe1, /* 0xb0-0xb7 */
+       0xfc, 0x00, 0xbc, 0xc8, 0x00, 0x00, 0x00, 0xc0, /* 0xb8-0xbf */
+       0xcb, 0xe7, 0xe5, 0xcc, 0x80, 0x81, 0xae, 0x82, /* 0xc0-0xc7 */
+       0xe9, 0x83, 0xe6, 0xe8, 0xed, 0xea, 0xeb, 0xec, /* 0xc8-0xcf */
+       0x00, 0x84, 0xf1, 0xee, 0xef, 0xcd, 0x85, 0x00, /* 0xd0-0xd7 */
+       0xaf, 0xf4, 0xf2, 0xf3, 0x86, 0x00, 0x00, 0xa7, /* 0xd8-0xdf */
+       0x88, 0x87, 0x89, 0x8b, 0x8a, 0x8c, 0xbe, 0x8d, /* 0xe0-0xe7 */
+       0x8f, 0x8e, 0x90, 0x91, 0x93, 0x92, 0x94, 0x95, /* 0xe8-0xef */
+       0x00, 0x96, 0x98, 0x97, 0x99, 0x9b, 0x9a, 0xd6, /* 0xf0-0xf7 */
+       0xbf, 0x9d, 0x9c, 0x9e, 0x9f, 0x00, 0x00, 0xd8, /* 0xf8-0xff */
+};
+
+static const unsigned char page01[256] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xda, 0xdb, /* 0x18-0x1f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0xdc, 0xdd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0x00, 0x00, 0xce, 0xcf, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0xdf, /* 0x58-0x5f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0xd9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0xc4, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char page02[256] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xff, /* 0xc0-0xc7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0xf9, 0xfa, 0xfb, 0xfe, 0xf7, 0xfd, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char page03[256] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0xbd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0xb9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char page20[256] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+       0x00, 0x00, 0x00, 0xd0, 0xd1, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+       0xd4, 0xd5, 0xe2, 0x00, 0xd2, 0xd3, 0xe3, 0x00, /* 0x18-0x1f */
+       0xa0, 0xe0, 0xa5, 0x00, 0x00, 0x00, 0xc9, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0xe4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char page21[256] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+       0x00, 0x00, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char page22[256] = {
+       0x00, 0x00, 0xb6, 0x00, 0x00, 0x00, 0xc6, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb8, /* 0x08-0x0f */
+       0x00, 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+       0x00, 0x00, 0xc3, 0x00, 0x00, 0x00, 0xb0, 0x00, /* 0x18-0x1f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0xba, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0xc5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0xad, 0x00, 0x00, 0x00, 0xb2, 0xb3, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char page25[256] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+       0x00, 0x00, 0xd7, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static const unsigned char pagef8[256] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+       0xf5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, /* 0xf8-0xff */
+};
+
+static const unsigned char *const page_uni2charset[256] = {
+       page00, page01, page02, page03, NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       page20, page21, page22, NULL,   NULL,   page25, NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+       pagef8, NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+};
+
+static const unsigned char charset2lower[256] = {
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x00-0x07 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x08-0x0f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x10-0x17 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x18-0x1f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x20-0x27 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x28-0x2f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x30-0x37 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x38-0x3f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x40-0x47 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x48-0x4f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x50-0x57 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x58-0x5f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x60-0x67 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x68-0x6f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x70-0x77 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x78-0x7f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x80-0x87 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x88-0x8f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x90-0x97 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x98-0x9f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xa0-0xa7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xa8-0xaf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xb0-0xb7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xb8-0xbf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xc0-0xc7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xc8-0xcf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xd0-0xd7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xd8-0xdf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xe0-0xe7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xe8-0xef */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xf0-0xf7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xf8-0xff */
+};
+
+static const unsigned char charset2upper[256] = {
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x00-0x07 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x08-0x0f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x10-0x17 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x18-0x1f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x20-0x27 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x28-0x2f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x30-0x37 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x38-0x3f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x40-0x47 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x48-0x4f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x50-0x57 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x58-0x5f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x60-0x67 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x68-0x6f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x70-0x77 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x78-0x7f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x80-0x87 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x88-0x8f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x90-0x97 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x98-0x9f */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xa0-0xa7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xa8-0xaf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xb0-0xb7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xb8-0xbf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xc0-0xc7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xc8-0xcf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xd0-0xd7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xd8-0xdf */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xe0-0xe7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xe8-0xef */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xf0-0xf7 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xf8-0xff */
+};
+
+static int uni2char(wchar_t uni, unsigned char *out, int boundlen)
+{
+       const unsigned char *uni2charset;
+       unsigned char cl = uni & 0x00ff;
+       unsigned char ch = (uni & 0xff00) >> 8;
+
+       if (boundlen <= 0)
+               return -ENAMETOOLONG;
+
+       uni2charset = page_uni2charset[ch];
+       if (uni2charset && uni2charset[cl])
+               out[0] = uni2charset[cl];
+       else
+               return -EINVAL;
+       return 1;
+}
+
+static int char2uni(const unsigned char *rawstring, int boundlen, wchar_t *uni)
+{
+       *uni = charset2uni[*rawstring];
+       if (*uni == 0x0000)
+               return -EINVAL;
+       return 1;
+}
+
+static struct nls_table table = {
+       .charset        = "macturkish",
+       .uni2char       = uni2char,
+       .char2uni       = char2uni,
+       .charset2lower  = charset2lower,
+       .charset2upper  = charset2upper,
+       .owner          = THIS_MODULE,
+};
+
+static int __init init_nls_macturkish(void)
+{
+       return register_nls(&table);
+}
+
+static void __exit exit_nls_macturkish(void)
+{
+       unregister_nls(&table);
+}
+
+module_init(init_nls_macturkish)
+module_exit(exit_nls_macturkish)
+
+MODULE_LICENSE("Dual BSD/GPL");
index ccb14d3fc0de99790d282ae17ce984338ca309ab..b39c5c161adb64bff0d33faa64f41d8f4a9942cd 100644 (file)
@@ -123,7 +123,7 @@ int __fsnotify_parent(struct path *path, struct dentry *dentry, __u32 mask)
 }
 EXPORT_SYMBOL_GPL(__fsnotify_parent);
 
-static int send_to_group(struct inode *to_tell, struct vfsmount *mnt,
+static int send_to_group(struct inode *to_tell,
                         struct fsnotify_mark *inode_mark,
                         struct fsnotify_mark *vfsmount_mark,
                         __u32 mask, void *data,
@@ -168,10 +168,10 @@ static int send_to_group(struct inode *to_tell, struct vfsmount *mnt,
                        vfsmount_test_mask &= ~inode_mark->ignored_mask;
        }
 
-       pr_debug("%s: group=%p to_tell=%p mnt=%p mask=%x inode_mark=%p"
+       pr_debug("%s: group=%p to_tell=%p mask=%x inode_mark=%p"
                 " inode_test_mask=%x vfsmount_mark=%p vfsmount_test_mask=%x"
                 " data=%p data_is=%d cookie=%d event=%p\n",
-                __func__, group, to_tell, mnt, mask, inode_mark,
+                __func__, group, to_tell, mask, inode_mark,
                 inode_test_mask, vfsmount_mark, vfsmount_test_mask, data,
                 data_is, cookie, *event);
 
@@ -258,16 +258,16 @@ int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is,
 
                if (inode_group > vfsmount_group) {
                        /* handle inode */
-                       ret = send_to_group(to_tell, NULL, inode_mark, NULL, mask, data,
+                       ret = send_to_group(to_tell, inode_mark, NULL, mask, data,
                                            data_is, cookie, file_name, &event);
                        /* we didn't use the vfsmount_mark */
                        vfsmount_group = NULL;
                } else if (vfsmount_group > inode_group) {
-                       ret = send_to_group(to_tell, &mnt->mnt, NULL, vfsmount_mark, mask, data,
+                       ret = send_to_group(to_tell, NULL, vfsmount_mark, mask, data,
                                            data_is, cookie, file_name, &event);
                        inode_group = NULL;
                } else {
-                       ret = send_to_group(to_tell, &mnt->mnt, inode_mark, vfsmount_mark,
+                       ret = send_to_group(to_tell, inode_mark, vfsmount_mark,
                                            mask, data, data_is, cookie, file_name,
                                            &event);
                }
index 8639169221c7aed21c0bd600ab4ef1a0d8102cb1..7389d2d5e51d257c72f9fb0c1468c38a28b309e4 100644 (file)
@@ -2096,7 +2096,9 @@ static ssize_t ntfs_file_aio_write_nolock(struct kiocb *iocb,
        err = file_remove_suid(file);
        if (err)
                goto out;
-       file_update_time(file);
+       err = file_update_time(file);
+       if (err)
+               goto out;
        written = ntfs_file_buffered_write(iocb, iov, nr_segs, pos, ppos,
                        count);
 out:
index c7ee03c22226253d970cce94beb11f6353b3e1d0..0725e605465040b6b1e7c5e7744c5243968158c9 100644 (file)
@@ -422,45 +422,46 @@ int ocfs2_block_check_validate(void *data, size_t blocksize,
                               struct ocfs2_blockcheck_stats *stats)
 {
        int rc = 0;
-       struct ocfs2_block_check check;
+       u32 bc_crc32e;
+       u16 bc_ecc;
        u32 crc, ecc;
 
        ocfs2_blockcheck_inc_check(stats);
 
-       check.bc_crc32e = le32_to_cpu(bc->bc_crc32e);
-       check.bc_ecc = le16_to_cpu(bc->bc_ecc);
+       bc_crc32e = le32_to_cpu(bc->bc_crc32e);
+       bc_ecc = le16_to_cpu(bc->bc_ecc);
 
        memset(bc, 0, sizeof(struct ocfs2_block_check));
 
        /* Fast path - if the crc32 validates, we're good to go */
        crc = crc32_le(~0, data, blocksize);
-       if (crc == check.bc_crc32e)
+       if (crc == bc_crc32e)
                goto out;
 
        ocfs2_blockcheck_inc_failure(stats);
        mlog(ML_ERROR,
             "CRC32 failed: stored: 0x%x, computed 0x%x. Applying ECC.\n",
-            (unsigned int)check.bc_crc32e, (unsigned int)crc);
+            (unsigned int)bc_crc32e, (unsigned int)crc);
 
        /* Ok, try ECC fixups */
        ecc = ocfs2_hamming_encode_block(data, blocksize);
-       ocfs2_hamming_fix_block(data, blocksize, ecc ^ check.bc_ecc);
+       ocfs2_hamming_fix_block(data, blocksize, ecc ^ bc_ecc);
 
        /* And check the crc32 again */
        crc = crc32_le(~0, data, blocksize);
-       if (crc == check.bc_crc32e) {
+       if (crc == bc_crc32e) {
                ocfs2_blockcheck_inc_recover(stats);
                goto out;
        }
 
        mlog(ML_ERROR, "Fixed CRC32 failed: stored: 0x%x, computed 0x%x\n",
-            (unsigned int)check.bc_crc32e, (unsigned int)crc);
+            (unsigned int)bc_crc32e, (unsigned int)crc);
 
        rc = -EIO;
 
 out:
-       bc->bc_crc32e = cpu_to_le32(check.bc_crc32e);
-       bc->bc_ecc = cpu_to_le16(check.bc_ecc);
+       bc->bc_crc32e = cpu_to_le32(bc_crc32e);
+       bc->bc_ecc = cpu_to_le16(bc_ecc);
 
        return rc;
 }
@@ -528,7 +529,8 @@ int ocfs2_block_check_validate_bhs(struct buffer_head **bhs, int nr,
                                   struct ocfs2_blockcheck_stats *stats)
 {
        int i, rc = 0;
-       struct ocfs2_block_check check;
+       u32 bc_crc32e;
+       u16 bc_ecc;
        u32 crc, ecc, fix;
 
        BUG_ON(nr < 0);
@@ -538,21 +540,21 @@ int ocfs2_block_check_validate_bhs(struct buffer_head **bhs, int nr,
 
        ocfs2_blockcheck_inc_check(stats);
 
-       check.bc_crc32e = le32_to_cpu(bc->bc_crc32e);
-       check.bc_ecc = le16_to_cpu(bc->bc_ecc);
+       bc_crc32e = le32_to_cpu(bc->bc_crc32e);
+       bc_ecc = le16_to_cpu(bc->bc_ecc);
 
        memset(bc, 0, sizeof(struct ocfs2_block_check));
 
        /* Fast path - if the crc32 validates, we're good to go */
        for (i = 0, crc = ~0; i < nr; i++)
                crc = crc32_le(crc, bhs[i]->b_data, bhs[i]->b_size);
-       if (crc == check.bc_crc32e)
+       if (crc == bc_crc32e)
                goto out;
 
        ocfs2_blockcheck_inc_failure(stats);
        mlog(ML_ERROR,
             "CRC32 failed: stored: %u, computed %u.  Applying ECC.\n",
-            (unsigned int)check.bc_crc32e, (unsigned int)crc);
+            (unsigned int)bc_crc32e, (unsigned int)crc);
 
        /* Ok, try ECC fixups */
        for (i = 0, ecc = 0; i < nr; i++) {
@@ -565,7 +567,7 @@ int ocfs2_block_check_validate_bhs(struct buffer_head **bhs, int nr,
                                                bhs[i]->b_size * 8,
                                                bhs[i]->b_size * 8 * i);
        }
-       fix = ecc ^ check.bc_ecc;
+       fix = ecc ^ bc_ecc;
        for (i = 0; i < nr; i++) {
                /*
                 * Try the fix against each buffer.  It will only affect
@@ -578,19 +580,19 @@ int ocfs2_block_check_validate_bhs(struct buffer_head **bhs, int nr,
        /* And check the crc32 again */
        for (i = 0, crc = ~0; i < nr; i++)
                crc = crc32_le(crc, bhs[i]->b_data, bhs[i]->b_size);
-       if (crc == check.bc_crc32e) {
+       if (crc == bc_crc32e) {
                ocfs2_blockcheck_inc_recover(stats);
                goto out;
        }
 
        mlog(ML_ERROR, "Fixed CRC32 failed: stored: %u, computed %u\n",
-            (unsigned int)check.bc_crc32e, (unsigned int)crc);
+            (unsigned int)bc_crc32e, (unsigned int)crc);
 
        rc = -EIO;
 
 out:
-       bc->bc_crc32e = cpu_to_le32(check.bc_crc32e);
-       bc->bc_ecc = cpu_to_le16(check.bc_ecc);
+       bc->bc_crc32e = cpu_to_le32(bc_crc32e);
+       bc->bc_ecc = cpu_to_le16(bc_ecc);
 
        return rc;
 }
index 3a3ed4bb794b0d6c75e7e321b042b1b4128fbd27..fbec0be6232622ddda0c3ed4ed49c50cc0129386 100644 (file)
@@ -293,7 +293,7 @@ int dlm_proxy_ast_handler(struct o2net_msg *msg, u32 len, void *data,
        struct dlm_proxy_ast *past = (struct dlm_proxy_ast *) msg->buf;
        char *name;
        struct list_head *iter, *head=NULL;
-       u64 cookie;
+       __be64 cookie;
        u32 flags;
        u8 node;
 
index a5952ceecba5a83147389ad4a1cd24972ee0bfbe..de854cca12a2d23dea5652d3dad38461c7dbde13 100644 (file)
@@ -679,7 +679,7 @@ struct dlm_query_join_packet {
 };
 
 union dlm_query_join_response {
-       u32 intval;
+       __be32 intval;
        struct dlm_query_join_packet packet;
 };
 
@@ -755,8 +755,8 @@ struct dlm_query_region {
 struct dlm_node_info {
        u8 ni_nodenum;
        u8 pad1;
-       u16 ni_ipv4_port;
-       u32 ni_ipv4_address;
+       __be16 ni_ipv4_port;
+       __be32 ni_ipv4_address;
 };
 
 struct dlm_query_nodeinfo {
index 92f2ead0fab6de22fa138cc4410dee6e1544216c..9e89d70df337fc98836e87f90e38887a716843e6 100644 (file)
@@ -818,7 +818,7 @@ static void dlm_query_join_packet_to_wire(struct dlm_query_join_packet *packet,
        union dlm_query_join_response response;
 
        response.packet = *packet;
-       *wire = cpu_to_be32(response.intval);
+       *wire = be32_to_cpu(response.intval);
 }
 
 static void dlm_query_join_wire_to_packet(u32 wire,
index 745db42528d5fd2f875a099177158f347fd361e7..322216a5f0dd1e0f2e178540781b3c6fd263c985 100644 (file)
@@ -177,21 +177,23 @@ bail:
        return parent;
 }
 
-static int ocfs2_encode_fh(struct dentry *dentry, u32 *fh_in, int *max_len,
-                          int connectable)
+static int ocfs2_encode_fh(struct inode *inode, u32 *fh_in, int *max_len,
+                          struct inode *parent)
 {
-       struct inode *inode = dentry->d_inode;
        int len = *max_len;
        int type = 1;
        u64 blkno;
        u32 generation;
        __le32 *fh = (__force __le32 *) fh_in;
 
+#ifdef TRACE_HOOKS_ARE_NOT_BRAINDEAD_IN_YOUR_OPINION
+#error "You go ahead and fix that mess, then.  Somehow"
        trace_ocfs2_encode_fh_begin(dentry, dentry->d_name.len,
                                    dentry->d_name.name,
                                    fh, len, connectable);
+#endif
 
-       if (connectable && (len < 6)) {
+       if (parent && (len < 6)) {
                *max_len = 6;
                type = 255;
                goto bail;
@@ -211,12 +213,7 @@ static int ocfs2_encode_fh(struct dentry *dentry, u32 *fh_in, int *max_len,
        fh[1] = cpu_to_le32((u32)(blkno & 0xffffffff));
        fh[2] = cpu_to_le32(generation);
 
-       if (connectable && !S_ISDIR(inode->i_mode)) {
-               struct inode *parent;
-
-               spin_lock(&dentry->d_lock);
-
-               parent = dentry->d_parent->d_inode;
+       if (parent) {
                blkno = OCFS2_I(parent)->ip_blkno;
                generation = parent->i_generation;
 
@@ -224,8 +221,6 @@ static int ocfs2_encode_fh(struct dentry *dentry, u32 *fh_in, int *max_len,
                fh[4] = cpu_to_le32((u32)(blkno & 0xffffffff));
                fh[5] = cpu_to_le32(generation);
 
-               spin_unlock(&dentry->d_lock);
-
                len = 6;
                type = 2;
 
index 735514ca400f7942268dff8f387c7c029506b859..d89e08a81eda8875fcd59d76c5e1d75827e7d7ac 100644 (file)
@@ -273,11 +273,13 @@ void ocfs2_populate_inode(struct inode *inode, struct ocfs2_dinode *fe,
        inode->i_gid = le32_to_cpu(fe->i_gid);
 
        /* Fast symlinks will have i_size but no allocated clusters. */
-       if (S_ISLNK(inode->i_mode) && !fe->i_clusters)
+       if (S_ISLNK(inode->i_mode) && !fe->i_clusters) {
                inode->i_blocks = 0;
-       else
+               inode->i_mapping->a_ops = &ocfs2_fast_symlink_aops;
+       } else {
                inode->i_blocks = ocfs2_inode_sector_count(inode);
-       inode->i_mapping->a_ops = &ocfs2_aops;
+               inode->i_mapping->a_ops = &ocfs2_aops;
+       }
        inode->i_atime.tv_sec = le64_to_cpu(fe->i_atime);
        inode->i_atime.tv_nsec = le32_to_cpu(fe->i_atime_nsec);
        inode->i_mtime.tv_sec = le64_to_cpu(fe->i_mtime);
@@ -331,10 +333,7 @@ void ocfs2_populate_inode(struct inode *inode, struct ocfs2_dinode *fe,
                    OCFS2_I(inode)->ip_dir_lock_gen = 1;
                    break;
            case S_IFLNK:
-                   if (ocfs2_inode_is_fast_symlink(inode))
-                       inode->i_op = &ocfs2_fast_symlink_inode_operations;
-                   else
-                       inode->i_op = &ocfs2_symlink_inode_operations;
+                   inode->i_op = &ocfs2_symlink_inode_operations;
                    i_size_write(inode, le64_to_cpu(fe->i_size));
                    break;
            default:
index a1a1bfd652c90d49521ad3ea12a908f9a168c1e9..d96f7f81d8dd3257f49bb02885db296af22881cb 100644 (file)
@@ -864,7 +864,7 @@ int ocfs2_info_handle(struct inode *inode, struct ocfs2_info *info,
                if (status)
                        break;
 
-               reqp = (struct ocfs2_info_request *)(unsigned long)req_addr;
+               reqp = (struct ocfs2_info_request __user *)(unsigned long)req_addr;
                if (!reqp) {
                        status = -EINVAL;
                        goto bail;
@@ -888,9 +888,11 @@ long ocfs2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
        struct ocfs2_space_resv sr;
        struct ocfs2_new_group_input input;
        struct reflink_arguments args;
-       const char *old_path, *new_path;
+       const char __user *old_path;
+       const char __user *new_path;
        bool preserve;
        struct ocfs2_info info;
+       void __user *argp = (void __user *)arg;
 
        switch (cmd) {
        case OCFS2_IOC_GETFLAGS:
@@ -937,17 +939,15 @@ long ocfs2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 
                return ocfs2_group_add(inode, &input);
        case OCFS2_IOC_REFLINK:
-               if (copy_from_user(&args, (struct reflink_arguments *)arg,
-                                  sizeof(args)))
+               if (copy_from_user(&args, argp, sizeof(args)))
                        return -EFAULT;
-               old_path = (const char *)(unsigned long)args.old_path;
-               new_path = (const char *)(unsigned long)args.new_path;
+               old_path = (const char __user *)(unsigned long)args.old_path;
+               new_path = (const char __user *)(unsigned long)args.new_path;
                preserve = (args.preserve != 0);
 
                return ocfs2_reflink_ioctl(inode, old_path, new_path, preserve);
        case OCFS2_IOC_INFO:
-               if (copy_from_user(&info, (struct ocfs2_info __user *)arg,
-                                  sizeof(struct ocfs2_info)))
+               if (copy_from_user(&info, argp, sizeof(struct ocfs2_info)))
                        return -EFAULT;
 
                return ocfs2_info_handle(inode, &info, 0);
@@ -960,22 +960,20 @@ long ocfs2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
                if (!capable(CAP_SYS_ADMIN))
                        return -EPERM;
 
-               if (copy_from_user(&range, (struct fstrim_range *)arg,
-                   sizeof(range)))
+               if (copy_from_user(&range, argp, sizeof(range)))
                        return -EFAULT;
 
                ret = ocfs2_trim_fs(sb, &range);
                if (ret < 0)
                        return ret;
 
-               if (copy_to_user((struct fstrim_range *)arg, &range,
-                   sizeof(range)))
+               if (copy_to_user(argp, &range, sizeof(range)))
                        return -EFAULT;
 
                return 0;
        }
        case OCFS2_IOC_MOVE_EXT:
-               return ocfs2_ioctl_move_extents(filp, (void __user *)arg);
+               return ocfs2_ioctl_move_extents(filp, argp);
        default:
                return -ENOTTY;
        }
@@ -988,6 +986,7 @@ long ocfs2_compat_ioctl(struct file *file, unsigned cmd, unsigned long arg)
        struct reflink_arguments args;
        struct inode *inode = file->f_path.dentry->d_inode;
        struct ocfs2_info info;
+       void __user *argp = (void __user *)arg;
 
        switch (cmd) {
        case OCFS2_IOC32_GETFLAGS:
@@ -1006,16 +1005,14 @@ long ocfs2_compat_ioctl(struct file *file, unsigned cmd, unsigned long arg)
        case FITRIM:
                break;
        case OCFS2_IOC_REFLINK:
-               if (copy_from_user(&args, (struct reflink_arguments *)arg,
-                                  sizeof(args)))
+               if (copy_from_user(&args, argp, sizeof(args)))
                        return -EFAULT;
                preserve = (args.preserve != 0);
 
                return ocfs2_reflink_ioctl(inode, compat_ptr(args.old_path),
                                           compat_ptr(args.new_path), preserve);
        case OCFS2_IOC_INFO:
-               if (copy_from_user(&info, (struct ocfs2_info __user *)arg,
-                                  sizeof(struct ocfs2_info)))
+               if (copy_from_user(&info, argp, sizeof(struct ocfs2_info)))
                        return -EFAULT;
 
                return ocfs2_info_handle(inode, &info, 1);
index b1e3fce72ea4767bf795c692e98faacebc42797c..6083432f667e3077eb466842ef0f00136d0b4b6f 100644 (file)
@@ -1082,8 +1082,7 @@ int ocfs2_ioctl_move_extents(struct file *filp, void __user *argp)
        context->file = filp;
 
        if (argp) {
-               if (copy_from_user(&range, (struct ocfs2_move_extents *)argp,
-                                  sizeof(range))) {
+               if (copy_from_user(&range, argp, sizeof(range))) {
                        status = -EFAULT;
                        goto out;
                }
@@ -1138,8 +1137,7 @@ out:
         * length and new_offset even if failure happens somewhere.
         */
        if (argp) {
-               if (copy_to_user((struct ocfs2_move_extents *)argp, &range,
-                               sizeof(range)))
+               if (copy_to_user(argp, &range, sizeof(range)))
                        status = -EFAULT;
        }
 
index a9856e3eaaf09753b4921d56ccdfb172db5cad7b..9f39c640cddf2076b951295dde5ef68217b26452 100644 (file)
@@ -1724,15 +1724,16 @@ static int ocfs2_symlink(struct inode *dir,
        fe = (struct ocfs2_dinode *) new_fe_bh->b_data;
        inode->i_rdev = 0;
        newsize = l - 1;
+       inode->i_op = &ocfs2_symlink_inode_operations;
        if (l > ocfs2_fast_symlink_chars(sb)) {
                u32 offset = 0;
 
-               inode->i_op = &ocfs2_symlink_inode_operations;
                status = dquot_alloc_space_nodirty(inode,
                    ocfs2_clusters_to_bytes(osb->sb, 1));
                if (status)
                        goto bail;
                did_quota = 1;
+               inode->i_mapping->a_ops = &ocfs2_aops;
                status = ocfs2_add_inode_data(osb, inode, &offset, 1, 0,
                                              new_fe_bh,
                                              handle, data_ac, NULL,
@@ -1750,7 +1751,7 @@ static int ocfs2_symlink(struct inode *dir,
                i_size_write(inode, newsize);
                inode->i_blocks = ocfs2_inode_sector_count(inode);
        } else {
-               inode->i_op = &ocfs2_fast_symlink_inode_operations;
+               inode->i_mapping->a_ops = &ocfs2_fast_symlink_aops;
                memcpy((char *) fe->id2.i_symlink, symname, l);
                i_size_write(inode, newsize);
                inode->i_blocks = 0;
index 5d22872e2bb36012b711ac7720a90bee24717b15..f1fbb4b552ad3649238becdd9c21d4b138d5c8d7 100644 (file)
 #include "buffer_head_io.h"
 
 
-static char *ocfs2_fast_symlink_getlink(struct inode *inode,
-                                       struct buffer_head **bh)
+static int ocfs2_fast_symlink_readpage(struct file *unused, struct page *page)
 {
-       int status;
-       char *link = NULL;
+       struct inode *inode = page->mapping->host;
+       struct buffer_head *bh;
+       int status = ocfs2_read_inode_block(inode, &bh);
        struct ocfs2_dinode *fe;
+       const char *link;
+       void *kaddr;
+       size_t len;
 
-       status = ocfs2_read_inode_block(inode, bh);
        if (status < 0) {
                mlog_errno(status);
-               link = ERR_PTR(status);
-               goto bail;
+               return status;
        }
 
-       fe = (struct ocfs2_dinode *) (*bh)->b_data;
+       fe = (struct ocfs2_dinode *) bh->b_data;
        link = (char *) fe->id2.i_symlink;
-bail:
-
-       return link;
-}
-
-static int ocfs2_readlink(struct dentry *dentry,
-                         char __user *buffer,
-                         int buflen)
-{
-       int ret;
-       char *link;
-       struct buffer_head *bh = NULL;
-       struct inode *inode = dentry->d_inode;
-
-       link = ocfs2_fast_symlink_getlink(inode, &bh);
-       if (IS_ERR(link)) {
-               ret = PTR_ERR(link);
-               goto out;
-       }
-
-       /*
-        * Without vfsmount we can't update atime now,
-        * but we will update atime here ultimately.
-        */
-       ret = vfs_readlink(dentry, buffer, buflen, link);
-
+       /* will be less than a page size */
+       len = strnlen(link, ocfs2_fast_symlink_chars(inode->i_sb));
+       kaddr = kmap_atomic(page);
+       memcpy(kaddr, link, len + 1);
+       kunmap_atomic(kaddr);
+       SetPageUptodate(page);
+       unlock_page(page);
        brelse(bh);
-out:
-       if (ret < 0)
-               mlog_errno(ret);
-       return ret;
+       return 0;
 }
 
-static void *ocfs2_fast_follow_link(struct dentry *dentry,
-                                   struct nameidata *nd)
-{
-       int status = 0;
-       int len;
-       char *target, *link = ERR_PTR(-ENOMEM);
-       struct inode *inode = dentry->d_inode;
-       struct buffer_head *bh = NULL;
-
-       BUG_ON(!ocfs2_inode_is_fast_symlink(inode));
-       target = ocfs2_fast_symlink_getlink(inode, &bh);
-       if (IS_ERR(target)) {
-               status = PTR_ERR(target);
-               mlog_errno(status);
-               goto bail;
-       }
-
-       /* Fast symlinks can't be large */
-       len = strnlen(target, ocfs2_fast_symlink_chars(inode->i_sb));
-       link = kzalloc(len + 1, GFP_NOFS);
-       if (!link) {
-               status = -ENOMEM;
-               mlog_errno(status);
-               goto bail;
-       }
-
-       memcpy(link, target, len);
-
-bail:
-       nd_set_link(nd, status ? ERR_PTR(status) : link);
-       brelse(bh);
-
-       if (status)
-               mlog_errno(status);
-       return NULL;
-}
-
-static void ocfs2_fast_put_link(struct dentry *dentry, struct nameidata *nd, void *cookie)
-{
-       char *link = nd_get_link(nd);
-       if (!IS_ERR(link))
-               kfree(link);
-}
+const struct address_space_operations ocfs2_fast_symlink_aops = {
+       .readpage               = ocfs2_fast_symlink_readpage,
+};
 
 const struct inode_operations ocfs2_symlink_inode_operations = {
-       .readlink       = page_readlink,
+       .readlink       = generic_readlink,
        .follow_link    = page_follow_link_light,
        .put_link       = page_put_link,
        .getattr        = ocfs2_getattr,
@@ -159,15 +98,3 @@ const struct inode_operations ocfs2_symlink_inode_operations = {
        .removexattr    = generic_removexattr,
        .fiemap         = ocfs2_fiemap,
 };
-const struct inode_operations ocfs2_fast_symlink_inode_operations = {
-       .readlink       = ocfs2_readlink,
-       .follow_link    = ocfs2_fast_follow_link,
-       .put_link       = ocfs2_fast_put_link,
-       .getattr        = ocfs2_getattr,
-       .setattr        = ocfs2_setattr,
-       .setxattr       = generic_setxattr,
-       .getxattr       = generic_getxattr,
-       .listxattr      = ocfs2_listxattr,
-       .removexattr    = generic_removexattr,
-       .fiemap         = ocfs2_fiemap,
-};
index 65a6c9c6ad51d1018147cff4685743dd22bae935..71ee4245e9192274552ef9492412b36b068e72d6 100644 (file)
@@ -27,7 +27,7 @@
 #define OCFS2_SYMLINK_H
 
 extern const struct inode_operations ocfs2_symlink_inode_operations;
-extern const struct inode_operations ocfs2_fast_symlink_inode_operations;
+extern const struct address_space_operations ocfs2_fast_symlink_aops;
 
 /*
  * Test whether an inode is a fast symlink.
index d54301219d04f1c8fed18d6de15ed590a593e2ab..d6c79a0dffc7b0827b09562e11fa0f610af5657d 100644 (file)
--- a/fs/open.c
+++ b/fs/open.c
@@ -654,10 +654,23 @@ static inline int __get_file_write_access(struct inode *inode,
        return error;
 }
 
-static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt,
-                                       struct file *f,
-                                       int (*open)(struct inode *, struct file *),
-                                       const struct cred *cred)
+int open_check_o_direct(struct file *f)
+{
+       /* NB: we're sure to have correct a_ops only after f_op->open */
+       if (f->f_flags & O_DIRECT) {
+               if (!f->f_mapping->a_ops ||
+                   ((!f->f_mapping->a_ops->direct_IO) &&
+                   (!f->f_mapping->a_ops->get_xip_mem))) {
+                       return -EINVAL;
+               }
+       }
+       return 0;
+}
+
+static struct file *do_dentry_open(struct dentry *dentry, struct vfsmount *mnt,
+                                  struct file *f,
+                                  int (*open)(struct inode *, struct file *),
+                                  const struct cred *cred)
 {
        static const struct file_operations empty_fops = {};
        struct inode *inode;
@@ -713,16 +726,6 @@ static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt,
 
        file_ra_state_init(&f->f_ra, f->f_mapping->host->i_mapping);
 
-       /* NB: we're sure to have correct a_ops only after f_op->open */
-       if (f->f_flags & O_DIRECT) {
-               if (!f->f_mapping->a_ops ||
-                   ((!f->f_mapping->a_ops->direct_IO) &&
-                   (!f->f_mapping->a_ops->get_xip_mem))) {
-                       fput(f);
-                       f = ERR_PTR(-EINVAL);
-               }
-       }
-
        return f;
 
 cleanup_all:
@@ -744,12 +747,29 @@ cleanup_all:
        f->f_path.dentry = NULL;
        f->f_path.mnt = NULL;
 cleanup_file:
-       put_filp(f);
        dput(dentry);
        mntput(mnt);
        return ERR_PTR(error);
 }
 
+static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt,
+                               struct file *f,
+                               int (*open)(struct inode *, struct file *),
+                               const struct cred *cred)
+{
+       struct file *res = do_dentry_open(dentry, mnt, f, open, cred);
+       if (!IS_ERR(res)) {
+               int error = open_check_o_direct(f);
+               if (error) {
+                       fput(res);
+                       res = ERR_PTR(error);
+               }
+       } else {
+               put_filp(f);
+       }
+       return res;
+}
+
 /**
  * lookup_instantiate_filp - instantiates the open intent filp
  * @nd: pointer to nameidata
@@ -804,13 +824,31 @@ struct file *nameidata_to_filp(struct nameidata *nd)
 
        /* Pick up the filp from the open intent */
        filp = nd->intent.open.file;
-       nd->intent.open.file = NULL;
 
        /* Has the filesystem initialised the file for us? */
-       if (filp->f_path.dentry == NULL) {
+       if (filp->f_path.dentry != NULL) {
+               nd->intent.open.file = NULL;
+       } else {
+               struct file *res;
+
                path_get(&nd->path);
-               filp = __dentry_open(nd->path.dentry, nd->path.mnt, filp,
-                                    NULL, cred);
+               res = do_dentry_open(nd->path.dentry, nd->path.mnt,
+                                    filp, NULL, cred);
+               if (!IS_ERR(res)) {
+                       int error;
+
+                       nd->intent.open.file = NULL;
+                       BUG_ON(res != filp);
+
+                       error = open_check_o_direct(filp);
+                       if (error) {
+                               fput(filp);
+                               filp = ERR_PTR(error);
+                       }
+               } else {
+                       /* Allow nd->intent.open.file to be recycled */
+                       filp = res;
+               }
        }
        return filp;
 }
index fec5e4ad071a36bb8783bdcc8c40c07c614340a5..49c1065256fd10d9d5fdca3cf449b1e56bd58a0a 100644 (file)
--- a/fs/pipe.c
+++ b/fs/pipe.c
@@ -654,8 +654,11 @@ out:
                wake_up_interruptible_sync_poll(&pipe->wait, POLLIN | POLLRDNORM);
                kill_fasync(&pipe->fasync_readers, SIGIO, POLL_IN);
        }
-       if (ret > 0)
-               file_update_time(filp);
+       if (ret > 0) {
+               int err = file_update_time(filp);
+               if (err)
+                       ret = err;
+       }
        return ret;
 }
 
@@ -693,7 +696,7 @@ static long pipe_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 
                        return put_user(count, (int __user *)arg);
                default:
-                       return -EINVAL;
+                       return -ENOIOCTLCMD;
        }
 }
 
index ab5fa9e1a79ac8277ac1cb51db6a92e55ba2c935..bed378db075813350362c39f423d1b4335240bfa 100644 (file)
@@ -257,12 +257,12 @@ int propagate_mnt(struct mount *dest_mnt, struct dentry *dest_dentry,
                prev_src_mnt  = child;
        }
 out:
-       br_write_lock(vfsmount_lock);
+       br_write_lock(&vfsmount_lock);
        while (!list_empty(&tmp_list)) {
                child = list_first_entry(&tmp_list, struct mount, mnt_hash);
                umount_tree(child, 0, &umount_list);
        }
-       br_write_unlock(vfsmount_lock);
+       br_write_unlock(&vfsmount_lock);
        release_mounts(&umount_list);
        return ret;
 }
index dc4c5a7b9eceb767c0d7305ce5e1b609cdcdaef3..c1c207c36caefeb1fcbc8d782504cfcbf1b616d8 100644 (file)
@@ -370,7 +370,7 @@ static int do_task_stat(struct seq_file *m, struct pid_namespace *ns,
                        struct pid *pid, struct task_struct *task, int whole)
 {
        unsigned long vsize, eip, esp, wchan = ~0UL;
-       long priority, nice;
+       int priority, nice;
        int tty_pgrp = -1, tty_nr = 0;
        sigset_t sigign, sigcatch;
        char state;
@@ -492,7 +492,7 @@ static int do_task_stat(struct seq_file *m, struct pid_namespace *ns,
        seq_put_decimal_ull(m, ' ', 0);
        seq_put_decimal_ull(m, ' ', start_time);
        seq_put_decimal_ull(m, ' ', vsize);
-       seq_put_decimal_ll(m, ' ', mm ? get_mm_rss(mm) : 0);
+       seq_put_decimal_ull(m, ' ', mm ? get_mm_rss(mm) : 0);
        seq_put_decimal_ull(m, ' ', rsslim);
        seq_put_decimal_ull(m, ' ', mm ? (permitted ? mm->start_code : 1) : 0);
        seq_put_decimal_ull(m, ' ', mm ? (permitted ? mm->end_code : 1) : 0);
@@ -517,9 +517,23 @@ static int do_task_stat(struct seq_file *m, struct pid_namespace *ns,
        seq_put_decimal_ull(m, ' ', delayacct_blkio_ticks(task));
        seq_put_decimal_ull(m, ' ', cputime_to_clock_t(gtime));
        seq_put_decimal_ll(m, ' ', cputime_to_clock_t(cgtime));
-       seq_put_decimal_ull(m, ' ', (mm && permitted) ? mm->start_data : 0);
-       seq_put_decimal_ull(m, ' ', (mm && permitted) ? mm->end_data : 0);
-       seq_put_decimal_ull(m, ' ', (mm && permitted) ? mm->start_brk : 0);
+
+       if (mm && permitted) {
+               seq_put_decimal_ull(m, ' ', mm->start_data);
+               seq_put_decimal_ull(m, ' ', mm->end_data);
+               seq_put_decimal_ull(m, ' ', mm->start_brk);
+               seq_put_decimal_ull(m, ' ', mm->arg_start);
+               seq_put_decimal_ull(m, ' ', mm->arg_end);
+               seq_put_decimal_ull(m, ' ', mm->env_start);
+               seq_put_decimal_ull(m, ' ', mm->env_end);
+       } else
+               seq_printf(m, " 0 0 0 0 0 0 0");
+
+       if (permitted)
+               seq_put_decimal_ll(m, ' ', task->exit_code);
+       else
+               seq_put_decimal_ll(m, ' ', 0);
+
        seq_putc(m, '\n');
        if (mm)
                mmput(mm);
@@ -565,3 +579,126 @@ int proc_pid_statm(struct seq_file *m, struct pid_namespace *ns,
 
        return 0;
 }
+
+#ifdef CONFIG_CHECKPOINT_RESTORE
+static struct pid *
+get_children_pid(struct inode *inode, struct pid *pid_prev, loff_t pos)
+{
+       struct task_struct *start, *task;
+       struct pid *pid = NULL;
+
+       read_lock(&tasklist_lock);
+
+       start = pid_task(proc_pid(inode), PIDTYPE_PID);
+       if (!start)
+               goto out;
+
+       /*
+        * Lets try to continue searching first, this gives
+        * us significant speedup on children-rich processes.
+        */
+       if (pid_prev) {
+               task = pid_task(pid_prev, PIDTYPE_PID);
+               if (task && task->real_parent == start &&
+                   !(list_empty(&task->sibling))) {
+                       if (list_is_last(&task->sibling, &start->children))
+                               goto out;
+                       task = list_first_entry(&task->sibling,
+                                               struct task_struct, sibling);
+                       pid = get_pid(task_pid(task));
+                       goto out;
+               }
+       }
+
+       /*
+        * Slow search case.
+        *
+        * We might miss some children here if children
+        * are exited while we were not holding the lock,
+        * but it was never promised to be accurate that
+        * much.
+        *
+        * "Just suppose that the parent sleeps, but N children
+        *  exit after we printed their tids. Now the slow paths
+        *  skips N extra children, we miss N tasks." (c)
+        *
+        * So one need to stop or freeze the leader and all
+        * its children to get a precise result.
+        */
+       list_for_each_entry(task, &start->children, sibling) {
+               if (pos-- == 0) {
+                       pid = get_pid(task_pid(task));
+                       break;
+               }
+       }
+
+out:
+       read_unlock(&tasklist_lock);
+       return pid;
+}
+
+static int children_seq_show(struct seq_file *seq, void *v)
+{
+       struct inode *inode = seq->private;
+       pid_t pid;
+
+       pid = pid_nr_ns(v, inode->i_sb->s_fs_info);
+       return seq_printf(seq, "%d ", pid);
+}
+
+static void *children_seq_start(struct seq_file *seq, loff_t *pos)
+{
+       return get_children_pid(seq->private, NULL, *pos);
+}
+
+static void *children_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+       struct pid *pid;
+
+       pid = get_children_pid(seq->private, v, *pos + 1);
+       put_pid(v);
+
+       ++*pos;
+       return pid;
+}
+
+static void children_seq_stop(struct seq_file *seq, void *v)
+{
+       put_pid(v);
+}
+
+static const struct seq_operations children_seq_ops = {
+       .start  = children_seq_start,
+       .next   = children_seq_next,
+       .stop   = children_seq_stop,
+       .show   = children_seq_show,
+};
+
+static int children_seq_open(struct inode *inode, struct file *file)
+{
+       struct seq_file *m;
+       int ret;
+
+       ret = seq_open(file, &children_seq_ops);
+       if (ret)
+               return ret;
+
+       m = file->private_data;
+       m->private = inode;
+
+       return ret;
+}
+
+int children_seq_release(struct inode *inode, struct file *file)
+{
+       seq_release(inode, file);
+       return 0;
+}
+
+const struct file_operations proc_tid_children_operations = {
+       .open    = children_seq_open,
+       .read    = seq_read,
+       .llseek  = seq_lseek,
+       .release = children_seq_release,
+};
+#endif /* CONFIG_CHECKPOINT_RESTORE */
index d2d3108a611c8cf96b6d1aa275270a3929556ccf..616f41a7cde657d91d2bbd4c360d338c1dd66def 100644 (file)
@@ -199,11 +199,6 @@ static int proc_root_link(struct dentry *dentry, struct path *path)
        return result;
 }
 
-struct mm_struct *mm_for_maps(struct task_struct *task)
-{
-       return mm_access(task, PTRACE_MODE_READ);
-}
-
 static int proc_pid_cmdline(struct task_struct *task, char * buffer)
 {
        int res = 0;
@@ -243,7 +238,7 @@ out:
 
 static int proc_pid_auxv(struct task_struct *task, char *buffer)
 {
-       struct mm_struct *mm = mm_for_maps(task);
+       struct mm_struct *mm = mm_access(task, PTRACE_MODE_READ);
        int res = PTR_ERR(mm);
        if (mm && !IS_ERR(mm)) {
                unsigned int nwords = 0;
@@ -411,12 +406,13 @@ static const struct file_operations proc_lstats_operations = {
 
 static int proc_oom_score(struct task_struct *task, char *buffer)
 {
+       unsigned long totalpages = totalram_pages + total_swap_pages;
        unsigned long points = 0;
 
        read_lock(&tasklist_lock);
        if (pid_alive(task))
-               points = oom_badness(task, NULL, NULL,
-                                       totalram_pages + total_swap_pages);
+               points = oom_badness(task, NULL, NULL, totalpages) *
+                                               1000 / totalpages;
        read_unlock(&tasklist_lock);
        return sprintf(buffer, "%lu\n", points);
 }
@@ -678,7 +674,7 @@ static const struct file_operations proc_single_file_operations = {
        .release        = single_release,
 };
 
-static int mem_open(struct inode* inode, struct file* file)
+static int __mem_open(struct inode *inode, struct file *file, unsigned int mode)
 {
        struct task_struct *task = get_proc_task(file->f_path.dentry->d_inode);
        struct mm_struct *mm;
@@ -686,7 +682,7 @@ static int mem_open(struct inode* inode, struct file* file)
        if (!task)
                return -ESRCH;
 
-       mm = mm_access(task, PTRACE_MODE_ATTACH);
+       mm = mm_access(task, mode);
        put_task_struct(task);
 
        if (IS_ERR(mm))
@@ -706,6 +702,11 @@ static int mem_open(struct inode* inode, struct file* file)
        return 0;
 }
 
+static int mem_open(struct inode *inode, struct file *file)
+{
+       return __mem_open(inode, file, PTRACE_MODE_ATTACH);
+}
+
 static ssize_t mem_rw(struct file *file, char __user *buf,
                        size_t count, loff_t *ppos, int write)
 {
@@ -802,30 +803,29 @@ static const struct file_operations proc_mem_operations = {
        .release        = mem_release,
 };
 
+static int environ_open(struct inode *inode, struct file *file)
+{
+       return __mem_open(inode, file, PTRACE_MODE_READ);
+}
+
 static ssize_t environ_read(struct file *file, char __user *buf,
                        size_t count, loff_t *ppos)
 {
-       struct task_struct *task = get_proc_task(file->f_dentry->d_inode);
        char *page;
        unsigned long src = *ppos;
-       int ret = -ESRCH;
-       struct mm_struct *mm;
+       int ret = 0;
+       struct mm_struct *mm = file->private_data;
 
-       if (!task)
-               goto out_no_task;
+       if (!mm)
+               return 0;
 
-       ret = -ENOMEM;
        page = (char *)__get_free_page(GFP_TEMPORARY);
        if (!page)
-               goto out;
-
-
-       mm = mm_for_maps(task);
-       ret = PTR_ERR(mm);
-       if (!mm || IS_ERR(mm))
-               goto out_free;
+               return -ENOMEM;
 
        ret = 0;
+       if (!atomic_inc_not_zero(&mm->mm_users))
+               goto free;
        while (count > 0) {
                int this_len, retval, max_len;
 
@@ -837,7 +837,7 @@ static ssize_t environ_read(struct file *file, char __user *buf,
                max_len = (count > PAGE_SIZE) ? PAGE_SIZE : count;
                this_len = (this_len > max_len) ? max_len : this_len;
 
-               retval = access_process_vm(task, (mm->env_start + src),
+               retval = access_remote_vm(mm, (mm->env_start + src),
                        page, this_len, 0);
 
                if (retval <= 0) {
@@ -856,19 +856,18 @@ static ssize_t environ_read(struct file *file, char __user *buf,
                count -= retval;
        }
        *ppos = src;
-
        mmput(mm);
-out_free:
+
+free:
        free_page((unsigned long) page);
-out:
-       put_task_struct(task);
-out_no_task:
        return ret;
 }
 
 static const struct file_operations proc_environ_operations = {
+       .open           = environ_open,
        .read           = environ_read,
        .llseek         = generic_file_llseek,
+       .release        = mem_release,
 };
 
 static ssize_t oom_adjust_read(struct file *file, char __user *buf,
@@ -1849,7 +1848,7 @@ static const struct dentry_operations tid_fd_dentry_operations =
 static struct dentry *proc_fd_instantiate(struct inode *dir,
        struct dentry *dentry, struct task_struct *task, const void *ptr)
 {
-       unsigned fd = *(const unsigned *)ptr;
+       unsigned fd = (unsigned long)ptr;
        struct inode *inode;
        struct proc_inode *ei;
        struct dentry *error = ERR_PTR(-ENOENT);
@@ -1886,7 +1885,7 @@ static struct dentry *proc_lookupfd_common(struct inode *dir,
        if (fd == ~0U)
                goto out;
 
-       result = instantiate(dir, dentry, task, &fd);
+       result = instantiate(dir, dentry, task, (void *)(unsigned long)fd);
 out:
        put_task_struct(task);
 out_no_task:
@@ -1929,21 +1928,22 @@ static int proc_readfd_common(struct file * filp, void * dirent,
                             fd++, filp->f_pos++) {
                                char name[PROC_NUMBUF];
                                int len;
+                               int rv;
 
                                if (!fcheck_files(files, fd))
                                        continue;
                                rcu_read_unlock();
 
                                len = snprintf(name, sizeof(name), "%d", fd);
-                               if (proc_fill_cache(filp, dirent, filldir,
-                                                   name, len, instantiate,
-                                                   p, &fd) < 0) {
-                                       rcu_read_lock();
-                                       break;
-                               }
+                               rv = proc_fill_cache(filp, dirent, filldir,
+                                                    name, len, instantiate, p,
+                                                    (void *)(unsigned long)fd);
+                               if (rv < 0)
+                                       goto out_fd_loop;
                                rcu_read_lock();
                        }
                        rcu_read_unlock();
+out_fd_loop:
                        put_files_struct(files);
        }
 out:
@@ -2023,11 +2023,8 @@ static int map_files_d_revalidate(struct dentry *dentry, struct nameidata *nd)
        if (!task)
                goto out_notask;
 
-       if (!ptrace_may_access(task, PTRACE_MODE_READ))
-               goto out;
-
-       mm = get_task_mm(task);
-       if (!mm)
+       mm = mm_access(task, PTRACE_MODE_READ);
+       if (IS_ERR_OR_NULL(mm))
                goto out;
 
        if (!dname_to_vma_addr(dentry, &vm_start, &vm_end)) {
@@ -2356,7 +2353,7 @@ static const struct inode_operations proc_fd_inode_operations = {
 static struct dentry *proc_fdinfo_instantiate(struct inode *dir,
        struct dentry *dentry, struct task_struct *task, const void *ptr)
 {
-       unsigned fd = *(unsigned *)ptr;
+       unsigned fd = (unsigned long)ptr;
        struct inode *inode;
        struct proc_inode *ei;
        struct dentry *error = ERR_PTR(-ENOENT);
@@ -3403,6 +3400,9 @@ static const struct pid_entry tid_base_stuff[] = {
        ONE("stat",      S_IRUGO, proc_tid_stat),
        ONE("statm",     S_IRUGO, proc_pid_statm),
        REG("maps",      S_IRUGO, proc_tid_maps_operations),
+#ifdef CONFIG_CHECKPOINT_RESTORE
+       REG("children",  S_IRUGO, proc_tid_children_operations),
+#endif
 #ifdef CONFIG_NUMA
        REG("numa_maps", S_IRUGO, proc_tid_numa_maps_operations),
 #endif
index 5f79bb8b4c60211620c8af8212e9ec97c0978dc1..eca4aca5b6e227c11bb13c100deac3861b747a44 100644 (file)
@@ -31,8 +31,6 @@ struct vmalloc_info {
        unsigned long   largest_chunk;
 };
 
-extern struct mm_struct *mm_for_maps(struct task_struct *);
-
 #ifdef CONFIG_MMU
 #define VMALLOC_TOTAL (VMALLOC_END - VMALLOC_START)
 extern void get_vmalloc_info(struct vmalloc_info *vmi);
@@ -56,6 +54,7 @@ extern int proc_pid_statm(struct seq_file *m, struct pid_namespace *ns,
                                struct pid *pid, struct task_struct *task);
 extern loff_t mem_lseek(struct file *file, loff_t offset, int orig);
 
+extern const struct file_operations proc_tid_children_operations;
 extern const struct file_operations proc_pid_maps_operations;
 extern const struct file_operations proc_tid_maps_operations;
 extern const struct file_operations proc_pid_numa_maps_operations;
index 1030a716d155b4a19a91972b2ead074733b4add9..4540b8f76f163fbaaef250b6facf161424a90869 100644 (file)
@@ -125,7 +125,7 @@ static void *m_start(struct seq_file *m, loff_t *pos)
        if (!priv->task)
                return ERR_PTR(-ESRCH);
 
-       mm = mm_for_maps(priv->task);
+       mm = mm_access(priv->task, PTRACE_MODE_READ);
        if (!mm || IS_ERR(mm))
                return mm;
        down_read(&mm->mmap_sem);
@@ -393,6 +393,7 @@ struct mem_size_stats {
        unsigned long anonymous;
        unsigned long anonymous_thp;
        unsigned long swap;
+       unsigned long nonlinear;
        u64 pss;
 };
 
@@ -402,24 +403,33 @@ static void smaps_pte_entry(pte_t ptent, unsigned long addr,
 {
        struct mem_size_stats *mss = walk->private;
        struct vm_area_struct *vma = mss->vma;
-       struct page *page;
+       pgoff_t pgoff = linear_page_index(vma, addr);
+       struct page *page = NULL;
        int mapcount;
 
-       if (is_swap_pte(ptent)) {
-               mss->swap += ptent_size;
-               return;
+       if (pte_present(ptent)) {
+               page = vm_normal_page(vma, addr, ptent);
+       } else if (is_swap_pte(ptent)) {
+               swp_entry_t swpent = pte_to_swp_entry(ptent);
+
+               if (!non_swap_entry(swpent))
+                       mss->swap += ptent_size;
+               else if (is_migration_entry(swpent))
+                       page = migration_entry_to_page(swpent);
+       } else if (pte_file(ptent)) {
+               if (pte_to_pgoff(ptent) != pgoff)
+                       mss->nonlinear += ptent_size;
        }
 
-       if (!pte_present(ptent))
-               return;
-
-       page = vm_normal_page(vma, addr, ptent);
        if (!page)
                return;
 
        if (PageAnon(page))
                mss->anonymous += ptent_size;
 
+       if (page->index != pgoff)
+               mss->nonlinear += ptent_size;
+
        mss->resident += ptent_size;
        /* Accumulate the size in pages that have been accessed. */
        if (pte_young(ptent) || PageReferenced(page))
@@ -521,6 +531,10 @@ static int show_smap(struct seq_file *m, void *v, int is_pid)
                   (vma->vm_flags & VM_LOCKED) ?
                        (unsigned long)(mss.pss >> (10 + PSS_SHIFT)) : 0);
 
+       if (vma->vm_flags & VM_NONLINEAR)
+               seq_printf(m, "Nonlinear:      %8lu kB\n",
+                               mss.nonlinear >> 10);
+
        if (m->count < m->size)  /* vma is copied successfully */
                m->version = (vma != get_gate_vma(task->mm))
                        ? vma->vm_start : 0;
@@ -700,6 +714,7 @@ struct pagemapread {
 
 #define PM_PRESENT          PM_STATUS(4LL)
 #define PM_SWAP             PM_STATUS(2LL)
+#define PM_FILE             PM_STATUS(1LL)
 #define PM_NOT_PRESENT      PM_PSHIFT(PAGE_SHIFT)
 #define PM_END_OF_BUFFER    1
 
@@ -733,22 +748,33 @@ static int pagemap_pte_hole(unsigned long start, unsigned long end,
        return err;
 }
 
-static u64 swap_pte_to_pagemap_entry(pte_t pte)
-{
-       swp_entry_t e = pte_to_swp_entry(pte);
-       return swp_type(e) | (swp_offset(e) << MAX_SWAPFILES_SHIFT);
-}
-
-static void pte_to_pagemap_entry(pagemap_entry_t *pme, pte_t pte)
+static void pte_to_pagemap_entry(pagemap_entry_t *pme,
+               struct vm_area_struct *vma, unsigned long addr, pte_t pte)
 {
-       if (is_swap_pte(pte))
-               *pme = make_pme(PM_PFRAME(swap_pte_to_pagemap_entry(pte))
-                               | PM_PSHIFT(PAGE_SHIFT) | PM_SWAP);
-       else if (pte_present(pte))
-               *pme = make_pme(PM_PFRAME(pte_pfn(pte))
-                               | PM_PSHIFT(PAGE_SHIFT) | PM_PRESENT);
-       else
+       u64 frame, flags;
+       struct page *page = NULL;
+
+       if (pte_present(pte)) {
+               frame = pte_pfn(pte);
+               flags = PM_PRESENT;
+               page = vm_normal_page(vma, addr, pte);
+       } else if (is_swap_pte(pte)) {
+               swp_entry_t entry = pte_to_swp_entry(pte);
+
+               frame = swp_type(entry) |
+                       (swp_offset(entry) << MAX_SWAPFILES_SHIFT);
+               flags = PM_SWAP;
+               if (is_migration_entry(entry))
+                       page = migration_entry_to_page(entry);
+       } else {
                *pme = make_pme(PM_NOT_PRESENT);
+               return;
+       }
+
+       if (page && !PageAnon(page))
+               flags |= PM_FILE;
+
+       *pme = make_pme(PM_PFRAME(frame) | PM_PSHIFT(PAGE_SHIFT) | flags);
 }
 
 #ifdef CONFIG_TRANSPARENT_HUGEPAGE
@@ -784,7 +810,7 @@ static int pagemap_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end,
 
        /* find the first VMA at or above 'addr' */
        vma = find_vma(walk->mm, addr);
-       if (pmd_trans_huge_lock(pmd, vma) == 1) {
+       if (vma && pmd_trans_huge_lock(pmd, vma) == 1) {
                for (; addr != end; addr += PAGE_SIZE) {
                        unsigned long offset;
 
@@ -815,7 +841,7 @@ static int pagemap_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end,
                if (vma && (vma->vm_start <= addr) &&
                    !is_vm_hugetlb_page(vma)) {
                        pte = pte_offset_map(pmd, addr);
-                       pte_to_pagemap_entry(&pme, *pte);
+                       pte_to_pagemap_entry(&pme, vma, addr, *pte);
                        /* unmap before userspace copy */
                        pte_unmap(pte);
                }
@@ -869,11 +895,11 @@ static int pagemap_hugetlb_range(pte_t *pte, unsigned long hmask,
  * For each page in the address space, this file contains one 64-bit entry
  * consisting of the following:
  *
- * Bits 0-55  page frame number (PFN) if present
+ * Bits 0-54  page frame number (PFN) if present
  * Bits 0-4   swap type if swapped
- * Bits 5-55  swap offset if swapped
+ * Bits 5-54  swap offset if swapped
  * Bits 55-60 page shift (page size = 1<<page shift)
- * Bit  61    reserved for future use
+ * Bit  61    page is file-page or shared-anon
  * Bit  62    page swapped
  * Bit  63    page present
  *
@@ -919,7 +945,7 @@ static ssize_t pagemap_read(struct file *file, char __user *buf,
        if (!pm.buffer)
                goto out_task;
 
-       mm = mm_for_maps(task);
+       mm = mm_access(task, PTRACE_MODE_READ);
        ret = PTR_ERR(mm);
        if (!mm || IS_ERR(mm))
                goto out_free;
index 74fe164d1b233924f6a4d3e8ddd24624660dd63a..1ccfa537f5f5dfaac3c1351dabe391f0ca3e6d22 100644 (file)
@@ -223,7 +223,7 @@ static void *m_start(struct seq_file *m, loff_t *pos)
        if (!priv->task)
                return ERR_PTR(-ESRCH);
 
-       mm = mm_for_maps(priv->task);
+       mm = mm_access(priv->task, PTRACE_MODE_READ);
        if (!mm || IS_ERR(mm)) {
                put_task_struct(priv->task);
                priv->task = NULL;
index 12412852d88a94d574bacebb5e64200f202db852..5e289a7cbad17d8547458f1d8b2526f2e85e5cb9 100644 (file)
@@ -23,12 +23,12 @@ static unsigned mounts_poll(struct file *file, poll_table *wait)
 
        poll_wait(file, &p->ns->poll, wait);
 
-       br_read_lock(vfsmount_lock);
+       br_read_lock(&vfsmount_lock);
        if (p->m.poll_event != ns->event) {
                p->m.poll_event = ns->event;
                res |= POLLERR | POLLPRI;
        }
-       br_read_unlock(vfsmount_lock);
+       br_read_unlock(&vfsmount_lock);
 
        return res;
 }
index ffc99d22e0a3656711f14ac7e094cc954d1d90bd..c20614f86c01ed88ed36a65e9dfafdabfd3ba4d3 100644 (file)
@@ -633,8 +633,7 @@ ssize_t do_loop_readv_writev(struct file *filp, struct iovec *iov,
 ssize_t rw_copy_check_uvector(int type, const struct iovec __user * uvector,
                              unsigned long nr_segs, unsigned long fast_segs,
                              struct iovec *fast_pointer,
-                             struct iovec **ret_pointer,
-                             int check_access)
+                             struct iovec **ret_pointer)
 {
        unsigned long seg;
        ssize_t ret;
@@ -690,7 +689,7 @@ ssize_t rw_copy_check_uvector(int type, const struct iovec __user * uvector,
                        ret = -EINVAL;
                        goto out;
                }
-               if (check_access
+               if (type >= 0
                    && unlikely(!access_ok(vrfy_dir(type), buf, len))) {
                        ret = -EFAULT;
                        goto out;
@@ -723,7 +722,7 @@ static ssize_t do_readv_writev(int type, struct file *file,
        }
 
        ret = rw_copy_check_uvector(type, uvector, nr_segs,
-                                   ARRAY_SIZE(iovstack), iovstack, &iov, 1);
+                                   ARRAY_SIZE(iovstack), iovstack, &iov);
        if (ret <= 0)
                goto out;
 
index cc0a8227cddf688f70e289c427666057ce98e613..39e3370d79cf1e6399843137e2d64165baf49a03 100644 (file)
@@ -108,11 +108,11 @@ SYSCALL_DEFINE3(old_readdir, unsigned int, fd,
        int error;
        struct file * file;
        struct readdir_callback buf;
+       int fput_needed;
 
-       error = -EBADF;
-       file = fget(fd);
+       file = fget_light(fd, &fput_needed);
        if (!file)
-               goto out;
+               return -EBADF;
 
        buf.result = 0;
        buf.dirent = dirent;
@@ -121,8 +121,7 @@ SYSCALL_DEFINE3(old_readdir, unsigned int, fd,
        if (buf.result)
                error = buf.result;
 
-       fput(file);
-out:
+       fput_light(file, fput_needed);
        return error;
 }
 
@@ -195,16 +194,15 @@ SYSCALL_DEFINE3(getdents, unsigned int, fd,
        struct file * file;
        struct linux_dirent __user * lastdirent;
        struct getdents_callback buf;
+       int fput_needed;
        int error;
 
-       error = -EFAULT;
        if (!access_ok(VERIFY_WRITE, dirent, count))
-               goto out;
+               return -EFAULT;
 
-       error = -EBADF;
-       file = fget(fd);
+       file = fget_light(fd, &fput_needed);
        if (!file)
-               goto out;
+               return -EBADF;
 
        buf.current_dir = dirent;
        buf.previous = NULL;
@@ -221,8 +219,7 @@ SYSCALL_DEFINE3(getdents, unsigned int, fd,
                else
                        error = count - buf.count;
        }
-       fput(file);
-out:
+       fput_light(file, fput_needed);
        return error;
 }
 
@@ -278,16 +275,15 @@ SYSCALL_DEFINE3(getdents64, unsigned int, fd,
        struct file * file;
        struct linux_dirent64 __user * lastdirent;
        struct getdents_callback64 buf;
+       int fput_needed;
        int error;
 
-       error = -EFAULT;
        if (!access_ok(VERIFY_WRITE, dirent, count))
-               goto out;
+               return -EFAULT;
 
-       error = -EBADF;
-       file = fget(fd);
+       file = fget_light(fd, &fput_needed);
        if (!file)
-               goto out;
+               return -EBADF;
 
        buf.current_dir = dirent;
        buf.previous = NULL;
@@ -305,7 +301,6 @@ SYSCALL_DEFINE3(getdents64, unsigned int, fd,
                else
                        error = count - buf.count;
        }
-       fput(file);
-out:
+       fput_light(file, fput_needed);
        return error;
 }
index 59d06871a850dcebc966581f43c656bedba440f0..a6d4268fb6c11798db5f8339bd14ab297cd9b21f 100644 (file)
@@ -1592,13 +1592,12 @@ struct dentry *reiserfs_fh_to_parent(struct super_block *sb, struct fid *fid,
                (fh_type == 6) ? fid->raw[5] : 0);
 }
 
-int reiserfs_encode_fh(struct dentry *dentry, __u32 * data, int *lenp,
-                      int need_parent)
+int reiserfs_encode_fh(struct inode *inode, __u32 * data, int *lenp,
+                      struct inode *parent)
 {
-       struct inode *inode = dentry->d_inode;
        int maxlen = *lenp;
 
-       if (need_parent && (maxlen < 5)) {
+       if (parent && (maxlen < 5)) {
                *lenp = 5;
                return 255;
        } else if (maxlen < 3) {
@@ -1610,20 +1609,15 @@ int reiserfs_encode_fh(struct dentry *dentry, __u32 * data, int *lenp,
        data[1] = le32_to_cpu(INODE_PKEY(inode)->k_dir_id);
        data[2] = inode->i_generation;
        *lenp = 3;
-       /* no room for directory info? return what we've stored so far */
-       if (maxlen < 5 || !need_parent)
-               return 3;
-
-       spin_lock(&dentry->d_lock);
-       inode = dentry->d_parent->d_inode;
-       data[3] = inode->i_ino;
-       data[4] = le32_to_cpu(INODE_PKEY(inode)->k_dir_id);
-       *lenp = 5;
-       if (maxlen >= 6) {
-               data[5] = inode->i_generation;
-               *lenp = 6;
-       }
-       spin_unlock(&dentry->d_lock);
+       if (parent) {
+               data[3] = parent->i_ino;
+               data[4] = le32_to_cpu(INODE_PKEY(parent)->k_dir_id);
+               *lenp = 5;
+               if (maxlen >= 6) {
+                       data[5] = parent->i_generation;
+                       *lenp = 6;
+               }
+       }
        return *lenp;
 }
 
index b1a08573fe14277961aa3039ce0fb587d4f889cc..afcadcc03e8ac87c7f25f3e2393b3c108daaf91d 100644 (file)
@@ -1923,6 +1923,8 @@ static int do_journal_release(struct reiserfs_transaction_handle *th,
         * the workqueue job (flush_async_commit) needs this lock
         */
        reiserfs_write_unlock(sb);
+
+       cancel_delayed_work_sync(&REISERFS_SB(sb)->old_work);
        flush_workqueue(commit_wq);
 
        if (!reiserfs_mounted_fs_count) {
@@ -3231,8 +3233,6 @@ int journal_mark_dirty(struct reiserfs_transaction_handle *th,
                               th->t_trans_id, journal->j_trans_id);
        }
 
-       sb->s_dirt = 1;
-
        prepared = test_clear_buffer_journal_prepared(bh);
        clear_buffer_journal_restore_dirty(bh);
        /* already in this transaction, we are done */
@@ -3316,6 +3316,7 @@ int journal_mark_dirty(struct reiserfs_transaction_handle *th,
                journal->j_first = cn;
                journal->j_last = cn;
        }
+       reiserfs_schedule_old_flush(sb);
        return 0;
 }
 
@@ -3492,7 +3493,7 @@ static void flush_async_commits(struct work_struct *work)
 ** flushes any old transactions to disk
 ** ends the current transaction if it is too old
 */
-int reiserfs_flush_old_commits(struct super_block *sb)
+void reiserfs_flush_old_commits(struct super_block *sb)
 {
        time_t now;
        struct reiserfs_transaction_handle th;
@@ -3502,9 +3503,8 @@ int reiserfs_flush_old_commits(struct super_block *sb)
        /* safety check so we don't flush while we are replaying the log during
         * mount
         */
-       if (list_empty(&journal->j_journal_list)) {
-               return 0;
-       }
+       if (list_empty(&journal->j_journal_list))
+               return;
 
        /* check the current transaction.  If there are no writers, and it is
         * too old, finish it, and force the commit blocks to disk
@@ -3526,7 +3526,6 @@ int reiserfs_flush_old_commits(struct super_block *sb)
                        do_journal_end(&th, sb, 1, COMMIT_NOW | WAIT);
                }
        }
-       return sb->s_dirt;
 }
 
 /*
@@ -3955,7 +3954,7 @@ static int do_journal_end(struct reiserfs_transaction_handle *th,
         ** it tells us if we should continue with the journal_end, or just return
         */
        if (!check_journal_end(th, sb, nblocks, flags)) {
-               sb->s_dirt = 1;
+               reiserfs_schedule_old_flush(sb);
                wake_queued_writers(sb);
                reiserfs_async_progress_wait(sb);
                goto out;
index a59d27126338e43939f8fc04942acf13c956f1e4..33215f57ea06ce3026ef2d488832a337a361bd71 100644 (file)
@@ -480,6 +480,11 @@ struct reiserfs_sb_info {
        struct dentry *priv_root;       /* root of /.reiserfs_priv */
        struct dentry *xattr_root;      /* root of /.reiserfs_priv/xattrs */
        int j_errno;
+
+       int work_queued;              /* non-zero delayed work is queued */
+       struct delayed_work old_work; /* old transactions flush delayed work */
+       spinlock_t old_work_lock;     /* protects old_work and work_queued */
+
 #ifdef CONFIG_QUOTA
        char *s_qf_names[MAXQUOTAS];
        int s_jquota_fmt;
@@ -2452,7 +2457,7 @@ struct reiserfs_transaction_handle *reiserfs_persistent_transaction(struct
 int reiserfs_end_persistent_transaction(struct reiserfs_transaction_handle *);
 int reiserfs_commit_page(struct inode *inode, struct page *page,
                         unsigned from, unsigned to);
-int reiserfs_flush_old_commits(struct super_block *);
+void reiserfs_flush_old_commits(struct super_block *);
 int reiserfs_commit_for_inode(struct inode *);
 int reiserfs_inode_needs_commit(struct inode *);
 void reiserfs_update_inode_transaction(struct inode *);
@@ -2487,6 +2492,7 @@ void reiserfs_abort(struct super_block *sb, int errno, const char *fmt, ...);
 int reiserfs_allocate_list_bitmaps(struct super_block *s,
                                   struct reiserfs_list_bitmap *, unsigned int);
 
+void reiserfs_schedule_old_flush(struct super_block *s);
 void add_save_link(struct reiserfs_transaction_handle *th,
                   struct inode *inode, int truncate);
 int remove_save_link(struct inode *inode, int truncate);
@@ -2611,8 +2617,8 @@ struct dentry *reiserfs_fh_to_dentry(struct super_block *sb, struct fid *fid,
                                     int fh_len, int fh_type);
 struct dentry *reiserfs_fh_to_parent(struct super_block *sb, struct fid *fid,
                                     int fh_len, int fh_type);
-int reiserfs_encode_fh(struct dentry *dentry, __u32 * data, int *lenp,
-                      int connectable);
+int reiserfs_encode_fh(struct inode *inode, __u32 * data, int *lenp,
+                      struct inode *parent);
 
 int reiserfs_truncate_file(struct inode *, int update_timestamps);
 void make_cpu_key(struct cpu_key *cpu_key, struct inode *inode, loff_t offset,
index 9a17f63c3fd7f3618a44bdf946476e4957dcd50d..3ce02cff5e90bd1c26374e12e15a6f56ea8c8803 100644 (file)
@@ -200,7 +200,6 @@ int reiserfs_resize(struct super_block *s, unsigned long block_count_new)
                                          (bmap_nr_new - bmap_nr)));
        PUT_SB_BLOCK_COUNT(s, block_count_new);
        PUT_SB_BMAP_NR(s, bmap_would_wrap(bmap_nr_new) ? : bmap_nr_new);
-       s->s_dirt = 1;
 
        journal_mark_dirty(&th, s, SB_BUFFER_WITH_SB(s));
 
index c07b7d709447de1670e9caf8a7fe9bbca6593138..651ce767b55d8241e283b3001d7fc9e6d803b317 100644 (file)
@@ -72,20 +72,58 @@ static int reiserfs_sync_fs(struct super_block *s, int wait)
        if (!journal_begin(&th, s, 1))
                if (!journal_end_sync(&th, s, 1))
                        reiserfs_flush_old_commits(s);
-       s->s_dirt = 0;  /* Even if it's not true.
-                        * We'll loop forever in sync_supers otherwise */
        reiserfs_write_unlock(s);
        return 0;
 }
 
-static void reiserfs_write_super(struct super_block *s)
+static void flush_old_commits(struct work_struct *work)
 {
+       struct reiserfs_sb_info *sbi;
+       struct super_block *s;
+
+       sbi = container_of(work, struct reiserfs_sb_info, old_work.work);
+       s = sbi->s_journal->j_work_sb;
+
+       spin_lock(&sbi->old_work_lock);
+       sbi->work_queued = 0;
+       spin_unlock(&sbi->old_work_lock);
+
        reiserfs_sync_fs(s, 1);
 }
 
+void reiserfs_schedule_old_flush(struct super_block *s)
+{
+       struct reiserfs_sb_info *sbi = REISERFS_SB(s);
+       unsigned long delay;
+
+       if (s->s_flags & MS_RDONLY)
+               return;
+
+       spin_lock(&sbi->old_work_lock);
+       if (!sbi->work_queued) {
+               delay = msecs_to_jiffies(dirty_writeback_interval * 10);
+               queue_delayed_work(system_long_wq, &sbi->old_work, delay);
+               sbi->work_queued = 1;
+       }
+       spin_unlock(&sbi->old_work_lock);
+}
+
+static void cancel_old_flush(struct super_block *s)
+{
+       struct reiserfs_sb_info *sbi = REISERFS_SB(s);
+
+       cancel_delayed_work_sync(&REISERFS_SB(s)->old_work);
+       spin_lock(&sbi->old_work_lock);
+       sbi->work_queued = 0;
+       spin_unlock(&sbi->old_work_lock);
+}
+
 static int reiserfs_freeze(struct super_block *s)
 {
        struct reiserfs_transaction_handle th;
+
+       cancel_old_flush(s);
+
        reiserfs_write_lock(s);
        if (!(s->s_flags & MS_RDONLY)) {
                int err = journal_begin(&th, s, 1);
@@ -99,7 +137,6 @@ static int reiserfs_freeze(struct super_block *s)
                        journal_end_sync(&th, s, 1);
                }
        }
-       s->s_dirt = 0;
        reiserfs_write_unlock(s);
        return 0;
 }
@@ -483,9 +520,6 @@ static void reiserfs_put_super(struct super_block *s)
 
        reiserfs_write_lock(s);
 
-       if (s->s_dirt)
-               reiserfs_write_super(s);
-
        /* change file system state to current state if it was mounted with read-write permissions */
        if (!(s->s_flags & MS_RDONLY)) {
                if (!journal_begin(&th, s, 10)) {
@@ -692,7 +726,6 @@ static const struct super_operations reiserfs_sops = {
        .dirty_inode = reiserfs_dirty_inode,
        .evict_inode = reiserfs_evict_inode,
        .put_super = reiserfs_put_super,
-       .write_super = reiserfs_write_super,
        .sync_fs = reiserfs_sync_fs,
        .freeze_fs = reiserfs_freeze,
        .unfreeze_fs = reiserfs_unfreeze,
@@ -1400,7 +1433,6 @@ static int reiserfs_remount(struct super_block *s, int *mount_flags, char *arg)
        err = journal_end(&th, s, 10);
        if (err)
                goto out_err;
-       s->s_dirt = 0;
 
        if (!(*mount_flags & MS_RDONLY)) {
                dquot_resume(s, -1);
@@ -1730,19 +1762,21 @@ static int reiserfs_fill_super(struct super_block *s, void *data, int silent)
                return -ENOMEM;
        s->s_fs_info = sbi;
        /* Set default values for options: non-aggressive tails, RO on errors */
-       REISERFS_SB(s)->s_mount_opt |= (1 << REISERFS_SMALLTAIL);
-       REISERFS_SB(s)->s_mount_opt |= (1 << REISERFS_ERROR_RO);
-       REISERFS_SB(s)->s_mount_opt |= (1 << REISERFS_BARRIER_FLUSH);
+       sbi->s_mount_opt |= (1 << REISERFS_SMALLTAIL);
+       sbi->s_mount_opt |= (1 << REISERFS_ERROR_RO);
+       sbi->s_mount_opt |= (1 << REISERFS_BARRIER_FLUSH);
        /* no preallocation minimum, be smart in
           reiserfs_file_write instead */
-       REISERFS_SB(s)->s_alloc_options.preallocmin = 0;
+       sbi->s_alloc_options.preallocmin = 0;
        /* Preallocate by 16 blocks (17-1) at once */
-       REISERFS_SB(s)->s_alloc_options.preallocsize = 17;
+       sbi->s_alloc_options.preallocsize = 17;
        /* setup default block allocator options */
        reiserfs_init_alloc_options(s);
 
-       mutex_init(&REISERFS_SB(s)->lock);
-       REISERFS_SB(s)->lock_depth = -1;
+       spin_lock_init(&sbi->old_work_lock);
+       INIT_DELAYED_WORK(&sbi->old_work, flush_old_commits);
+       mutex_init(&sbi->lock);
+       sbi->lock_depth = -1;
 
        jdev_name = NULL;
        if (reiserfs_parse_options
@@ -1751,8 +1785,8 @@ static int reiserfs_fill_super(struct super_block *s, void *data, int silent)
                goto error_unlocked;
        }
        if (jdev_name && jdev_name[0]) {
-               REISERFS_SB(s)->s_jdev = kstrdup(jdev_name, GFP_KERNEL);
-               if (!REISERFS_SB(s)->s_jdev) {
+               sbi->s_jdev = kstrdup(jdev_name, GFP_KERNEL);
+               if (!sbi->s_jdev) {
                        SWARN(silent, s, "", "Cannot allocate memory for "
                                "journal device name");
                        goto error;
@@ -1810,7 +1844,7 @@ static int reiserfs_fill_super(struct super_block *s, void *data, int silent)
        /* make data=ordered the default */
        if (!reiserfs_data_log(s) && !reiserfs_data_ordered(s) &&
            !reiserfs_data_writeback(s)) {
-               REISERFS_SB(s)->s_mount_opt |= (1 << REISERFS_DATA_ORDERED);
+               sbi->s_mount_opt |= (1 << REISERFS_DATA_ORDERED);
        }
 
        if (reiserfs_data_log(s)) {
@@ -2003,6 +2037,8 @@ error_unlocked:
                reiserfs_write_unlock(s);
        }
 
+       cancel_delayed_work_sync(&REISERFS_SB(s)->old_work);
+
        reiserfs_free_bitmap_cache(s);
        if (SB_BUFFER_WITH_SB(s))
                brelse(SB_BUFFER_WITH_SB(s));
index 17d33d09fc16f4843c72f9c7444dfdb3fb738952..bae321569dfa7283b290a2e2d65c99bb10b622f4 100644 (file)
@@ -614,7 +614,6 @@ SYSCALL_DEFINE5(select, int, n, fd_set __user *, inp, fd_set __user *, outp,
        return ret;
 }
 
-#ifdef HAVE_SET_RESTORE_SIGMASK
 static long do_pselect(int n, fd_set __user *inp, fd_set __user *outp,
                       fd_set __user *exp, struct timespec __user *tsp,
                       const sigset_t __user *sigmask, size_t sigsetsize)
@@ -686,7 +685,6 @@ SYSCALL_DEFINE6(pselect6, int, n, fd_set __user *, inp, fd_set __user *, outp,
 
        return do_pselect(n, inp, outp, exp, tsp, up, sigsetsize);
 }
-#endif /* HAVE_SET_RESTORE_SIGMASK */
 
 #ifdef __ARCH_WANT_SYS_OLD_SELECT
 struct sel_arg_struct {
@@ -941,7 +939,6 @@ SYSCALL_DEFINE3(poll, struct pollfd __user *, ufds, unsigned int, nfds,
        return ret;
 }
 
-#ifdef HAVE_SET_RESTORE_SIGMASK
 SYSCALL_DEFINE5(ppoll, struct pollfd __user *, ufds, unsigned int, nfds,
                struct timespec __user *, tsp, const sigset_t __user *, sigmask,
                size_t, sigsetsize)
@@ -992,4 +989,3 @@ SYSCALL_DEFINE5(ppoll, struct pollfd __user *, ufds, unsigned int, nfds,
 
        return ret;
 }
-#endif /* HAVE_SET_RESTORE_SIGMASK */
index 7ae2a574cb25a64902128f53832b317202dbee8f..9f35a37173de0de1f7fbd8d80ca8ad39b50e3782 100644 (file)
@@ -269,12 +269,13 @@ SYSCALL_DEFINE4(signalfd4, int, ufd, sigset_t __user *, user_mask,
                if (ufd < 0)
                        kfree(ctx);
        } else {
-               struct file *file = fget(ufd);
+               int fput_needed;
+               struct file *file = fget_light(ufd, &fput_needed);
                if (!file)
                        return -EBADF;
                ctx = file->private_data;
                if (file->f_op != &signalfd_fops) {
-                       fput(file);
+                       fput_light(file, fput_needed);
                        return -EINVAL;
                }
                spin_lock_irq(&current->sighand->siglock);
@@ -282,7 +283,7 @@ SYSCALL_DEFINE4(signalfd4, int, ufd, sigset_t __user *, user_mask,
                spin_unlock_irq(&current->sighand->siglock);
 
                wake_up(&current->sighand->signalfd_wqh);
-               fput(file);
+               fput_light(file, fput_needed);
        }
 
        return ufd;
index f8476841eb04e08edc2ad069e97cddc3da242e7f..c9f1318a3b820b363526576036c4894205552921 100644 (file)
@@ -1003,8 +1003,10 @@ generic_file_splice_write(struct pipe_inode_info *pipe, struct file *out,
                mutex_lock_nested(&inode->i_mutex, I_MUTEX_CHILD);
                ret = file_remove_suid(out);
                if (!ret) {
-                       file_update_time(out);
-                       ret = splice_from_pipe_feed(pipe, &sd, pipe_to_file);
+                       ret = file_update_time(out);
+                       if (!ret)
+                               ret = splice_from_pipe_feed(pipe, &sd,
+                                                           pipe_to_file);
                }
                mutex_unlock(&inode->i_mutex);
        } while (ret > 0);
@@ -1388,7 +1390,7 @@ static long do_splice(struct file *in, loff_t __user *off_in,
  */
 static int get_iovec_page_array(const struct iovec __user *iov,
                                unsigned int nr_vecs, struct page **pages,
-                               struct partial_page *partial, int aligned,
+                               struct partial_page *partial, bool aligned,
                                unsigned int pipe_buffers)
 {
        int buffers = 0, error = 0;
@@ -1626,7 +1628,7 @@ static long vmsplice_to_pipe(struct file *file, const struct iovec __user *iov,
                return -ENOMEM;
 
        spd.nr_pages = get_iovec_page_array(iov, nr_segs, spd.pages,
-                                           spd.partial, flags & SPLICE_F_GIFT,
+                                           spd.partial, false,
                                            pipe->buffers);
        if (spd.nr_pages <= 0)
                ret = spd.nr_pages;
index 43e6b6fe4e855684a197c48ed6bb8dee70f95467..95ad5c0e586c9f64fe492e141387b5092956d553 100644 (file)
@@ -87,11 +87,12 @@ int user_statfs(const char __user *pathname, struct kstatfs *st)
 
 int fd_statfs(int fd, struct kstatfs *st)
 {
-       struct file *file = fget(fd);
+       int fput_needed;
+       struct file *file = fget_light(fd, &fput_needed);
        int error = -EBADF;
        if (file) {
                error = vfs_statfs(&file->f_path, st);
-               fput(file);
+               fput_light(file, fput_needed);
        }
        return error;
 }
index 0e8db939d96f8fdaa072df7e6fcadb15a5781a84..11e3d1c449018dcf9a95c352746f46d6522c4cb2 100644 (file)
--- a/fs/sync.c
+++ b/fs/sync.c
@@ -188,11 +188,12 @@ static int do_fsync(unsigned int fd, int datasync)
 {
        struct file *file;
        int ret = -EBADF;
+       int fput_needed;
 
-       file = fget(fd);
+       file = fget_light(fd, &fput_needed);
        if (file) {
                ret = vfs_fsync(file, datasync);
-               fput(file);
+               fput_light(file, fput_needed);
        }
        return ret;
 }
index 62a2727f4ecf71809f518dd206922fc7f9234f0d..a6d42efc76d227d62289f852982160442d7e5cea 100644 (file)
@@ -1127,16 +1127,7 @@ int ubifs_getattr(struct vfsmount *mnt, struct dentry *dentry,
        struct ubifs_inode *ui = ubifs_inode(inode);
 
        mutex_lock(&ui->ui_mutex);
-       stat->dev = inode->i_sb->s_dev;
-       stat->ino = inode->i_ino;
-       stat->mode = inode->i_mode;
-       stat->nlink = inode->i_nlink;
-       stat->uid = inode->i_uid;
-       stat->gid = inode->i_gid;
-       stat->rdev = inode->i_rdev;
-       stat->atime = inode->i_atime;
-       stat->mtime = inode->i_mtime;
-       stat->ctime = inode->i_ctime;
+       generic_fillattr(inode, stat);
        stat->blksize = UBIFS_BLOCK_SIZE;
        stat->size = ui->ui_size;
 
index a165c66e3eef2249379890c60a4c7d4111e8df4d..18024178ac4c040a3f23181ff2dc1a5cc48f2dc8 100644 (file)
@@ -1260,16 +1260,15 @@ static struct dentry *udf_fh_to_parent(struct super_block *sb,
                                 fid->udf.parent_partref,
                                 fid->udf.parent_generation);
 }
-static int udf_encode_fh(struct dentry *de, __u32 *fh, int *lenp,
-                        int connectable)
+static int udf_encode_fh(struct inode *inode, __u32 *fh, int *lenp,
+                        struct inode *parent)
 {
        int len = *lenp;
-       struct inode *inode =  de->d_inode;
        struct kernel_lb_addr location = UDF_I(inode)->i_location;
        struct fid *fid = (struct fid *)fh;
        int type = FILEID_UDF_WITHOUT_PARENT;
 
-       if (connectable && (len < 5)) {
+       if (parent && (len < 5)) {
                *lenp = 5;
                return 255;
        } else if (len < 3) {
@@ -1282,14 +1281,11 @@ static int udf_encode_fh(struct dentry *de, __u32 *fh, int *lenp,
        fid->udf.partref = location.partitionReferenceNum;
        fid->udf.generation = inode->i_generation;
 
-       if (connectable && !S_ISDIR(inode->i_mode)) {
-               spin_lock(&de->d_lock);
-               inode = de->d_parent->d_inode;
-               location = UDF_I(inode)->i_location;
+       if (parent) {
+               location = UDF_I(parent)->i_location;
                fid->udf.parent_block = location.logicalBlockNum;
                fid->udf.parent_partref = location.partitionReferenceNum;
                fid->udf.parent_generation = inode->i_generation;
-               spin_unlock(&de->d_lock);
                *lenp = 5;
                type = FILEID_UDF_WITH_PARENT;
        }
index ba653f3dc1bc9c66010290e53e0bb2a5b8fd94b1..fa4dbe451e278eab0f52bbacc110b157a300cdad 100644 (file)
@@ -140,18 +140,19 @@ long do_utimes(int dfd, const char __user *filename, struct timespec *times,
                goto out;
 
        if (filename == NULL && dfd != AT_FDCWD) {
+               int fput_needed;
                struct file *file;
 
                if (flags & AT_SYMLINK_NOFOLLOW)
                        goto out;
 
-               file = fget(dfd);
+               file = fget_light(dfd, &fput_needed);
                error = -EBADF;
                if (!file)
                        goto out;
 
                error = utimes_common(&file->f_path, times);
-               fput(file);
+               fput_light(file, fput_needed);
        } else {
                struct path path;
                int lookup_flags = 0;
index 3c8c1cc333c7c79dfa105049a62d8b6e1c28661c..1d7ac379045879b827b196f0d7a7420fe33c783d 100644 (file)
@@ -399,11 +399,12 @@ SYSCALL_DEFINE5(lsetxattr, const char __user *, pathname,
 SYSCALL_DEFINE5(fsetxattr, int, fd, const char __user *, name,
                const void __user *,value, size_t, size, int, flags)
 {
+       int fput_needed;
        struct file *f;
        struct dentry *dentry;
        int error = -EBADF;
 
-       f = fget(fd);
+       f = fget_light(fd, &fput_needed);
        if (!f)
                return error;
        dentry = f->f_path.dentry;
@@ -413,7 +414,7 @@ SYSCALL_DEFINE5(fsetxattr, int, fd, const char __user *, name,
                error = setxattr(dentry, name, value, size, flags);
                mnt_drop_write_file(f);
        }
-       fput(f);
+       fput_light(f, fput_needed);
        return error;
 }
 
@@ -486,15 +487,16 @@ SYSCALL_DEFINE4(lgetxattr, const char __user *, pathname,
 SYSCALL_DEFINE4(fgetxattr, int, fd, const char __user *, name,
                void __user *, value, size_t, size)
 {
+       int fput_needed;
        struct file *f;
        ssize_t error = -EBADF;
 
-       f = fget(fd);
+       f = fget_light(fd, &fput_needed);
        if (!f)
                return error;
        audit_inode(NULL, f->f_path.dentry);
        error = getxattr(f->f_path.dentry, name, value, size);
-       fput(f);
+       fput_light(f, fput_needed);
        return error;
 }
 
@@ -566,15 +568,16 @@ SYSCALL_DEFINE3(llistxattr, const char __user *, pathname, char __user *, list,
 
 SYSCALL_DEFINE3(flistxattr, int, fd, char __user *, list, size_t, size)
 {
+       int fput_needed;
        struct file *f;
        ssize_t error = -EBADF;
 
-       f = fget(fd);
+       f = fget_light(fd, &fput_needed);
        if (!f)
                return error;
        audit_inode(NULL, f->f_path.dentry);
        error = listxattr(f->f_path.dentry, list, size);
-       fput(f);
+       fput_light(f, fput_needed);
        return error;
 }
 
@@ -634,11 +637,12 @@ SYSCALL_DEFINE2(lremovexattr, const char __user *, pathname,
 
 SYSCALL_DEFINE2(fremovexattr, int, fd, const char __user *, name)
 {
+       int fput_needed;
        struct file *f;
        struct dentry *dentry;
        int error = -EBADF;
 
-       f = fget(fd);
+       f = fget_light(fd, &fput_needed);
        if (!f)
                return error;
        dentry = f->f_path.dentry;
@@ -648,7 +652,7 @@ SYSCALL_DEFINE2(fremovexattr, int, fd, const char __user *, name)
                error = removexattr(dentry, name);
                mnt_drop_write_file(f);
        }
-       fput(f);
+       fput_light(f, fput_needed);
        return error;
 }
 
index a907de565db3bf287f7d1a7894fff23f85ca18d5..4a7286c1dc80d270af40a3733870bb9dd769ee82 100644 (file)
@@ -46,7 +46,7 @@ kmem_zalloc_greedy(size_t *size, size_t minsize, size_t maxsize)
 }
 
 void *
-kmem_alloc(size_t size, unsigned int __nocast flags)
+kmem_alloc(size_t size, xfs_km_flags_t flags)
 {
        int     retries = 0;
        gfp_t   lflags = kmem_flags_convert(flags);
@@ -65,7 +65,7 @@ kmem_alloc(size_t size, unsigned int __nocast flags)
 }
 
 void *
-kmem_zalloc(size_t size, unsigned int __nocast flags)
+kmem_zalloc(size_t size, xfs_km_flags_t flags)
 {
        void    *ptr;
 
@@ -87,7 +87,7 @@ kmem_free(const void *ptr)
 
 void *
 kmem_realloc(const void *ptr, size_t newsize, size_t oldsize,
-            unsigned int __nocast flags)
+            xfs_km_flags_t flags)
 {
        void    *new;
 
@@ -102,7 +102,7 @@ kmem_realloc(const void *ptr, size_t newsize, size_t oldsize,
 }
 
 void *
-kmem_zone_alloc(kmem_zone_t *zone, unsigned int __nocast flags)
+kmem_zone_alloc(kmem_zone_t *zone, xfs_km_flags_t flags)
 {
        int     retries = 0;
        gfp_t   lflags = kmem_flags_convert(flags);
@@ -121,7 +121,7 @@ kmem_zone_alloc(kmem_zone_t *zone, unsigned int __nocast flags)
 }
 
 void *
-kmem_zone_zalloc(kmem_zone_t *zone, unsigned int __nocast flags)
+kmem_zone_zalloc(kmem_zone_t *zone, xfs_km_flags_t flags)
 {
        void    *ptr;
 
index ab7c53fe346e2273311a1b73bee866ab8c4f8d7f..b2f2620f9a87b9f1bf6836c8faf3d5039e7af94f 100644 (file)
  * General memory allocation interfaces
  */
 
-#define KM_SLEEP       0x0001u
-#define KM_NOSLEEP     0x0002u
-#define KM_NOFS                0x0004u
-#define KM_MAYFAIL     0x0008u
+typedef unsigned __bitwise xfs_km_flags_t;
+#define KM_SLEEP       ((__force xfs_km_flags_t)0x0001u)
+#define KM_NOSLEEP     ((__force xfs_km_flags_t)0x0002u)
+#define KM_NOFS                ((__force xfs_km_flags_t)0x0004u)
+#define KM_MAYFAIL     ((__force xfs_km_flags_t)0x0008u)
 
 /*
  * We use a special process flag to avoid recursive callbacks into
@@ -38,7 +39,7 @@
  * warnings, so we explicitly skip any generic ones (silly of us).
  */
 static inline gfp_t
-kmem_flags_convert(unsigned int __nocast flags)
+kmem_flags_convert(xfs_km_flags_t flags)
 {
        gfp_t   lflags;
 
@@ -54,9 +55,9 @@ kmem_flags_convert(unsigned int __nocast flags)
        return lflags;
 }
 
-extern void *kmem_alloc(size_t, unsigned int __nocast);
-extern void *kmem_zalloc(size_t, unsigned int __nocast);
-extern void *kmem_realloc(const void *, size_t, size_t, unsigned int __nocast);
+extern void *kmem_alloc(size_t, xfs_km_flags_t);
+extern void *kmem_zalloc(size_t, xfs_km_flags_t);
+extern void *kmem_realloc(const void *, size_t, size_t, xfs_km_flags_t);
 extern void  kmem_free(const void *);
 
 static inline void *kmem_zalloc_large(size_t size)
@@ -107,7 +108,7 @@ kmem_zone_destroy(kmem_zone_t *zone)
                kmem_cache_destroy(zone);
 }
 
-extern void *kmem_zone_alloc(kmem_zone_t *, unsigned int __nocast);
-extern void *kmem_zone_zalloc(kmem_zone_t *, unsigned int __nocast);
+extern void *kmem_zone_alloc(kmem_zone_t *, xfs_km_flags_t);
+extern void *kmem_zone_zalloc(kmem_zone_t *, xfs_km_flags_t);
 
 #endif /* __XFS_SUPPORT_KMEM_H__ */
index 2d25d19c4ea17b991fa4a43dcbb340e5c957c461..42679223a0fde641e3013980fbd1e733dc6ec60e 100644 (file)
@@ -52,19 +52,18 @@ static int xfs_fileid_length(int fileid_type)
 
 STATIC int
 xfs_fs_encode_fh(
-       struct dentry           *dentry,
-       __u32                   *fh,
-       int                     *max_len,
-       int                     connectable)
+       struct inode    *inode,
+       __u32           *fh,
+       int             *max_len,
+       struct inode    *parent)
 {
        struct fid              *fid = (struct fid *)fh;
        struct xfs_fid64        *fid64 = (struct xfs_fid64 *)fh;
-       struct inode            *inode = dentry->d_inode;
        int                     fileid_type;
        int                     len;
 
        /* Directories don't need their parent encoded, they have ".." */
-       if (S_ISDIR(inode->i_mode) || !connectable)
+       if (!parent)
                fileid_type = FILEID_INO32_GEN;
        else
                fileid_type = FILEID_INO32_GEN_PARENT;
@@ -96,20 +95,16 @@ xfs_fs_encode_fh(
 
        switch (fileid_type) {
        case FILEID_INO32_GEN_PARENT:
-               spin_lock(&dentry->d_lock);
-               fid->i32.parent_ino = XFS_I(dentry->d_parent->d_inode)->i_ino;
-               fid->i32.parent_gen = dentry->d_parent->d_inode->i_generation;
-               spin_unlock(&dentry->d_lock);
+               fid->i32.parent_ino = XFS_I(parent)->i_ino;
+               fid->i32.parent_gen = parent->i_generation;
                /*FALLTHRU*/
        case FILEID_INO32_GEN:
                fid->i32.ino = XFS_I(inode)->i_ino;
                fid->i32.gen = inode->i_generation;
                break;
        case FILEID_INO32_GEN_PARENT | XFS_FILEID_TYPE_64FLAG:
-               spin_lock(&dentry->d_lock);
-               fid64->parent_ino = XFS_I(dentry->d_parent->d_inode)->i_ino;
-               fid64->parent_gen = dentry->d_parent->d_inode->i_generation;
-               spin_unlock(&dentry->d_lock);
+               fid64->parent_ino = XFS_I(parent)->i_ino;
+               fid64->parent_gen = parent->i_generation;
                /*FALLTHRU*/
        case FILEID_INO32_GEN | XFS_FILEID_TYPE_64FLAG:
                fid64->ino = XFS_I(inode)->i_ino;
index 8d214b87f6bb06ed1f7ed204cdfda8da9345172f..9f7ec15a65222e2fe318e0ab81ac9cca0a664b4a 100644 (file)
@@ -586,8 +586,11 @@ restart:
         * lock above.  Eventually we should look into a way to avoid
         * the pointless lock roundtrip.
         */
-       if (likely(!(file->f_mode & FMODE_NOCMTIME)))
-               file_update_time(file);
+       if (likely(!(file->f_mode & FMODE_NOCMTIME))) {
+               error = file_update_time(file);
+               if (error)
+                       return error;
+       }
 
        /*
         * If we're writing the file then make sure to clear the setuid and
index 6b965bf450e44d5972fc689d486deec3dd5c8094..f30d9807dc48a0535084da1afd5a7620389adcd5 100644 (file)
@@ -3152,7 +3152,7 @@ xlog_ticket_alloc(
        int             cnt,
        char            client,
        bool            permanent,
-       int             alloc_flags)
+       xfs_km_flags_t  alloc_flags)
 {
        struct xlog_ticket *tic;
        uint            num_headers;
index 735ff1ee53da447eee9c5b88d54e9007ee988138..5bc33261f5be6311fb5732f42db25e17064abded 100644 (file)
@@ -555,7 +555,7 @@ extern void  xlog_pack_data(xlog_t *log, xlog_in_core_t *iclog, int);
 extern kmem_zone_t *xfs_log_ticket_zone;
 struct xlog_ticket *xlog_ticket_alloc(struct log *log, int unit_bytes,
                                int count, char client, bool permanent,
-                               int alloc_flags);
+                               xfs_km_flags_t alloc_flags);
 
 
 static inline void
index cdf896fcbfa43810c83bfbc7a99f84f118a4d75d..fdf324508c5ee467c6055f0866e1be88c387942d 100644 (file)
@@ -584,7 +584,7 @@ xfs_trans_t *
 _xfs_trans_alloc(
        xfs_mount_t     *mp,
        uint            type,
-       uint            memflags)
+       xfs_km_flags_t  memflags)
 {
        xfs_trans_t     *tp;
 
index 7ab99e1898c8de10e875aff23599d140c9867c4b..7c37b533aa8e5c169f0ef98643f96958df3788df 100644 (file)
@@ -443,7 +443,7 @@ typedef struct xfs_trans {
  * XFS transaction mechanism exported interfaces.
  */
 xfs_trans_t    *xfs_trans_alloc(struct xfs_mount *, uint);
-xfs_trans_t    *_xfs_trans_alloc(struct xfs_mount *, uint, uint);
+xfs_trans_t    *_xfs_trans_alloc(struct xfs_mount *, uint, xfs_km_flags_t);
 xfs_trans_t    *xfs_trans_dup(xfs_trans_t *);
 int            xfs_trans_reserve(xfs_trans_t *, uint, uint, uint,
                                  uint, uint);
index 53f91b1ae53a425f0c5c2b487dcb51e658affa77..2c85a0f647b7a44e7d2e0cbb20071a9e27b322f6 100644 (file)
@@ -8,6 +8,7 @@ header-y += int-ll64.h
 header-y += ioctl.h
 header-y += ioctls.h
 header-y += ipcbuf.h
+header-y += kvm_para.h
 header-y += mman-common.h
 header-y += mman.h
 header-y += msgbuf.h
index 4ae54e07de83d2b970d88807f662bcfe300f2ddc..a7b0914348fd0fce08ed7cb42bbe2c8fbacbf8f9 100644 (file)
@@ -28,5 +28,9 @@
 #error Inconsistent word size. Check asm/bitsperlong.h
 #endif
 
+#ifndef BITS_PER_LONG_LONG
+#define BITS_PER_LONG_LONG 64
+#endif
+
 #endif /* __KERNEL__ */
 #endif /* __ASM_GENERIC_BITS_PER_LONG */
index e2768f188f55b9e37d53b7dee3701523fe349120..6f2b45a9b6bc425b7df6231f474516e1bd4c1344 100644 (file)
@@ -445,6 +445,18 @@ static inline int pmd_write(pmd_t pmd)
 #endif /* __HAVE_ARCH_PMD_WRITE */
 #endif /* CONFIG_TRANSPARENT_HUGEPAGE */
 
+#ifndef pmd_read_atomic
+static inline pmd_t pmd_read_atomic(pmd_t *pmdp)
+{
+       /*
+        * Depend on compiler for an atomic pmd read. NOTE: this is
+        * only going to work, if the pmdval_t isn't larger than
+        * an unsigned long.
+        */
+       return *pmdp;
+}
+#endif
+
 /*
  * This function is meant to be used by sites walking pagetables with
  * the mmap_sem hold in read mode to protect against MADV_DONTNEED and
@@ -458,11 +470,17 @@ static inline int pmd_write(pmd_t pmd)
  * undefined so behaving like if the pmd was none is safe (because it
  * can return none anyway). The compiler level barrier() is critically
  * important to compute the two checks atomically on the same pmdval.
+ *
+ * For 32bit kernels with a 64bit large pmd_t this automatically takes
+ * care of reading the pmd atomically to avoid SMP race conditions
+ * against pmd_populate() when the mmap_sem is hold for reading by the
+ * caller (a special atomic read not done by "gcc" as in the generic
+ * version above, is also needed when THP is disabled because the page
+ * fault can populate the pmd from under us).
  */
 static inline int pmd_none_or_trans_huge_or_clear_bad(pmd_t *pmd)
 {
-       /* depend on compiler for an atomic pmd read */
-       pmd_t pmdval = *pmd;
+       pmd_t pmdval = pmd_read_atomic(pmd);
        /*
         * The barrier will stabilize the pmdval in a register or on
         * the stack so that it will stop changing under the code.
index 91d44bd4dde32574bb6365a5526ac33a19992050..fe74fccf18db75742d240151ec358049861b7406 100644 (file)
@@ -23,10 +23,6 @@ typedef __kernel_ulong_t __kernel_ino_t;
 typedef unsigned int   __kernel_mode_t;
 #endif
 
-#ifndef __kernel_nlink_t
-typedef __kernel_ulong_t __kernel_nlink_t;
-#endif
-
 #ifndef __kernel_pid_t
 typedef int            __kernel_pid_t;
 #endif
index 6bd325fedc873ae4785a619e8a679f339d562485..19a240446fca657e9928e93defa3ead34905ee56 100644 (file)
@@ -31,7 +31,7 @@
 
 static __inline__ void *drm_calloc_large(size_t nmemb, size_t size)
 {
-       if (size != 0 && nmemb > ULONG_MAX / size)
+       if (size != 0 && nmemb > SIZE_MAX / size)
                return NULL;
 
        if (size * nmemb <= PAGE_SIZE)
@@ -44,7 +44,7 @@ static __inline__ void *drm_calloc_large(size_t nmemb, size_t size)
 /* Modeled after cairo's malloc_ab, it's like calloc but without the zeroing. */
 static __inline__ void *drm_malloc_ab(size_t nmemb, size_t size)
 {
-       if (size != 0 && nmemb > ULONG_MAX / size)
+       if (size != 0 && nmemb > SIZE_MAX / size)
                return NULL;
 
        if (size * nmemb <= PAGE_SIZE)
index 4cd59b95858f9eab1e47a3dafde27c9176175c9c..8760be30b3750a8f4d8e8e01692ccf92bb3b1931 100644 (file)
@@ -225,6 +225,8 @@ header-y += kd.h
 header-y += kdev_t.h
 header-y += kernel.h
 header-y += kernelcapi.h
+header-y += kernel-page-flags.h
+header-y += kexec.h
 header-y += keyboard.h
 header-y += keyctl.h
 header-y += l2tp.h
index 47bedc0eee6939b8c1f397177c8c0ef6a5148f9c..0a95e730fcea706a185f52af4b3c563ee40e7d7f 100644 (file)
@@ -5,7 +5,7 @@
 #ifndef _LINUX_APPLE_BL_H
 #define _LINUX_APPLE_BL_H
 
-#ifdef CONFIG_BACKLIGHT_APPLE
+#if defined(CONFIG_BACKLIGHT_APPLE) || defined(CONFIG_BACKLIGHT_APPLE_MODULE)
 
 extern int apple_bl_register(void);
 extern void apple_bl_unregister(void);
index 4d94eb8bcbccc224bd0206cb435b1f052ddaaebf..26435890dc87a6c3b9d37f14571833fb1afe0164 100644 (file)
@@ -269,6 +269,14 @@ extern struct bio_vec *bvec_alloc_bs(gfp_t, int, unsigned long *, struct bio_set
 extern void bvec_free_bs(struct bio_set *, struct bio_vec *, unsigned int);
 extern unsigned int bvec_nr_vecs(unsigned short idx);
 
+#ifdef CONFIG_BLK_CGROUP
+int bio_associate_current(struct bio *bio);
+void bio_disassociate_task(struct bio *bio);
+#else  /* CONFIG_BLK_CGROUP */
+static inline int bio_associate_current(struct bio *bio) { return -ENOENT; }
+static inline void bio_disassociate_task(struct bio *bio) { }
+#endif /* CONFIG_BLK_CGROUP */
+
 /*
  * bio_set is used to allow other portions of the IO system to
  * allocate their own private memory pools for bio and iovec structures.
index 4053cbd4490edb6530eee7bb332ab95138352d17..0edb65dd8eddd35de6b0c383a27d889ec00f2193 100644 (file)
@@ -14,6 +14,8 @@ struct bio;
 struct bio_integrity_payload;
 struct page;
 struct block_device;
+struct io_context;
+struct cgroup_subsys_state;
 typedef void (bio_end_io_t) (struct bio *, int);
 typedef void (bio_destructor_t) (struct bio *);
 
@@ -66,6 +68,14 @@ struct bio {
        bio_end_io_t            *bi_end_io;
 
        void                    *bi_private;
+#ifdef CONFIG_BLK_CGROUP
+       /*
+        * Optional ioc and css associated with this bio.  Put on bio
+        * release.  Read comment on top of bio_associate_current().
+        */
+       struct io_context       *bi_ioc;
+       struct cgroup_subsys_state *bi_css;
+#endif
 #if defined(CONFIG_BLK_DEV_INTEGRITY)
        struct bio_integrity_payload *bi_integrity;  /* data integrity */
 #endif
index 4d4ac24a263ea956457d4ea4a63f1431408a6d90..ba43f408baa38907a8f63a64314e07bb622a6e7f 100644 (file)
@@ -32,10 +32,17 @@ struct blk_trace;
 struct request;
 struct sg_io_hdr;
 struct bsg_job;
+struct blkcg_gq;
 
 #define BLKDEV_MIN_RQ  4
 #define BLKDEV_MAX_RQ  128     /* Default maximum */
 
+/*
+ * Maximum number of blkcg policies allowed to be registered concurrently.
+ * Defined here to simplify include dependency.
+ */
+#define BLKCG_MAX_POLS         2
+
 struct request;
 typedef void (rq_end_io_fn)(struct request *, int);
 
@@ -363,6 +370,11 @@ struct request_queue {
        struct list_head        timeout_list;
 
        struct list_head        icq_list;
+#ifdef CONFIG_BLK_CGROUP
+       DECLARE_BITMAP          (blkcg_pols, BLKCG_MAX_POLS);
+       struct blkcg_gq         *root_blkg;
+       struct list_head        blkg_list;
+#endif
 
        struct queue_limits     limits;
 
@@ -390,12 +402,17 @@ struct request_queue {
 
        struct mutex            sysfs_lock;
 
+       int                     bypass_depth;
+
 #if defined(CONFIG_BLK_DEV_BSG)
        bsg_job_fn              *bsg_job_fn;
        int                     bsg_job_size;
        struct bsg_class_device bsg_dev;
 #endif
 
+#ifdef CONFIG_BLK_CGROUP
+       struct list_head        all_q_node;
+#endif
 #ifdef CONFIG_BLK_DEV_THROTTLING
        /* Throttle data */
        struct throtl_data *td;
@@ -407,7 +424,7 @@ struct request_queue {
 #define        QUEUE_FLAG_SYNCFULL     3       /* read queue has been filled */
 #define QUEUE_FLAG_ASYNCFULL   4       /* write queue has been filled */
 #define QUEUE_FLAG_DEAD                5       /* queue being torn down */
-#define QUEUE_FLAG_ELVSWITCH   6       /* don't use elevator, just do FIFO */
+#define QUEUE_FLAG_BYPASS      6       /* act as dumb FIFO queue */
 #define QUEUE_FLAG_BIDI                7       /* queue supports bidi requests */
 #define QUEUE_FLAG_NOMERGES     8      /* disable merge attempts */
 #define QUEUE_FLAG_SAME_COMP   9       /* complete on same CPU-group */
@@ -491,6 +508,7 @@ static inline void queue_flag_clear(unsigned int flag, struct request_queue *q)
 #define blk_queue_tagged(q)    test_bit(QUEUE_FLAG_QUEUED, &(q)->queue_flags)
 #define blk_queue_stopped(q)   test_bit(QUEUE_FLAG_STOPPED, &(q)->queue_flags)
 #define blk_queue_dead(q)      test_bit(QUEUE_FLAG_DEAD, &(q)->queue_flags)
+#define blk_queue_bypass(q)    test_bit(QUEUE_FLAG_BYPASS, &(q)->queue_flags)
 #define blk_queue_nomerges(q)  test_bit(QUEUE_FLAG_NOMERGES, &(q)->queue_flags)
 #define blk_queue_noxmerges(q) \
        test_bit(QUEUE_FLAG_NOXMERGES, &(q)->queue_flags)
index 1a0cd270bb7a0850f643cc3902aa5f943279ec8c..324fe08ea3b140b7b8b92f7129ad334df2e260d5 100644 (file)
@@ -135,9 +135,6 @@ extern void *__alloc_bootmem_low_node(pg_data_t *pgdat,
 extern int reserve_bootmem_generic(unsigned long addr, unsigned long size,
                                   int flags);
 
-extern void *alloc_bootmem_section(unsigned long size,
-                                  unsigned long section_nr);
-
 #ifdef CONFIG_HAVE_ARCH_ALLOC_REMAP
 extern void *alloc_remap(int nid, unsigned long size);
 #else
index 72961c39576a4af5eb68a45f6bebc18e88935d2e..aaac4bba6f5c7faa1d2b95b13983323658c8188c 100644 (file)
@@ -30,6 +30,13 @@ struct pt_regs;
 #define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); }))
 #define BUILD_BUG_ON_NULL(e) ((void *)sizeof(struct { int:-!!(e); }))
 
+/*
+ * BUILD_BUG_ON_INVALID() permits the compiler to check the validity of the
+ * expression but avoids the generation of any code, even if that expression
+ * has side-effects.
+ */
+#define BUILD_BUG_ON_INVALID(e) ((void)(sizeof((__force long)(e))))
+
 /**
  * BUILD_BUG_ON - break compile if a condition is true.
  * @condition: the condition which the compiler should know is false.
index aa13392a7efbf2234add8465540869b75930a25b..d4080f309b5699d5fbaa5dbe96d38a1977be7693 100644 (file)
 struct ceph_auth_client;
 struct ceph_authorizer;
 
+struct ceph_auth_handshake {
+       struct ceph_authorizer *authorizer;
+       void *authorizer_buf;
+       size_t authorizer_buf_len;
+       void *authorizer_reply_buf;
+       size_t authorizer_reply_buf_len;
+};
+
 struct ceph_auth_client_ops {
        const char *name;
 
@@ -43,9 +51,7 @@ struct ceph_auth_client_ops {
         * the response to authenticate the service.
         */
        int (*create_authorizer)(struct ceph_auth_client *ac, int peer_type,
-                                struct ceph_authorizer **a,
-                                void **buf, size_t *len,
-                                void **reply_buf, size_t *reply_len);
+                                struct ceph_auth_handshake *auth);
        int (*verify_authorizer_reply)(struct ceph_auth_client *ac,
                                       struct ceph_authorizer *a, size_t len);
        void (*destroy_authorizer)(struct ceph_auth_client *ac,
index b8c60694b2b0977d4ecdb982d8bcaea5f0ef2673..e81ab30d4896329e29d47bea33d92e5634da6a89 100644 (file)
@@ -65,7 +65,7 @@ struct ceph_file_layout {
        __le32 fl_object_stripe_unit;  /* UNUSED.  for per-object parity, if any */
 
        /* object -> pg layout */
-       __le32 fl_pg_preferred; /* preferred primary for pg (-1 for none) */
+       __le32 fl_unused;       /* unused; used to be preferred primary (-1) */
        __le32 fl_pg_pool;      /* namespace, crush ruleset, rep level */
 } __attribute__ ((packed));
 
@@ -384,7 +384,7 @@ union ceph_mds_request_args {
                __le32 stripe_count;         /* ... */
                __le32 object_size;
                __le32 file_replication;
-               __le32 preferred;
+               __le32 unused;               /* used to be preferred osd */
        } __attribute__ ((packed)) open;
        struct {
                __le32 flags;
index 220ae21e819b1fb2623d19d8cf4f619862f11c42..d8615dee5808d3f55c93a38c6fdb66113f09a691 100644 (file)
@@ -46,9 +46,14 @@ static inline void ceph_decode_copy(void **p, void *pv, size_t n)
 /*
  * bounds check input.
  */
+static inline int ceph_has_room(void **p, void *end, size_t n)
+{
+       return end >= *p && n <= end - *p;
+}
+
 #define ceph_decode_need(p, end, n, bad)               \
        do {                                            \
-               if (unlikely(*(p) + (n) > (end)))       \
+               if (!likely(ceph_has_room(p, end, n)))  \
                        goto bad;                       \
        } while (0)
 
@@ -167,7 +172,7 @@ static inline void ceph_encode_string(void **p, void *end,
 
 #define ceph_encode_need(p, end, n, bad)               \
        do {                                            \
-               if (unlikely(*(p) + (n) > (end)))       \
+               if (!likely(ceph_has_room(p, end, n)))  \
                        goto bad;                       \
        } while (0)
 
index 3bff047f6b0f19d1037e4e7157fc785dd213f4cc..2521a95fa6d98597d1fafe4d4cff23ce9dc0f069 100644 (file)
@@ -25,9 +25,9 @@ struct ceph_connection_operations {
        void (*dispatch) (struct ceph_connection *con, struct ceph_msg *m);
 
        /* authorize an outgoing connection */
-       int (*get_authorizer) (struct ceph_connection *con,
-                              void **buf, int *len, int *proto,
-                              void **reply_buf, int *reply_len, int force_new);
+       struct ceph_auth_handshake *(*get_authorizer) (
+                               struct ceph_connection *con,
+                              int *proto, int force_new);
        int (*verify_authorizer_reply) (struct ceph_connection *con, int len);
        int (*invalidate_authorizer)(struct ceph_connection *con);
 
index 7c05ac202d90650069d4713ac2e13b3be9b86024..cedfb1a8434a11a0ba0b32348a0687e0ff9e7836 100644 (file)
@@ -6,9 +6,10 @@
 #include <linux/mempool.h>
 #include <linux/rbtree.h>
 
-#include "types.h"
-#include "osdmap.h"
-#include "messenger.h"
+#include <linux/ceph/types.h>
+#include <linux/ceph/osdmap.h>
+#include <linux/ceph/messenger.h>
+#include <linux/ceph/auth.h>
 
 /* 
  * Maximum object name size 
@@ -40,9 +41,7 @@ struct ceph_osd {
        struct list_head o_requests;
        struct list_head o_linger_requests;
        struct list_head o_osd_lru;
-       struct ceph_authorizer *o_authorizer;
-       void *o_authorizer_buf, *o_authorizer_reply_buf;
-       size_t o_authorizer_buf_len, o_authorizer_reply_buf_len;
+       struct ceph_auth_handshake o_auth;
        unsigned long lru_ttl;
        int o_marked_for_keepalive;
        struct list_head o_keepalive_item;
index ba4c205cbb016a141495e2e872ee6a3cfd57253a..311ef8d6aa9efc41b89ea1e6af9b07f538a84440 100644 (file)
@@ -65,8 +65,6 @@ struct ceph_osdmap {
 #define ceph_file_layout_cas_hash(l) ((__s32)le32_to_cpu((l).fl_cas_hash))
 #define ceph_file_layout_object_su(l) \
        ((__s32)le32_to_cpu((l).fl_object_stripe_unit))
-#define ceph_file_layout_pg_preferred(l) \
-       ((__s32)le32_to_cpu((l).fl_pg_preferred))
 #define ceph_file_layout_pg_pool(l) \
        ((__s32)le32_to_cpu((l).fl_pg_pool))
 
index 51a90b7f2d606a3dcb9bb582cd209add0c429351..e988037abd2a1afa25b9e22607295c0e915f1541 100644 (file)
@@ -1,6 +1,8 @@
 #ifndef _LINUX_COMPACTION_H
 #define _LINUX_COMPACTION_H
 
+#include <linux/node.h>
+
 /* Return values for compact_zone() and try_to_compact_pages() */
 /* compaction didn't start as it was not possible or direct reclaim was more suitable */
 #define COMPACT_SKIPPED                0
 /* The full zone was compacted */
 #define COMPACT_COMPLETE       3
 
+/*
+ * compaction supports three modes
+ *
+ * COMPACT_ASYNC_MOVABLE uses asynchronous migration and only scans
+ *    MIGRATE_MOVABLE pageblocks as migration sources and targets.
+ * COMPACT_ASYNC_UNMOVABLE uses asynchronous migration and only scans
+ *    MIGRATE_MOVABLE pageblocks as migration sources.
+ *    MIGRATE_UNMOVABLE pageblocks are scanned as potential migration
+ *    targets and convers them to MIGRATE_MOVABLE if possible
+ * COMPACT_SYNC uses synchronous migration and scans all pageblocks
+ */
+enum compact_mode {
+       COMPACT_ASYNC_MOVABLE,
+       COMPACT_ASYNC_UNMOVABLE,
+       COMPACT_SYNC,
+};
+
 #ifdef CONFIG_COMPACTION
 extern int sysctl_compact_memory;
 extern int sysctl_compaction_handler(struct ctl_table *table, int write,
index 5d46217f84adfaab0dbe679a7612da7062bb72c6..4e890394ef996e709c490439be23f0c6fe24292f 100644 (file)
@@ -577,8 +577,7 @@ extern ssize_t compat_rw_copy_check_uvector(int type,
                const struct compat_iovec __user *uvector,
                unsigned long nr_segs,
                unsigned long fast_segs, struct iovec *fast_pointer,
-               struct iovec **ret_pointer,
-               int check_access);
+               struct iovec **ret_pointer);
 
 extern void __user *compat_alloc_user_space(unsigned long len);
 
index 7230bb59a06fec1f09ec379027729ee472193c07..2e9b9ebbeb78927681026ddbceb0255bc9197aad 100644 (file)
@@ -177,6 +177,7 @@ extern void put_online_cpus(void);
 #define hotcpu_notifier(fn, pri)       cpu_notifier(fn, pri)
 #define register_hotcpu_notifier(nb)   register_cpu_notifier(nb)
 #define unregister_hotcpu_notifier(nb) unregister_cpu_notifier(nb)
+void clear_tasks_mm_cpumask(int cpu);
 int cpu_down(unsigned int cpu);
 
 #ifdef CONFIG_ARCH_CPU_PROBE_RELEASE
index 917dc5aeb1d4de6eff77341bc55c67df68c77fb6..ebbed2ce66379bd986fbf83f11e7ae8c32bf6070 100644 (file)
@@ -277,17 +277,13 @@ static inline void put_cred(const struct cred *_cred)
  * @task: The task to query
  *
  * Access the objective credentials of a task.  The caller must hold the RCU
- * readlock or the task must be dead and unable to change its own credentials.
+ * readlock.
  *
  * The result of this function should not be passed directly to get_cred();
  * rather get_task_cred() should be used instead.
  */
-#define __task_cred(task)                                              \
-       ({                                                              \
-               const struct task_struct *__t = (task);                 \
-               rcu_dereference_check(__t->real_cred,                   \
-                                     task_is_dead(__t));               \
-       })
+#define __task_cred(task)      \
+       rcu_dereference((task)->real_cred)
 
 /**
  * get_current_cred - Get the current task's subjective credentials
index 97e435b191f411380bbc546530c725093095231c..7c4750811b966e7d865484c2cf7020199c628164 100644 (file)
@@ -151,16 +151,6 @@ struct crush_map {
        struct crush_bucket **buckets;
        struct crush_rule **rules;
 
-       /*
-        * Parent pointers to identify the parent bucket a device or
-        * bucket in the hierarchy.  If an item appears more than
-        * once, this is the _last_ time it appeared (where buckets
-        * are processed in bucket id order, from -1 on down to
-        * -max_buckets.
-        */
-       __u32 *bucket_parents;
-       __u32 *device_parents;
-
        __s32 max_buckets;
        __u32 max_rules;
        __s32 max_devices;
@@ -168,8 +158,7 @@ struct crush_map {
 
 
 /* crush.c */
-extern int crush_get_bucket_item_weight(struct crush_bucket *b, int pos);
-extern void crush_calc_parents(struct crush_map *map);
+extern int crush_get_bucket_item_weight(const struct crush_bucket *b, int pos);
 extern void crush_destroy_bucket_uniform(struct crush_bucket_uniform *b);
 extern void crush_destroy_bucket_list(struct crush_bucket_list *b);
 extern void crush_destroy_bucket_tree(struct crush_bucket_tree *b);
@@ -177,4 +166,9 @@ extern void crush_destroy_bucket_straw(struct crush_bucket_straw *b);
 extern void crush_destroy_bucket(struct crush_bucket *b);
 extern void crush_destroy(struct crush_map *map);
 
+static inline int crush_calc_tree_node(int i)
+{
+       return ((i+1) << 1)-1;
+}
+
 #endif
index c46b99c18bb0ca772f87c567f4ab46cec54932db..71d79f44a7d0753faeb61a3072efdbacc1aa2371 100644 (file)
 
 #include "crush.h"
 
-extern int crush_find_rule(struct crush_map *map, int pool, int type, int size);
-extern int crush_do_rule(struct crush_map *map,
+extern int crush_find_rule(const struct crush_map *map, int ruleset, int type, int size);
+extern int crush_do_rule(const struct crush_map *map,
                         int ruleno,
                         int x, int *result, int result_max,
-                        int forcefeed,    /* -1 for none */
-                        __u32 *weights);
+                        const __u32 *weights);
 
 #endif
index d3fec584e8c3e93d253fce27d910b0bafdede38b..56377df391242d4639db14d9b23a8fa915db9335 100644 (file)
@@ -635,6 +635,18 @@ static inline struct dma_async_tx_descriptor *dmaengine_prep_slave_sg(
                                                  dir, flags, NULL);
 }
 
+#ifdef CONFIG_RAPIDIO_DMA_ENGINE
+struct rio_dma_ext;
+static inline struct dma_async_tx_descriptor *dmaengine_prep_rio_sg(
+       struct dma_chan *chan, struct scatterlist *sgl, unsigned int sg_len,
+       enum dma_transfer_direction dir, unsigned long flags,
+       struct rio_dma_ext *rio_ext)
+{
+       return chan->device->device_prep_slave_sg(chan, sgl, sg_len,
+                                                 dir, flags, rio_ext);
+}
+#endif
+
 static inline struct dma_async_tx_descriptor *dmaengine_prep_dma_cyclic(
                struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len,
                size_t period_len, enum dma_transfer_direction dir)
index 9e5f5607eba36b918db8beb8a3bf0c1a807e9ecc..47e3d48505843064b02fe9e7e167a1a591f5b83f 100644 (file)
@@ -53,7 +53,7 @@
 
 
 extern const char *drbd_buildtag(void);
-#define REL_VERSION "8.3.11"
+#define REL_VERSION "8.3.13"
 #define API_VERSION 88
 #define PRO_VERSION_MIN 86
 #define PRO_VERSION_MAX 96
@@ -112,8 +112,8 @@ enum drbd_ret_code {
        ERR_OPEN_MD_DISK        = 105,
        ERR_DISK_NOT_BDEV       = 107,
        ERR_MD_NOT_BDEV         = 108,
-       ERR_DISK_TO_SMALL       = 111,
-       ERR_MD_DISK_TO_SMALL    = 112,
+       ERR_DISK_TOO_SMALL      = 111,
+       ERR_MD_DISK_TOO_SMALL   = 112,
        ERR_BDCLAIM_DISK        = 114,
        ERR_BDCLAIM_MD_DISK     = 115,
        ERR_MD_IDX_INVALID      = 116,
index 447c36752385a52bc661adcea0cec1ca37ffad7f..fb670bf603f7c730469d0c56c22abb7f6274d192 100644 (file)
 #define DRBD_TIMEOUT_MAX 600
 #define DRBD_TIMEOUT_DEF 60       /* 6 seconds */
 
+ /* If backing disk takes longer than disk_timeout, mark the disk as failed */
+#define DRBD_DISK_TIMEOUT_MIN 0    /* 0 = disabled */
+#define DRBD_DISK_TIMEOUT_MAX 6000 /* 10 Minutes */
+#define DRBD_DISK_TIMEOUT_DEF 0    /* disabled */
+
   /* active connection retries when C_WF_CONNECTION */
 #define DRBD_CONNECT_INT_MIN 1
 #define DRBD_CONNECT_INT_MAX 120
@@ -60,7 +65,7 @@
 
  /* timeout for the ping packets.*/
 #define DRBD_PING_TIMEO_MIN  1
-#define DRBD_PING_TIMEO_MAX  100
+#define DRBD_PING_TIMEO_MAX  300
 #define DRBD_PING_TIMEO_DEF  5
 
   /* max number of write requests between write barriers */
index ab6159e4fcf0c6122cf840aef801fef822b0ab46..a8706f08ab367cfb9b8b9ac79b7f62cb94d0a876 100644 (file)
@@ -31,9 +31,12 @@ NL_PACKET(disk_conf, 3,
        NL_INTEGER(     56,     T_MAY_IGNORE,   max_bio_bvecs)
        NL_BIT(         57,     T_MAY_IGNORE,   no_disk_barrier)
        NL_BIT(         58,     T_MAY_IGNORE,   no_disk_drain)
+       NL_INTEGER(     89,     T_MAY_IGNORE,   disk_timeout)
 )
 
-NL_PACKET(detach, 4, )
+NL_PACKET(detach, 4,
+       NL_BIT(         88,     T_MANDATORY,    detach_force)
+)
 
 NL_PACKET(net_conf, 5,
        NL_STRING(      8,      T_MANDATORY,    my_addr,        128)
index c621d762bb2c9d18fd4ccda585c78491cafb1730..91ba3bae42ee53b7dac7c891284cee2b32ecb32a 100644 (file)
@@ -70,6 +70,25 @@ enum dev_type {
 #define DEV_FLAG_X32           BIT(DEV_X32)
 #define DEV_FLAG_X64           BIT(DEV_X64)
 
+/**
+ * enum hw_event_mc_err_type - type of the detected error
+ *
+ * @HW_EVENT_ERR_CORRECTED:    Corrected Error - Indicates that an ECC
+ *                             corrected error was detected
+ * @HW_EVENT_ERR_UNCORRECTED:  Uncorrected Error - Indicates an error that
+ *                             can't be corrected by ECC, but it is not
+ *                             fatal (maybe it is on an unused memory area,
+ *                             or the memory controller could recover from
+ *                             it for example, by re-trying the operation).
+ * @HW_EVENT_ERR_FATAL:                Fatal Error - Uncorrected error that could not
+ *                             be recovered.
+ */
+enum hw_event_mc_err_type {
+       HW_EVENT_ERR_CORRECTED,
+       HW_EVENT_ERR_UNCORRECTED,
+       HW_EVENT_ERR_FATAL,
+};
+
 /**
  * enum mem_type - memory types. For a more detailed reference, please see
  *                     http://en.wikipedia.org/wiki/DRAM
@@ -312,39 +331,142 @@ enum scrub_type {
  * PS - I enjoyed writing all that about as much as you enjoyed reading it.
  */
 
+/**
+ * enum edac_mc_layer - memory controller hierarchy layer
+ *
+ * @EDAC_MC_LAYER_BRANCH:      memory layer is named "branch"
+ * @EDAC_MC_LAYER_CHANNEL:     memory layer is named "channel"
+ * @EDAC_MC_LAYER_SLOT:                memory layer is named "slot"
+ * @EDAC_MC_LAYER_CHIP_SELECT: memory layer is named "chip select"
+ *
+ * This enum is used by the drivers to tell edac_mc_sysfs what name should
+ * be used when describing a memory stick location.
+ */
+enum edac_mc_layer_type {
+       EDAC_MC_LAYER_BRANCH,
+       EDAC_MC_LAYER_CHANNEL,
+       EDAC_MC_LAYER_SLOT,
+       EDAC_MC_LAYER_CHIP_SELECT,
+};
+
+/**
+ * struct edac_mc_layer - describes the memory controller hierarchy
+ * @layer:             layer type
+ * @size:              number of components per layer. For example,
+ *                     if the channel layer has two channels, size = 2
+ * @is_virt_csrow:     This layer is part of the "csrow" when old API
+ *                     compatibility mode is enabled. Otherwise, it is
+ *                     a channel
+ */
+struct edac_mc_layer {
+       enum edac_mc_layer_type type;
+       unsigned                size;
+       bool                    is_virt_csrow;
+};
+
+/*
+ * Maximum number of layers used by the memory controller to uniquely
+ * identify a single memory stick.
+ * NOTE: Changing this constant requires not only to change the constant
+ * below, but also to change the existing code at the core, as there are
+ * some code there that are optimized for 3 layers.
+ */
+#define EDAC_MAX_LAYERS                3
+
+/**
+ * EDAC_DIMM_PTR - Macro responsible to find a pointer inside a pointer array
+ *                for the element given by [layer0,layer1,layer2] position
+ *
+ * @layers:    a struct edac_mc_layer array, describing how many elements
+ *             were allocated for each layer
+ * @var:       name of the var where we want to get the pointer
+ *             (like mci->dimms)
+ * @n_layers:  Number of layers at the @layers array
+ * @layer0:    layer0 position
+ * @layer1:    layer1 position. Unused if n_layers < 2
+ * @layer2:    layer2 position. Unused if n_layers < 3
+ *
+ * For 1 layer, this macro returns &var[layer0]
+ * For 2 layers, this macro is similar to allocate a bi-dimensional array
+ *             and to return "&var[layer0][layer1]"
+ * For 3 layers, this macro is similar to allocate a tri-dimensional array
+ *             and to return "&var[layer0][layer1][layer2]"
+ *
+ * A loop could be used here to make it more generic, but, as we only have
+ * 3 layers, this is a little faster.
+ * By design, layers can never be 0 or more than 3. If that ever happens,
+ * a NULL is returned, causing an OOPS during the memory allocation routine,
+ * with would point to the developer that he's doing something wrong.
+ */
+#define EDAC_DIMM_PTR(layers, var, nlayers, layer0, layer1, layer2) ({ \
+       typeof(var) __p;                                                \
+       if ((nlayers) == 1)                                             \
+               __p = &var[layer0];                                     \
+       else if ((nlayers) == 2)                                        \
+               __p = &var[(layer1) + ((layers[1]).size * (layer0))];   \
+       else if ((nlayers) == 3)                                        \
+               __p = &var[(layer2) + ((layers[2]).size * ((layer1) +   \
+                           ((layers[1]).size * (layer0))))];           \
+       else                                                            \
+               __p = NULL;                                             \
+       __p;                                                            \
+})
+
+
+/* FIXME: add the proper per-location error counts */
+struct dimm_info {
+       char label[EDAC_MC_LABEL_LEN + 1];      /* DIMM label on motherboard */
+
+       /* Memory location data */
+       unsigned location[EDAC_MAX_LAYERS];
+
+       struct mem_ctl_info *mci;       /* the parent */
+
+       u32 grain;              /* granularity of reported error in bytes */
+       enum dev_type dtype;    /* memory device type */
+       enum mem_type mtype;    /* memory dimm type */
+       enum edac_type edac_mode;       /* EDAC mode for this dimm */
+
+       u32 nr_pages;                   /* number of pages on this dimm */
+
+       unsigned csrow, cschannel;      /* Points to the old API data */
+};
+
 /**
  * struct rank_info - contains the information for one DIMM rank
  *
  * @chan_idx:  channel number where the rank is (typically, 0 or 1)
  * @ce_count:  number of correctable errors for this rank
- * @label:     DIMM label. Different ranks for the same DIMM should be
- *             filled, on userspace, with the same label.
- *             FIXME: The core currently won't enforce it.
  * @csrow:     A pointer to the chip select row structure (the parent
  *             structure). The location of the rank is given by
  *             the (csrow->csrow_idx, chan_idx) vector.
+ * @dimm:      A pointer to the DIMM structure, where the DIMM label
+ *             information is stored.
+ *
+ * FIXME: Currently, the EDAC core model will assume one DIMM per rank.
+ *       This is a bad assumption, but it makes this patch easier. Later
+ *       patches in this series will fix this issue.
  */
 struct rank_info {
        int chan_idx;
-       u32 ce_count;
-       char label[EDAC_MC_LABEL_LEN + 1];
-       struct csrow_info *csrow;       /* the parent */
+       struct csrow_info *csrow;
+       struct dimm_info *dimm;
+
+       u32 ce_count;           /* Correctable Errors for this csrow */
 };
 
 struct csrow_info {
-       unsigned long first_page;       /* first page number in dimm */
-       unsigned long last_page;        /* last page number in dimm */
+       /* Used only by edac_mc_find_csrow_by_page() */
+       unsigned long first_page;       /* first page number in csrow */
+       unsigned long last_page;        /* last page number in csrow */
        unsigned long page_mask;        /* used for interleaving -
-                                        * 0UL for non intlv
-                                        */
-       u32 nr_pages;           /* number of pages in csrow */
-       u32 grain;              /* granularity of reported error in bytes */
-       int csrow_idx;          /* the chip-select row */
-       enum dev_type dtype;    /* memory device type */
+                                        * 0UL for non intlv */
+
+       int csrow_idx;                  /* the chip-select row */
+
        u32 ue_count;           /* Uncorrectable Errors for this csrow */
        u32 ce_count;           /* Correctable Errors for this csrow */
-       enum mem_type mtype;    /* memory csrow type */
-       enum edac_type edac_mode;       /* EDAC mode for this csrow */
+
        struct mem_ctl_info *mci;       /* the parent */
 
        struct kobject kobj;    /* sysfs kobject for this csrow */
@@ -426,8 +548,20 @@ struct mem_ctl_info {
        unsigned long (*ctl_page_to_phys) (struct mem_ctl_info * mci,
                                           unsigned long page);
        int mc_idx;
-       int nr_csrows;
        struct csrow_info *csrows;
+       unsigned nr_csrows, num_cschannel;
+
+       /* Memory Controller hierarchy */
+       unsigned n_layers;
+       struct edac_mc_layer *layers;
+       bool mem_is_per_rank;
+
+       /*
+        * DIMM info. Will eventually remove the entire csrows_info some day
+        */
+       unsigned tot_dimms;
+       struct dimm_info *dimms;
+
        /*
         * FIXME - what about controllers on other busses? - IDs must be
         * unique.  dev pointer should be sufficiently unique, but
@@ -440,12 +574,16 @@ struct mem_ctl_info {
        const char *dev_name;
        char proc_name[MC_PROC_NAME_MAX_LEN + 1];
        void *pvt_info;
-       u32 ue_noinfo_count;    /* Uncorrectable Errors w/o info */
-       u32 ce_noinfo_count;    /* Correctable Errors w/o info */
-       u32 ue_count;           /* Total Uncorrectable Errors for this MC */
-       u32 ce_count;           /* Total Correctable Errors for this MC */
        unsigned long start_time;       /* mci load start time (in jiffies) */
 
+       /*
+        * drivers shouldn't access those fields directly, as the core
+        * already handles that.
+        */
+       u32 ce_noinfo_count, ue_noinfo_count;
+       u32 ue_mc, ce_mc;
+       u32 *ce_per_layer[EDAC_MAX_LAYERS], *ue_per_layer[EDAC_MAX_LAYERS];
+
        struct completion complete;
 
        /* edac sysfs device control */
@@ -458,7 +596,7 @@ struct mem_ctl_info {
         * by the low level driver.
         *
         * Set by the low level driver to provide attributes at the
-        * controller level, same level as 'ue_count' and 'ce_count' above.
+        * controller level.
         * An array of structures, NULL terminated
         *
         * If attributes are desired, then set to array of attributes
index 7d4e0356f329253f8932e712693544d31e01cacd..c03af7687bb4fdd916d5bf3c835e801376132392 100644 (file)
@@ -28,12 +28,13 @@ typedef int (elevator_may_queue_fn) (struct request_queue *, int);
 
 typedef void (elevator_init_icq_fn) (struct io_cq *);
 typedef void (elevator_exit_icq_fn) (struct io_cq *);
-typedef int (elevator_set_req_fn) (struct request_queue *, struct request *, gfp_t);
+typedef int (elevator_set_req_fn) (struct request_queue *, struct request *,
+                                  struct bio *, gfp_t);
 typedef void (elevator_put_req_fn) (struct request *);
 typedef void (elevator_activate_req_fn) (struct request_queue *, struct request *);
 typedef void (elevator_deactivate_req_fn) (struct request_queue *, struct request *);
 
-typedef void *(elevator_init_fn) (struct request_queue *);
+typedef int (elevator_init_fn) (struct request_queue *);
 typedef void (elevator_exit_fn) (struct elevator_queue *);
 
 struct elevator_ops
@@ -129,7 +130,8 @@ extern void elv_unregister_queue(struct request_queue *q);
 extern int elv_may_queue(struct request_queue *, int);
 extern void elv_abort_queue(struct request_queue *);
 extern void elv_completed_request(struct request_queue *, struct request *);
-extern int elv_set_request(struct request_queue *, struct request *, gfp_t);
+extern int elv_set_request(struct request_queue *q, struct request *rq,
+                          struct bio *bio, gfp_t gfp_mask);
 extern void elv_put_request(struct request_queue *, struct request *);
 extern void elv_drain_elevator(struct request_queue *);
 
index 2d09bfa5c2628a3e1e396350b7d9d14f1a2791c8..e0de516374da37de6a95c35e79ab7cabb899d177 100644 (file)
@@ -17,6 +17,7 @@
 #define ENOIOCTLCMD    515     /* No ioctl command */
 #define ERESTART_RESTARTBLOCK 516 /* restart by calling sys_restart_syscall */
 #define EPROBE_DEFER   517     /* Driver requests probe retry */
+#define EOPENSTALE     518     /* open found a stale dentry */
 
 /* Defined for the NFSv3 protocol */
 #define EBADHANDLE     521     /* Illegal NFS file handle */
index 91bb4f27238cf156cb3ecc3daac22d37684aebd3..3c3ef19a625a26a38944cb06afca97b872d48234 100644 (file)
@@ -34,7 +34,7 @@ void eventfd_ctx_put(struct eventfd_ctx *ctx);
 struct file *eventfd_fget(int fd);
 struct eventfd_ctx *eventfd_ctx_fdget(int fd);
 struct eventfd_ctx *eventfd_ctx_fileget(struct file *file);
-int eventfd_signal(struct eventfd_ctx *ctx, int n);
+__u64 eventfd_signal(struct eventfd_ctx *ctx, __u64 n);
 ssize_t eventfd_ctx_read(struct eventfd_ctx *ctx, int no_wait, __u64 *cnt);
 int eventfd_ctx_remove_wait_queue(struct eventfd_ctx *ctx, wait_queue_t *wait,
                                  __u64 *cnt);
index 3a4cef5322dcab4d6b50b96243fa7187b2da1ebd..12291a7ee2759164026ac602ab1712b5d66faef0 100644 (file)
@@ -165,8 +165,8 @@ struct fid {
  */
 
 struct export_operations {
-       int (*encode_fh)(struct dentry *de, __u32 *fh, int *max_len,
-                       int connectable);
+       int (*encode_fh)(struct inode *inode, __u32 *fh, int *max_len,
+                       struct inode *parent);
        struct dentry * (*fh_to_dentry)(struct super_block *sb, struct fid *fid,
                        int fh_len, int fh_type);
        struct dentry * (*fh_to_parent)(struct super_block *sb, struct fid *fid,
index d31cb682e17371e0271947d59f2311818316152d..a3229d7ab9f26eb257950119c25a788df5fd9fc5 100644 (file)
@@ -554,6 +554,10 @@ struct fb_cursor_user {
 #define FB_EVENT_FB_UNBIND              0x0E
 /*      CONSOLE-SPECIFIC: remap all consoles to new fb - for vga switcheroo */
 #define FB_EVENT_REMAP_ALL_CONSOLE      0x0F
+/*      A hardware display blank early change occured */
+#define FB_EARLY_EVENT_BLANK           0x10
+/*      A hardware display blank revert early change occured */
+#define FB_R_EARLY_EVENT_BLANK         0x11
 
 struct fb_event {
        struct fb_info *info;
index cdc1a9630948e157c3b6e9f1916d0ebfb1bd88d2..51978ed43e973ccf70996c19c33820fd39edeabe 100644 (file)
@@ -173,6 +173,15 @@ struct inodes_stat_t {
 #define WRITE_FUA              (WRITE | REQ_SYNC | REQ_NOIDLE | REQ_FUA)
 #define WRITE_FLUSH_FUA                (WRITE | REQ_SYNC | REQ_NOIDLE | REQ_FLUSH | REQ_FUA)
 
+
+/*
+ * Flag for rw_copy_check_uvector and compat_rw_copy_check_uvector
+ * that indicates that they should check the contents of the iovec are
+ * valid, but not check the memory that the iovec elements
+ * points too.
+ */
+#define CHECK_IOVEC_ONLY -1
+
 #define SEL_IN         1
 #define SEL_OUT                2
 #define SEL_EX         4
@@ -1681,9 +1690,9 @@ struct inode_operations {
        ssize_t (*getxattr) (struct dentry *, const char *, void *, size_t);
        ssize_t (*listxattr) (struct dentry *, char *, size_t);
        int (*removexattr) (struct dentry *, const char *);
-       void (*truncate_range)(struct inode *, loff_t, loff_t);
        int (*fiemap)(struct inode *, struct fiemap_extent_info *, u64 start,
                      u64 len);
+       int (*update_time)(struct inode *, struct timespec *, int);
 } ____cacheline_aligned;
 
 struct seq_file;
@@ -1691,8 +1700,7 @@ struct seq_file;
 ssize_t rw_copy_check_uvector(int type, const struct iovec __user * uvector,
                              unsigned long nr_segs, unsigned long fast_segs,
                              struct iovec *fast_pointer,
-                             struct iovec **ret_pointer,
-                             int check_access);
+                             struct iovec **ret_pointer);
 
 extern ssize_t vfs_read(struct file *, char __user *, size_t, loff_t *);
 extern ssize_t vfs_write(struct file *, const char __user *, size_t, loff_t *);
@@ -1843,6 +1851,13 @@ static inline void inode_inc_iversion(struct inode *inode)
        spin_unlock(&inode->i_lock);
 }
 
+enum file_time_flags {
+       S_ATIME = 1,
+       S_MTIME = 2,
+       S_CTIME = 4,
+       S_VERSION = 8,
+};
+
 extern void touch_atime(struct path *);
 static inline void file_accessed(struct file *file)
 {
@@ -2454,8 +2469,6 @@ enum {
 };
 
 void dio_end_io(struct bio *bio, int error);
-void inode_dio_wait(struct inode *inode);
-void inode_dio_done(struct inode *inode);
 
 ssize_t __blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode,
        struct block_device *bdev, const struct iovec *iov, loff_t offset,
@@ -2470,12 +2483,11 @@ static inline ssize_t blockdev_direct_IO(int rw, struct kiocb *iocb,
                                    offset, nr_segs, get_block, NULL, NULL,
                                    DIO_LOCKING | DIO_SKIP_HOLES);
 }
-#else
-static inline void inode_dio_wait(struct inode *inode)
-{
-}
 #endif
 
+void inode_dio_wait(struct inode *inode);
+void inode_dio_done(struct inode *inode);
+
 extern const struct file_operations generic_ro_fops;
 
 #define special_file(m) (S_ISCHR(m)||S_ISBLK(m)||S_ISFIFO(m)||S_ISSOCK(m))
@@ -2579,7 +2591,7 @@ extern int inode_change_ok(const struct inode *, struct iattr *);
 extern int inode_newsize_ok(const struct inode *, loff_t offset);
 extern void setattr_copy(struct inode *inode, const struct iattr *attr);
 
-extern void file_update_time(struct file *file);
+extern int file_update_time(struct file *file);
 
 extern int generic_show_options(struct seq_file *m, struct dentry *root);
 extern void save_mount_options(struct super_block *sb, char *options);
index 91d0e0a34ef3185a6051d8394cab63dfb76a04cb..63d966d5c2ea7a382c2f42cc664c7804dec86f73 100644 (file)
@@ -60,7 +60,7 @@
 #define FS_EVENTS_POSS_ON_CHILD   (FS_ACCESS | FS_MODIFY | FS_ATTRIB |\
                                   FS_CLOSE_WRITE | FS_CLOSE_NOWRITE | FS_OPEN |\
                                   FS_MOVED_FROM | FS_MOVED_TO | FS_CREATE |\
-                                  FS_DELETE)
+                                  FS_DELETE | FS_OPEN_PERM | FS_ACCESS_PERM)
 
 #define FS_MOVE                        (FS_MOVED_FROM | FS_MOVED_TO)
 
index 73c28dea10ae395f1a7a7f4a517f174578dde313..7a114016ac7de83cf44190ebadec99b12f924cf4 100644 (file)
@@ -110,6 +110,9 @@ extern int lockdep_genl_is_held(void);
 #define genl_dereference(p)                                    \
        rcu_dereference_protected(p, lockdep_genl_is_held())
 
+#define MODULE_ALIAS_GENL_FAMILY(family)\
+ MODULE_ALIAS_NET_PF_PROTO_NAME(PF_NETLINK, NETLINK_GENERIC, "-family-" family)
+
 #endif /* __KERNEL__ */
 
 #endif /* __LINUX_GENERIC_NETLINK_H */
index c8af7a2efb5288e582e3fba9df08308d6c3f8076..4c59b11311870e74ebbeb110f77e2017be97c3bb 100644 (file)
@@ -59,6 +59,8 @@ extern pmd_t *page_check_address_pmd(struct page *page,
 #define HPAGE_PMD_MASK HPAGE_MASK
 #define HPAGE_PMD_SIZE HPAGE_SIZE
 
+extern bool is_vma_temporary_stack(struct vm_area_struct *vma);
+
 #define transparent_hugepage_enabled(__vma)                            \
        ((transparent_hugepage_flags &                                  \
          (1<<TRANSPARENT_HUGEPAGE_FLAG) ||                             \
index b66cb601435fa732388a3f2c79b3b1af00ebbc4b..ddfa04108baf14ade92a85d3a3f909ca9ab30589 100644 (file)
@@ -541,7 +541,7 @@ struct i2c_msg {
        __u16 flags;
 #define I2C_M_TEN              0x0010  /* this is a ten bit chip address */
 #define I2C_M_RD               0x0001  /* read data, from slave to master */
-#define I2C_M_NOSTART          0x4000  /* if I2C_FUNC_PROTOCOL_MANGLING */
+#define I2C_M_NOSTART          0x4000  /* if I2C_FUNC_NOSTART */
 #define I2C_M_REV_DIR_ADDR     0x2000  /* if I2C_FUNC_PROTOCOL_MANGLING */
 #define I2C_M_IGNORE_NAK       0x1000  /* if I2C_FUNC_PROTOCOL_MANGLING */
 #define I2C_M_NO_RD_ACK                0x0800  /* if I2C_FUNC_PROTOCOL_MANGLING */
@@ -554,8 +554,9 @@ struct i2c_msg {
 
 #define I2C_FUNC_I2C                   0x00000001
 #define I2C_FUNC_10BIT_ADDR            0x00000002
-#define I2C_FUNC_PROTOCOL_MANGLING     0x00000004 /* I2C_M_NOSTART etc. */
+#define I2C_FUNC_PROTOCOL_MANGLING     0x00000004 /* I2C_M_IGNORE_NAK etc. */
 #define I2C_FUNC_SMBUS_PEC             0x00000008
+#define I2C_FUNC_NOSTART               0x00000010 /* I2C_M_NOSTART */
 #define I2C_FUNC_SMBUS_BLOCK_PROC_CALL 0x00008000 /* SMBus 2.0 */
 #define I2C_FUNC_SMBUS_QUICK           0x00010000
 #define I2C_FUNC_SMBUS_READ_BYTE       0x00020000
index c91171599cb68825709aa12a9d79e80026bd9dfb..e68a8e53bb59acf87c2dd07259f77ce491d5ec1e 100644 (file)
@@ -142,8 +142,6 @@ request_any_context_irq(unsigned int irq, irq_handler_t handler,
 extern int __must_check
 request_percpu_irq(unsigned int irq, irq_handler_t handler,
                   const char *devname, void __percpu *percpu_dev_id);
-
-extern void exit_irq_thread(void);
 #else
 
 extern int __must_check
@@ -177,8 +175,6 @@ request_percpu_irq(unsigned int irq, irq_handler_t handler,
 {
        return request_irq(irq, handler, 0, devname, percpu_dev_id);
 }
-
-static inline void exit_irq_thread(void) { }
 #endif
 
 extern void free_irq(unsigned int, void *);
index 1a30180630343dabd49aa886fc7621964912afdf..df38db2ef45bbf50def2b9de1970225c8a76c6de 100644 (file)
@@ -6,11 +6,7 @@
 #include <linux/workqueue.h>
 
 enum {
-       ICQ_IOPRIO_CHANGED      = 1 << 0,
-       ICQ_CGROUP_CHANGED      = 1 << 1,
        ICQ_EXITED              = 1 << 2,
-
-       ICQ_CHANGED_MASK        = ICQ_IOPRIO_CHANGED | ICQ_CGROUP_CHANGED,
 };
 
 /*
@@ -100,6 +96,7 @@ struct io_cq {
  */
 struct io_context {
        atomic_long_t refcount;
+       atomic_t active_ref;
        atomic_t nr_tasks;
 
        /* all the fields below are protected by this lock */
@@ -120,29 +117,37 @@ struct io_context {
        struct work_struct release_work;
 };
 
-static inline struct io_context *ioc_task_link(struct io_context *ioc)
+/**
+ * get_io_context_active - get active reference on ioc
+ * @ioc: ioc of interest
+ *
+ * Only iocs with active reference can issue new IOs.  This function
+ * acquires an active reference on @ioc.  The caller must already have an
+ * active reference on @ioc.
+ */
+static inline void get_io_context_active(struct io_context *ioc)
 {
-       /*
-        * if ref count is zero, don't allow sharing (ioc is going away, it's
-        * a race).
-        */
-       if (ioc && atomic_long_inc_not_zero(&ioc->refcount)) {
-               atomic_inc(&ioc->nr_tasks);
-               return ioc;
-       }
+       WARN_ON_ONCE(atomic_long_read(&ioc->refcount) <= 0);
+       WARN_ON_ONCE(atomic_read(&ioc->active_ref) <= 0);
+       atomic_long_inc(&ioc->refcount);
+       atomic_inc(&ioc->active_ref);
+}
+
+static inline void ioc_task_link(struct io_context *ioc)
+{
+       get_io_context_active(ioc);
 
-       return NULL;
+       WARN_ON_ONCE(atomic_read(&ioc->nr_tasks) <= 0);
+       atomic_inc(&ioc->nr_tasks);
 }
 
 struct task_struct;
 #ifdef CONFIG_BLOCK
 void put_io_context(struct io_context *ioc);
+void put_io_context_active(struct io_context *ioc);
 void exit_io_context(struct task_struct *task);
 struct io_context *get_task_io_context(struct task_struct *task,
                                       gfp_t gfp_flags, int node);
-void ioc_ioprio_changed(struct io_context *ioc, int ioprio);
-void ioc_cgroup_changed(struct io_context *ioc);
-unsigned int icq_get_changed(struct io_cq *icq);
 #else
 struct io_context;
 static inline void put_io_context(struct io_context *ioc) { }
index d937580417ba668d343b30b1741d59139f7924b9..450293f6d68b6a9bfdbd74fdc3304a63c122505a 100644 (file)
@@ -35,12 +35,13 @@ struct iommu_domain;
 #define IOMMU_FAULT_WRITE      0x1
 
 typedef int (*iommu_fault_handler_t)(struct iommu_domain *,
-                               struct device *, unsigned long, int);
+                       struct device *, unsigned long, int, void *);
 
 struct iommu_domain {
        struct iommu_ops *ops;
        void *priv;
        iommu_fault_handler_t handler;
+       void *handler_token;
 };
 
 #define IOMMU_CAP_CACHE_COHERENCY      0x1
@@ -95,7 +96,7 @@ extern phys_addr_t iommu_iova_to_phys(struct iommu_domain *domain,
 extern int iommu_domain_has_cap(struct iommu_domain *domain,
                                unsigned long cap);
 extern void iommu_set_fault_handler(struct iommu_domain *domain,
-                                       iommu_fault_handler_t handler);
+                       iommu_fault_handler_t handler, void *token);
 extern int iommu_device_group(struct device *dev, unsigned int *groupid);
 
 /**
@@ -132,7 +133,8 @@ static inline int report_iommu_fault(struct iommu_domain *domain,
         * invoke it.
         */
        if (domain->handler)
-               ret = domain->handler(domain, dev, iova, flags);
+               ret = domain->handler(domain, dev, iova, flags,
+                                               domain->handler_token);
 
        return ret;
 }
@@ -191,7 +193,7 @@ static inline int domain_has_cap(struct iommu_domain *domain,
 }
 
 static inline void iommu_set_fault_handler(struct iommu_domain *domain,
-                                       iommu_fault_handler_t handler)
+                               iommu_fault_handler_t handler, void *token)
 {
 }
 
index 76dad48088474462b5f21deeff0414b40aca6b8e..beb9ce1c2c233595e47602d264782e56fdfc4682 100644 (file)
@@ -42,26 +42,14 @@ enum {
 };
 
 /*
- * if process has set io priority explicitly, use that. if not, convert
- * the cpu scheduler nice value to an io priority
+ * Fallback BE priority
  */
 #define IOPRIO_NORM    (4)
-static inline int task_ioprio(struct io_context *ioc)
-{
-       if (ioprio_valid(ioc->ioprio))
-               return IOPRIO_PRIO_DATA(ioc->ioprio);
-
-       return IOPRIO_NORM;
-}
-
-static inline int task_ioprio_class(struct io_context *ioc)
-{
-       if (ioprio_valid(ioc->ioprio))
-               return IOPRIO_PRIO_CLASS(ioc->ioprio);
-
-       return IOPRIO_CLASS_BE;
-}
 
+/*
+ * if process has set io priority explicitly, use that. if not, convert
+ * the cpu scheduler nice value to an io priority
+ */
 static inline int task_nice_ioprio(struct task_struct *task)
 {
        return (task_nice(task) + 20) / 5;
index 8a297a5e794cc8e51c22351098b80a35ce43ef09..5499c92a91539afcc0987d49fe6477acad2d16e4 100644 (file)
@@ -62,6 +62,8 @@ struct ipc_namespace {
        unsigned int    mq_queues_max;   /* initialized to DFLT_QUEUESMAX */
        unsigned int    mq_msg_max;      /* initialized to DFLT_MSGMAX */
        unsigned int    mq_msgsize_max;  /* initialized to DFLT_MSGSIZEMAX */
+       unsigned int    mq_msg_default;
+       unsigned int    mq_msgsize_default;
 
        /* user_ns which owns the ipc ns */
        struct user_namespace *user_ns;
@@ -90,11 +92,41 @@ static inline void shm_destroy_orphaned(struct ipc_namespace *ns) {}
 
 #ifdef CONFIG_POSIX_MQUEUE
 extern int mq_init_ns(struct ipc_namespace *ns);
-/* default values */
-#define DFLT_QUEUESMAX 256     /* max number of message queues */
-#define DFLT_MSGMAX    10      /* max number of messages in each queue */
-#define HARD_MSGMAX    (32768*sizeof(void *)/4)
-#define DFLT_MSGSIZEMAX 8192   /* max message size */
+/*
+ * POSIX Message Queue default values:
+ *
+ * MIN_*: Lowest value an admin can set the maximum unprivileged limit to
+ * DFLT_*MAX: Default values for the maximum unprivileged limits
+ * DFLT_{MSG,MSGSIZE}: Default values used when the user doesn't supply
+ *   an attribute to the open call and the queue must be created
+ * HARD_*: Highest value the maximums can be set to.  These are enforced
+ *   on CAP_SYS_RESOURCE apps as well making them inviolate (so make them
+ *   suitably high)
+ *
+ * POSIX Requirements:
+ *   Per app minimum openable message queues - 8.  This does not map well
+ *     to the fact that we limit the number of queues on a per namespace
+ *     basis instead of a per app basis.  So, make the default high enough
+ *     that no given app should have a hard time opening 8 queues.
+ *   Minimum maximum for HARD_MSGMAX - 32767.  I bumped this to 65536.
+ *   Minimum maximum for HARD_MSGSIZEMAX - POSIX is silent on this.  However,
+ *     we have run into a situation where running applications in the wild
+ *     require this to be at least 5MB, and preferably 10MB, so I set the
+ *     value to 16MB in hopes that this user is the worst of the bunch and
+ *     the new maximum will handle anyone else.  I may have to revisit this
+ *     in the future.
+ */
+#define MIN_QUEUESMAX                  1
+#define DFLT_QUEUESMAX               256
+#define HARD_QUEUESMAX              1024
+#define MIN_MSGMAX                     1
+#define DFLT_MSG                      10U
+#define DFLT_MSGMAX                   10
+#define HARD_MSGMAX                65536
+#define MIN_MSGSIZEMAX               128
+#define DFLT_MSGSIZE                8192U
+#define DFLT_MSGSIZEMAX                     8192
+#define HARD_MSGSIZEMAX            (16*1024*1024)
 #else
 static inline int mq_init_ns(struct ipc_namespace *ns) { return 0; }
 #endif
index 912c30a8ddb1e47cd732fbd95f281238ca601ae0..f334c7fab96762ab4131c9886df87d4d6d4dde9d 100644 (file)
@@ -31,6 +31,7 @@
 #include <linux/mutex.h>
 #include <linux/timer.h>
 #include <linux/slab.h>
+#include <crypto/hash.h>
 #endif
 
 #define journal_oom_retry 1
@@ -147,12 +148,24 @@ typedef struct journal_header_s
 #define JBD2_CRC32_CHKSUM   1
 #define JBD2_MD5_CHKSUM     2
 #define JBD2_SHA1_CHKSUM    3
+#define JBD2_CRC32C_CHKSUM  4
 
 #define JBD2_CRC32_CHKSUM_SIZE 4
 
 #define JBD2_CHECKSUM_BYTES (32 / sizeof(u32))
 /*
  * Commit block header for storing transactional checksums:
+ *
+ * NOTE: If FEATURE_COMPAT_CHECKSUM (checksum v1) is set, the h_chksum*
+ * fields are used to store a checksum of the descriptor and data blocks.
+ *
+ * If FEATURE_INCOMPAT_CSUM_V2 (checksum v2) is set, then the h_chksum
+ * field is used to store crc32c(uuid+commit_block).  Each journal metadata
+ * block gets its own checksum, and data block checksums are stored in
+ * journal_block_tag (in the descriptor).  The other h_chksum* fields are
+ * not used.
+ *
+ * Checksum v1 and v2 are mutually exclusive features.
  */
 struct commit_header {
        __be32          h_magic;
@@ -175,13 +188,19 @@ struct commit_header {
 typedef struct journal_block_tag_s
 {
        __be32          t_blocknr;      /* The on-disk block number */
-       __be32          t_flags;        /* See below */
+       __be16          t_checksum;     /* truncated crc32c(uuid+seq+block) */
+       __be16          t_flags;        /* See below */
        __be32          t_blocknr_high; /* most-significant high 32bits. */
 } journal_block_tag_t;
 
 #define JBD2_TAG_SIZE32 (offsetof(journal_block_tag_t, t_blocknr_high))
 #define JBD2_TAG_SIZE64 (sizeof(journal_block_tag_t))
 
+/* Tail of descriptor block, for checksumming */
+struct jbd2_journal_block_tail {
+       __be32          t_checksum;     /* crc32c(uuid+descr_block) */
+};
+
 /*
  * The revoke descriptor: used on disk to describe a series of blocks to
  * be revoked from the log
@@ -192,6 +211,10 @@ typedef struct jbd2_journal_revoke_header_s
        __be32           r_count;       /* Count of bytes used in the block */
 } jbd2_journal_revoke_header_t;
 
+/* Tail of revoke block, for checksumming */
+struct jbd2_journal_revoke_tail {
+       __be32          r_checksum;     /* crc32c(uuid+revoke_block) */
+};
 
 /* Definitions for the journal tag flags word: */
 #define JBD2_FLAG_ESCAPE               1       /* on-disk block is escaped */
@@ -241,7 +264,10 @@ typedef struct journal_superblock_s
        __be32  s_max_trans_data;       /* Limit of data blocks per trans. */
 
 /* 0x0050 */
-       __u32   s_padding[44];
+       __u8    s_checksum_type;        /* checksum type */
+       __u8    s_padding2[3];
+       __u32   s_padding[42];
+       __be32  s_checksum;             /* crc32c(superblock) */
 
 /* 0x0100 */
        __u8    s_users[16*48];         /* ids of all fs'es sharing the log */
@@ -263,13 +289,15 @@ typedef struct journal_superblock_s
 #define JBD2_FEATURE_INCOMPAT_REVOKE           0x00000001
 #define JBD2_FEATURE_INCOMPAT_64BIT            0x00000002
 #define JBD2_FEATURE_INCOMPAT_ASYNC_COMMIT     0x00000004
+#define JBD2_FEATURE_INCOMPAT_CSUM_V2          0x00000008
 
 /* Features known to this kernel version: */
 #define JBD2_KNOWN_COMPAT_FEATURES     JBD2_FEATURE_COMPAT_CHECKSUM
 #define JBD2_KNOWN_ROCOMPAT_FEATURES   0
 #define JBD2_KNOWN_INCOMPAT_FEATURES   (JBD2_FEATURE_INCOMPAT_REVOKE | \
                                        JBD2_FEATURE_INCOMPAT_64BIT | \
-                                       JBD2_FEATURE_INCOMPAT_ASYNC_COMMIT)
+                                       JBD2_FEATURE_INCOMPAT_ASYNC_COMMIT | \
+                                       JBD2_FEATURE_INCOMPAT_CSUM_V2)
 
 #ifdef __KERNEL__
 
@@ -939,6 +967,12 @@ struct journal_s
         * superblock pointer here
         */
        void *j_private;
+
+       /* Reference to checksum algorithm driver via cryptoapi */
+       struct crypto_shash *j_chksum_driver;
+
+       /* Precomputed journal UUID checksum for seeding other checksums */
+       __u32 j_csum_seed;
 };
 
 /*
@@ -1268,6 +1302,25 @@ static inline int jbd_space_needed(journal_t *journal)
 
 extern int jbd_blocks_per_page(struct inode *inode);
 
+static inline u32 jbd2_chksum(journal_t *journal, u32 crc,
+                             const void *address, unsigned int length)
+{
+       struct {
+               struct shash_desc shash;
+               char ctx[crypto_shash_descsize(journal->j_chksum_driver)];
+       } desc;
+       int err;
+
+       desc.shash.tfm = journal->j_chksum_driver;
+       desc.shash.flags = 0;
+       *(u32 *)desc.ctx = crc;
+
+       err = crypto_shash_update(&desc.shash, address, length);
+       BUG_ON(err);
+
+       return *(u32 *)desc.ctx;
+}
+
 #ifdef __KERNEL__
 
 #define buffer_trace_init(bh)  do {} while (0)
index 6230f8556a4eeac37bcaaa83a4cc913177e09c56..6133679bc4c01ace20a0114fd50ff7c3481c7eb9 100644 (file)
@@ -12,6 +12,7 @@ enum jbd_state_bits {
        BH_State,               /* Pins most journal_head state */
        BH_JournalHead,         /* Pins bh->b_private and jh->b_bh */
        BH_Unshadow,            /* Dummy bit, for BJ_Shadow wakeup filtering */
+       BH_Verified,            /* Metadata block has been verified ok */
        BH_JBDPrivateStart,     /* First bit available for private use by FS */
 };
 
@@ -24,6 +25,7 @@ TAS_BUFFER_FNS(Revoked, revoked)
 BUFFER_FNS(RevokeValid, revokevalid)
 TAS_BUFFER_FNS(RevokeValid, revokevalid)
 BUFFER_FNS(Freed, freed)
+BUFFER_FNS(Verified, verified)
 
 static inline struct buffer_head *jh2bh(struct journal_head *jh)
 {
index 387571959dd9a1219c90dc952c29f8382698f397..6883e197acb9e939156c4934d9cc7150b1b107f5 100644 (file)
@@ -36,6 +36,7 @@ const char *kallsyms_lookup(unsigned long addr,
 
 /* Look up a kernel symbol and return it in a text buffer. */
 extern int sprint_symbol(char *buffer, unsigned long address);
+extern int sprint_symbol_no_offset(char *buffer, unsigned long address);
 extern int sprint_backtrace(char *buffer, unsigned long address);
 
 /* Look up a kernel symbol and print it to the kernel messages. */
@@ -80,6 +81,12 @@ static inline int sprint_symbol(char *buffer, unsigned long addr)
        return 0;
 }
 
+static inline int sprint_symbol_no_offset(char *buffer, unsigned long addr)
+{
+       *buffer = '\0';
+       return 0;
+}
+
 static inline int sprint_backtrace(char *buffer, unsigned long addr)
 {
        *buffer = '\0';
diff --git a/include/linux/kcmp.h b/include/linux/kcmp.h
new file mode 100644 (file)
index 0000000..2dcd1b3
--- /dev/null
@@ -0,0 +1,17 @@
+#ifndef _LINUX_KCMP_H
+#define _LINUX_KCMP_H
+
+/* Comparison type */
+enum kcmp_type {
+       KCMP_FILE,
+       KCMP_VM,
+       KCMP_FILES,
+       KCMP_FS,
+       KCMP_SIGHAND,
+       KCMP_IO,
+       KCMP_SYSVSEM,
+
+       KCMP_TYPES,
+};
+
+#endif /* _LINUX_KCMP_H */
index 26a65711676f421dd1e287734ac4f781c713c8b0..a1bdf6966357b6b914d101c7545ef306fa395bdb 100644 (file)
@@ -32,6 +32,8 @@
 #define KPF_KSM                        21
 #define KPF_THP                        22
 
+#ifdef __KERNEL__
+
 /* kernel hacking assistances
  * WARNING: subject to change, never rely on them!
  */
@@ -44,4 +46,6 @@
 #define KPF_ARCH               38
 #define KPF_UNCACHED           39
 
+#endif /* __KERNEL__ */
+
 #endif /* LINUX_KERNEL_PAGE_FLAGS_H */
index ec55a3c8ba77db1ceee13a8e2cff5b014895b2e2..e07f5e0c5df4400eb34ac0d89150003e512381b0 100644 (file)
@@ -35,6 +35,7 @@
 #define LLONG_MAX      ((long long)(~0ULL>>1))
 #define LLONG_MIN      (-LLONG_MAX - 1)
 #define ULLONG_MAX     (~0ULL)
+#define SIZE_MAX       (~(size_t)0)
 
 #define STACK_MAGIC    0xdeadbeef
 
index 0d7d6a1b172f29fde03d030168b253197b84f479..37c5f7261142c24fa582121d2bdfd9479285f681 100644 (file)
@@ -1,8 +1,58 @@
 #ifndef LINUX_KEXEC_H
 #define LINUX_KEXEC_H
 
-#ifdef CONFIG_KEXEC
+/* kexec system call -  It loads the new kernel to boot into.
+ * kexec does not sync, or unmount filesystems so if you need
+ * that to happen you need to do that yourself.
+ */
+
 #include <linux/types.h>
+
+/* kexec flags for different usage scenarios */
+#define KEXEC_ON_CRASH         0x00000001
+#define KEXEC_PRESERVE_CONTEXT 0x00000002
+#define KEXEC_ARCH_MASK                0xffff0000
+
+/* These values match the ELF architecture values.
+ * Unless there is a good reason that should continue to be the case.
+ */
+#define KEXEC_ARCH_DEFAULT ( 0 << 16)
+#define KEXEC_ARCH_386     ( 3 << 16)
+#define KEXEC_ARCH_X86_64  (62 << 16)
+#define KEXEC_ARCH_PPC     (20 << 16)
+#define KEXEC_ARCH_PPC64   (21 << 16)
+#define KEXEC_ARCH_IA_64   (50 << 16)
+#define KEXEC_ARCH_ARM     (40 << 16)
+#define KEXEC_ARCH_S390    (22 << 16)
+#define KEXEC_ARCH_SH      (42 << 16)
+#define KEXEC_ARCH_MIPS_LE (10 << 16)
+#define KEXEC_ARCH_MIPS    ( 8 << 16)
+
+/* The artificial cap on the number of segments passed to kexec_load. */
+#define KEXEC_SEGMENT_MAX 16
+
+#ifndef __KERNEL__
+/*
+ * This structure is used to hold the arguments that are used when
+ * loading  kernel binaries.
+ */
+struct kexec_segment {
+       const void *buf;
+       size_t bufsz;
+       const void *mem;
+       size_t memsz;
+};
+
+/* Load a new kernel image as described by the kexec_segment array
+ * consisting of passed number of segments at the entry-point address.
+ * The flags allow different useage types.
+ */
+extern int kexec_load(void *, size_t, struct kexec_segment *,
+               unsigned long int);
+#endif /* __KERNEL__ */
+
+#ifdef __KERNEL__
+#ifdef CONFIG_KEXEC
 #include <linux/list.h>
 #include <linux/linkage.h>
 #include <linux/compat.h>
@@ -67,11 +117,10 @@ typedef unsigned long kimage_entry_t;
 #define IND_DONE         0x4
 #define IND_SOURCE       0x8
 
-#define KEXEC_SEGMENT_MAX 16
 struct kexec_segment {
        void __user *buf;
        size_t bufsz;
-       unsigned long mem;      /* User space sees this as a (void *) ... */
+       unsigned long mem;
        size_t memsz;
 };
 
@@ -175,25 +224,6 @@ extern struct kimage *kexec_crash_image;
 #define kexec_flush_icache_page(page)
 #endif
 
-#define KEXEC_ON_CRASH         0x00000001
-#define KEXEC_PRESERVE_CONTEXT 0x00000002
-#define KEXEC_ARCH_MASK                0xffff0000
-
-/* These values match the ELF architecture values.
- * Unless there is a good reason that should continue to be the case.
- */
-#define KEXEC_ARCH_DEFAULT ( 0 << 16)
-#define KEXEC_ARCH_386     ( 3 << 16)
-#define KEXEC_ARCH_X86_64  (62 << 16)
-#define KEXEC_ARCH_PPC     (20 << 16)
-#define KEXEC_ARCH_PPC64   (21 << 16)
-#define KEXEC_ARCH_IA_64   (50 << 16)
-#define KEXEC_ARCH_ARM     (40 << 16)
-#define KEXEC_ARCH_S390    (22 << 16)
-#define KEXEC_ARCH_SH      (42 << 16)
-#define KEXEC_ARCH_MIPS_LE (10 << 16)
-#define KEXEC_ARCH_MIPS    ( 8 << 16)
-
 /* List of defined/legal kexec flags */
 #ifndef CONFIG_KEXEC_JUMP
 #define KEXEC_FLAGS    KEXEC_ON_CRASH
@@ -228,4 +258,5 @@ struct task_struct;
 static inline void crash_kexec(struct pt_regs *regs) { }
 static inline int kexec_should_crash(struct task_struct *p) { return 0; }
 #endif /* CONFIG_KEXEC */
+#endif /* __KERNEL__ */
 #endif /* LINUX_KEXEC_H */
index 5231800770e1ea3b8cc0a9b081668b4edbdf55b8..4cd22ed627efd79205860b0efa6dde15a07897ed 100644 (file)
@@ -308,9 +308,6 @@ static inline bool key_is_instantiated(const struct key *key)
 #ifdef CONFIG_SYSCTL
 extern ctl_table key_sysctls[];
 #endif
-
-extern void key_replace_session_keyring(void);
-
 /*
  * the userspace interface
  */
@@ -334,7 +331,6 @@ extern void key_init(void);
 #define key_fsuid_changed(t)           do { } while(0)
 #define key_fsgid_changed(t)           do { } while(0)
 #define key_init()                     do { } while(0)
-#define key_replace_session_keyring()  do { } while(0)
 
 #endif /* CONFIG_KEYS */
 #endif /* __KERNEL__ */
index dd99c329e1616ec76af1c9e8b2d11cee08434aea..5398d5807075cd2649f99363e32c49e09c5d6a75 100644 (file)
@@ -66,40 +66,10 @@ struct subprocess_info {
        void *data;
 };
 
-/* Allocate a subprocess_info structure */
-struct subprocess_info *call_usermodehelper_setup(char *path, char **argv,
-                                                 char **envp, gfp_t gfp_mask);
-
-/* Set various pieces of state into the subprocess_info structure */
-void call_usermodehelper_setfns(struct subprocess_info *info,
-                   int (*init)(struct subprocess_info *info, struct cred *new),
-                   void (*cleanup)(struct subprocess_info *info),
-                   void *data);
-
-/* Actually execute the sub-process */
-int call_usermodehelper_exec(struct subprocess_info *info, int wait);
-
-/* Free the subprocess_info. This is only needed if you're not going
-   to call call_usermodehelper_exec */
-void call_usermodehelper_freeinfo(struct subprocess_info *info);
-
-static inline int
+extern int
 call_usermodehelper_fns(char *path, char **argv, char **envp, int wait,
                        int (*init)(struct subprocess_info *info, struct cred *new),
-                       void (*cleanup)(struct subprocess_info *), void *data)
-{
-       struct subprocess_info *info;
-       gfp_t gfp_mask = (wait == UMH_NO_WAIT) ? GFP_ATOMIC : GFP_KERNEL;
-
-       info = call_usermodehelper_setup(path, argv, envp, gfp_mask);
-
-       if (info == NULL)
-               return -ENOMEM;
-
-       call_usermodehelper_setfns(info, init, cleanup, data);
-
-       return call_usermodehelper_exec(info, wait);
-}
+                       void (*cleanup)(struct subprocess_info *), void *data);
 
 static inline int
 call_usermodehelper(char *path, char **argv, char **envp, int wait)
index 8877123f2d6e61dd8b22da7c9b8994f13d05e7f9..e00c3b0ebc6bd7303afdb0ffa448a31c0d000911 100644 (file)
@@ -40,6 +40,16 @@ struct lcd_ops {
        /* Get the LCD panel power status (0: full on, 1..3: controller
           power on, flat panel power off, 4: full off), see FB_BLANK_XXX */
        int (*get_power)(struct lcd_device *);
+       /*
+        * Enable or disable power to the LCD(0: on; 4: off, see FB_BLANK_XXX)
+        * and this callback would be called proir to fb driver's callback.
+        *
+        * P.S. note that if early_set_power is not NULL then early fb notifier
+        *      would be registered.
+        */
+       int (*early_set_power)(struct lcd_device *, int power);
+       /* revert the effects of the early blank event. */
+       int (*r_early_set_power)(struct lcd_device *, int power);
        /* Enable or disable power to the LCD (0: on; 4: off, see FB_BLANK_XXX) */
        int (*set_power)(struct lcd_device *, int power);
        /* Get the current contrast setting (0-max_contrast) */
index eeae6e742471f0bbf796439ba21668e511bc4df5..4b133479d6ea30ab5eab246931a04d6cfd353abd 100644 (file)
@@ -92,7 +92,7 @@ struct lm3530_pwm_data {
  * @als2_resistor_sel: internal resistance from ALS2 input to ground
  * @als_vmin: als input voltage calibrated for max brightness in mV
  * @als_vmax: als input voltage calibrated for min brightness in mV
- * @brt_val: brightness value (0-255)
+ * @brt_val: brightness value (0-127)
  * @pwm_data: PWM control functions (only valid when the mode is PWM)
  */
 struct lm3530_platform_data {
index 5884def15a24872a2b4be5ef9abe815fad233388..39eee41d8c6f4deee39d6904087a567657a2349e 100644 (file)
@@ -73,6 +73,8 @@ struct led_classdev {
        struct led_trigger      *trigger;
        struct list_head         trig_list;
        void                    *trigger_data;
+       /* true if activated - deactivate routine uses it to do cleanup */
+       bool                    activated;
 #endif
 };
 
index 87f402ccec55567330943ab774ffb12ae21c7da8..f01e5f6d1f07a4966927bb7acd5707f8f77904c8 100644 (file)
 #include <linux/lockdep.h>
 #include <linux/percpu.h>
 #include <linux/cpu.h>
+#include <linux/notifier.h>
 
 /* can make br locks by using local lock for read side, global lock for write */
-#define br_lock_init(name)     name##_lock_init()
-#define br_read_lock(name)     name##_local_lock()
-#define br_read_unlock(name)   name##_local_unlock()
-#define br_write_lock(name)    name##_global_lock_online()
-#define br_write_unlock(name)  name##_global_unlock_online()
+#define br_lock_init(name)     lg_lock_init(name, #name)
+#define br_read_lock(name)     lg_local_lock(name)
+#define br_read_unlock(name)   lg_local_unlock(name)
+#define br_write_lock(name)    lg_global_lock(name)
+#define br_write_unlock(name)  lg_global_unlock(name)
 
-#define DECLARE_BRLOCK(name)   DECLARE_LGLOCK(name)
 #define DEFINE_BRLOCK(name)    DEFINE_LGLOCK(name)
 
-
-#define lg_lock_init(name)     name##_lock_init()
-#define lg_local_lock(name)    name##_local_lock()
-#define lg_local_unlock(name)  name##_local_unlock()
-#define lg_local_lock_cpu(name, cpu)   name##_local_lock_cpu(cpu)
-#define lg_local_unlock_cpu(name, cpu) name##_local_unlock_cpu(cpu)
-#define lg_global_lock(name)   name##_global_lock()
-#define lg_global_unlock(name) name##_global_unlock()
-#define lg_global_lock_online(name) name##_global_lock_online()
-#define lg_global_unlock_online(name) name##_global_unlock_online()
-
 #ifdef CONFIG_DEBUG_LOCK_ALLOC
 #define LOCKDEP_INIT_MAP lockdep_init_map
 
 #define DEFINE_LGLOCK_LOCKDEP(name)
 #endif
 
-
-#define DECLARE_LGLOCK(name)                                           \
- extern void name##_lock_init(void);                                   \
- extern void name##_local_lock(void);                                  \
- extern void name##_local_unlock(void);                                        \
- extern void name##_local_lock_cpu(int cpu);                           \
- extern void name##_local_unlock_cpu(int cpu);                         \
- extern void name##_global_lock(void);                                 \
- extern void name##_global_unlock(void);                               \
- extern void name##_global_lock_online(void);                          \
- extern void name##_global_unlock_online(void);                                \
+struct lglock {
+       arch_spinlock_t __percpu *lock;
+#ifdef CONFIG_DEBUG_LOCK_ALLOC
+       struct lock_class_key lock_key;
+       struct lockdep_map    lock_dep_map;
+#endif
+};
 
 #define DEFINE_LGLOCK(name)                                            \
-                                                                       \
- DEFINE_SPINLOCK(name##_cpu_lock);                                     \
- cpumask_t name##_cpus __read_mostly;                                  \
- DEFINE_PER_CPU(arch_spinlock_t, name##_lock);                         \
- DEFINE_LGLOCK_LOCKDEP(name);                                          \
-                                                                       \
- static int                                                            \
- name##_lg_cpu_callback(struct notifier_block *nb,                     \
-                               unsigned long action, void *hcpu)       \
- {                                                                     \
-       switch (action & ~CPU_TASKS_FROZEN) {                           \
-       case CPU_UP_PREPARE:                                            \
-               spin_lock(&name##_cpu_lock);                            \
-               cpu_set((unsigned long)hcpu, name##_cpus);              \
-               spin_unlock(&name##_cpu_lock);                          \
-               break;                                                  \
-       case CPU_UP_CANCELED: case CPU_DEAD:                            \
-               spin_lock(&name##_cpu_lock);                            \
-               cpu_clear((unsigned long)hcpu, name##_cpus);            \
-               spin_unlock(&name##_cpu_lock);                          \
-       }                                                               \
-       return NOTIFY_OK;                                               \
- }                                                                     \
- static struct notifier_block name##_lg_cpu_notifier = {               \
-       .notifier_call = name##_lg_cpu_callback,                        \
- };                                                                    \
- void name##_lock_init(void) {                                         \
-       int i;                                                          \
-       LOCKDEP_INIT_MAP(&name##_lock_dep_map, #name, &name##_lock_key, 0); \
-       for_each_possible_cpu(i) {                                      \
-               arch_spinlock_t *lock;                                  \
-               lock = &per_cpu(name##_lock, i);                        \
-               *lock = (arch_spinlock_t)__ARCH_SPIN_LOCK_UNLOCKED;     \
-       }                                                               \
-       register_hotcpu_notifier(&name##_lg_cpu_notifier);              \
-       get_online_cpus();                                              \
-       for_each_online_cpu(i)                                          \
-               cpu_set(i, name##_cpus);                                \
-       put_online_cpus();                                              \
- }                                                                     \
- EXPORT_SYMBOL(name##_lock_init);                                      \
-                                                                       \
- void name##_local_lock(void) {                                                \
-       arch_spinlock_t *lock;                                          \
-       preempt_disable();                                              \
-       rwlock_acquire_read(&name##_lock_dep_map, 0, 0, _THIS_IP_);     \
-       lock = &__get_cpu_var(name##_lock);                             \
-       arch_spin_lock(lock);                                           \
- }                                                                     \
- EXPORT_SYMBOL(name##_local_lock);                                     \
-                                                                       \
- void name##_local_unlock(void) {                                      \
-       arch_spinlock_t *lock;                                          \
-       rwlock_release(&name##_lock_dep_map, 1, _THIS_IP_);             \
-       lock = &__get_cpu_var(name##_lock);                             \
-       arch_spin_unlock(lock);                                         \
-       preempt_enable();                                               \
- }                                                                     \
- EXPORT_SYMBOL(name##_local_unlock);                                   \
-                                                                       \
- void name##_local_lock_cpu(int cpu) {                                 \
-       arch_spinlock_t *lock;                                          \
-       preempt_disable();                                              \
-       rwlock_acquire_read(&name##_lock_dep_map, 0, 0, _THIS_IP_);     \
-       lock = &per_cpu(name##_lock, cpu);                              \
-       arch_spin_lock(lock);                                           \
- }                                                                     \
- EXPORT_SYMBOL(name##_local_lock_cpu);                                 \
-                                                                       \
- void name##_local_unlock_cpu(int cpu) {                               \
-       arch_spinlock_t *lock;                                          \
-       rwlock_release(&name##_lock_dep_map, 1, _THIS_IP_);             \
-       lock = &per_cpu(name##_lock, cpu);                              \
-       arch_spin_unlock(lock);                                         \
-       preempt_enable();                                               \
- }                                                                     \
- EXPORT_SYMBOL(name##_local_unlock_cpu);                               \
-                                                                       \
- void name##_global_lock_online(void) {                                        \
-       int i;                                                          \
-       spin_lock(&name##_cpu_lock);                                    \
-       rwlock_acquire(&name##_lock_dep_map, 0, 0, _RET_IP_);           \
-       for_each_cpu(i, &name##_cpus) {                                 \
-               arch_spinlock_t *lock;                                  \
-               lock = &per_cpu(name##_lock, i);                        \
-               arch_spin_lock(lock);                                   \
-       }                                                               \
- }                                                                     \
- EXPORT_SYMBOL(name##_global_lock_online);                             \
-                                                                       \
- void name##_global_unlock_online(void) {                              \
-       int i;                                                          \
-       rwlock_release(&name##_lock_dep_map, 1, _RET_IP_);              \
-       for_each_cpu(i, &name##_cpus) {                                 \
-               arch_spinlock_t *lock;                                  \
-               lock = &per_cpu(name##_lock, i);                        \
-               arch_spin_unlock(lock);                                 \
-       }                                                               \
-       spin_unlock(&name##_cpu_lock);                                  \
- }                                                                     \
- EXPORT_SYMBOL(name##_global_unlock_online);                           \
-                                                                       \
- void name##_global_lock(void) {                                       \
-       int i;                                                          \
-       preempt_disable();                                              \
-       rwlock_acquire(&name##_lock_dep_map, 0, 0, _RET_IP_);           \
-       for_each_possible_cpu(i) {                                      \
-               arch_spinlock_t *lock;                                  \
-               lock = &per_cpu(name##_lock, i);                        \
-               arch_spin_lock(lock);                                   \
-       }                                                               \
- }                                                                     \
- EXPORT_SYMBOL(name##_global_lock);                                    \
-                                                                       \
- void name##_global_unlock(void) {                                     \
-       int i;                                                          \
-       rwlock_release(&name##_lock_dep_map, 1, _RET_IP_);              \
-       for_each_possible_cpu(i) {                                      \
-               arch_spinlock_t *lock;                                  \
-               lock = &per_cpu(name##_lock, i);                        \
-               arch_spin_unlock(lock);                                 \
-       }                                                               \
-       preempt_enable();                                               \
- }                                                                     \
- EXPORT_SYMBOL(name##_global_unlock);
+       DEFINE_LGLOCK_LOCKDEP(name);                                    \
+       DEFINE_PER_CPU(arch_spinlock_t, name ## _lock)                  \
+       = __ARCH_SPIN_LOCK_UNLOCKED;                                    \
+       struct lglock name = { .lock = &name ## _lock }
+
+void lg_lock_init(struct lglock *lg, char *name);
+void lg_local_lock(struct lglock *lg);
+void lg_local_unlock(struct lglock *lg);
+void lg_local_lock_cpu(struct lglock *lg, int cpu);
+void lg_local_unlock_cpu(struct lglock *lg, int cpu);
+void lg_global_lock(struct lglock *lg);
+void lg_global_unlock(struct lglock *lg);
+
 #endif
index 11a966e5f829e9d9862589e393c1576780cfed48..4d24d64578c4c6f4baca418bad5b64ba5fc30555 100644 (file)
@@ -54,7 +54,7 @@ extern void   nlmclnt_done(struct nlm_host *host);
 
 extern int     nlmclnt_proc(struct nlm_host *host, int cmd,
                                        struct file_lock *fl);
-extern int     lockd_up(void);
-extern void    lockd_down(void);
+extern int     lockd_up(struct net *net);
+extern void    lockd_down(struct net *net);
 
 #endif /* LINUX_LOCKD_BIND_H */
index f94efd2f6c275b0ff14859e2451da53a6e3115a9..83e7ba90d6e5d5eb1b652ec1b7ce21dc47a4a9a9 100644 (file)
@@ -63,12 +63,7 @@ extern int mem_cgroup_cache_charge(struct page *page, struct mm_struct *mm,
                                        gfp_t gfp_mask);
 
 struct lruvec *mem_cgroup_zone_lruvec(struct zone *, struct mem_cgroup *);
-struct lruvec *mem_cgroup_lru_add_list(struct zone *, struct page *,
-                                      enum lru_list);
-void mem_cgroup_lru_del_list(struct page *, enum lru_list);
-void mem_cgroup_lru_del(struct page *);
-struct lruvec *mem_cgroup_lru_move_lists(struct zone *, struct page *,
-                                        enum lru_list, enum lru_list);
+struct lruvec *mem_cgroup_page_lruvec(struct page *, struct zone *);
 
 /* For coalescing uncharge for reducing memcg' overhead*/
 extern void mem_cgroup_uncharge_start(void);
@@ -79,6 +74,8 @@ extern void mem_cgroup_uncharge_cache_page(struct page *page);
 
 extern void mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask,
                                     int order);
+bool __mem_cgroup_same_or_subtree(const struct mem_cgroup *root_memcg,
+                                 struct mem_cgroup *memcg);
 int task_in_mem_cgroup(struct task_struct *task, const struct mem_cgroup *memcg);
 
 extern struct mem_cgroup *try_get_mem_cgroup_from_page(struct page *page);
@@ -92,10 +89,13 @@ static inline
 int mm_match_cgroup(const struct mm_struct *mm, const struct mem_cgroup *cgroup)
 {
        struct mem_cgroup *memcg;
+       int match;
+
        rcu_read_lock();
        memcg = mem_cgroup_from_task(rcu_dereference((mm)->owner));
+       match = __mem_cgroup_same_or_subtree(cgroup, memcg);
        rcu_read_unlock();
-       return cgroup == memcg;
+       return match;
 }
 
 extern struct cgroup_subsys_state *mem_cgroup_css(struct mem_cgroup *memcg);
@@ -114,17 +114,11 @@ void mem_cgroup_iter_break(struct mem_cgroup *, struct mem_cgroup *);
 /*
  * For memory reclaim.
  */
-int mem_cgroup_inactive_anon_is_low(struct mem_cgroup *memcg,
-                                   struct zone *zone);
-int mem_cgroup_inactive_file_is_low(struct mem_cgroup *memcg,
-                                   struct zone *zone);
+int mem_cgroup_inactive_anon_is_low(struct lruvec *lruvec);
+int mem_cgroup_inactive_file_is_low(struct lruvec *lruvec);
 int mem_cgroup_select_victim_node(struct mem_cgroup *memcg);
-unsigned long mem_cgroup_zone_nr_lru_pages(struct mem_cgroup *memcg,
-                                       int nid, int zid, unsigned int lrumask);
-struct zone_reclaim_stat *mem_cgroup_get_reclaim_stat(struct mem_cgroup *memcg,
-                                                     struct zone *zone);
-struct zone_reclaim_stat*
-mem_cgroup_get_reclaim_stat_from_page(struct page *page);
+unsigned long mem_cgroup_get_lru_size(struct lruvec *lruvec, enum lru_list);
+void mem_cgroup_update_lru_size(struct lruvec *, enum lru_list, int);
 extern void mem_cgroup_print_oom_info(struct mem_cgroup *memcg,
                                        struct task_struct *p);
 extern void mem_cgroup_replace_page_cache(struct page *oldpage,
@@ -251,25 +245,8 @@ static inline struct lruvec *mem_cgroup_zone_lruvec(struct zone *zone,
        return &zone->lruvec;
 }
 
-static inline struct lruvec *mem_cgroup_lru_add_list(struct zone *zone,
-                                                    struct page *page,
-                                                    enum lru_list lru)
-{
-       return &zone->lruvec;
-}
-
-static inline void mem_cgroup_lru_del_list(struct page *page, enum lru_list lru)
-{
-}
-
-static inline void mem_cgroup_lru_del(struct page *page)
-{
-}
-
-static inline struct lruvec *mem_cgroup_lru_move_lists(struct zone *zone,
-                                                      struct page *page,
-                                                      enum lru_list from,
-                                                      enum lru_list to)
+static inline struct lruvec *mem_cgroup_page_lruvec(struct page *page,
+                                                   struct zone *zone)
 {
        return &zone->lruvec;
 }
@@ -333,35 +310,27 @@ static inline bool mem_cgroup_disabled(void)
 }
 
 static inline int
-mem_cgroup_inactive_anon_is_low(struct mem_cgroup *memcg, struct zone *zone)
+mem_cgroup_inactive_anon_is_low(struct lruvec *lruvec)
 {
        return 1;
 }
 
 static inline int
-mem_cgroup_inactive_file_is_low(struct mem_cgroup *memcg, struct zone *zone)
+mem_cgroup_inactive_file_is_low(struct lruvec *lruvec)
 {
        return 1;
 }
 
 static inline unsigned long
-mem_cgroup_zone_nr_lru_pages(struct mem_cgroup *memcg, int nid, int zid,
-                               unsigned int lru_mask)
+mem_cgroup_get_lru_size(struct lruvec *lruvec, enum lru_list lru)
 {
        return 0;
 }
 
-
-static inline struct zone_reclaim_stat*
-mem_cgroup_get_reclaim_stat(struct mem_cgroup *memcg, struct zone *zone)
-{
-       return NULL;
-}
-
-static inline struct zone_reclaim_stat*
-mem_cgroup_get_reclaim_stat_from_page(struct page *page)
+static inline void
+mem_cgroup_update_lru_size(struct lruvec *lruvec, enum lru_list lru,
+                             int increment)
 {
-       return NULL;
 }
 
 static inline void
index 7c727a90d70da6229afeb9c9cf7c8601841863e2..4aa42732e47f34ca29392180fbee0fc73a086cd2 100644 (file)
@@ -225,8 +225,8 @@ static inline void check_highest_zone(enum zone_type k)
                policy_zone = k;
 }
 
-int do_migrate_pages(struct mm_struct *mm,
-       const nodemask_t *from_nodes, const nodemask_t *to_nodes, int flags);
+int do_migrate_pages(struct mm_struct *mm, const nodemask_t *from,
+                    const nodemask_t *to, int flags);
 
 
 #ifdef CONFIG_TMPFS
@@ -354,9 +354,8 @@ static inline bool mempolicy_nodemask_intersects(struct task_struct *tsk,
        return false;
 }
 
-static inline int do_migrate_pages(struct mm_struct *mm,
-                       const nodemask_t *from_nodes,
-                       const nodemask_t *to_nodes, int flags)
+static inline int do_migrate_pages(struct mm_struct *mm, const nodemask_t *from,
+                                  const nodemask_t *to, int flags)
 {
        return 0;
 }
index fccc3002f271de0e3d7e46cf7ce2525794176b2b..91dd3ef63e9925dc8c99ebe3141260c58882e7f9 100644 (file)
@@ -7,6 +7,7 @@
 #ifndef MFD_AB8500_H
 #define MFD_AB8500_H
 
+#include <linux/atomic.h>
 #include <linux/mutex.h>
 
 struct device;
@@ -194,6 +195,14 @@ enum ab8500_version {
 #define AB9540_INT_GPIO52F             123
 #define AB9540_INT_GPIO53F             124
 #define AB9540_INT_GPIO54F             125 /* not 8505 */
+/* ab8500_irq_regoffset[16] -> IT[Source|Latch|Mask]25 */
+#define AB8505_INT_KEYSTUCK            128
+#define AB8505_INT_IKR                 129
+#define AB8505_INT_IKP                 130
+#define AB8505_INT_KP                  131
+#define AB8505_INT_KEYDEGLITCH         132
+#define AB8505_INT_MODPWRSTATUSF       134
+#define AB8505_INT_MODPWRSTATUSR       135
 
 /*
  * AB8500_AB9540_NR_IRQS is used when configuring the IRQ numbers for the
@@ -203,8 +212,8 @@ enum ab8500_version {
  * which is larger.
  */
 #define AB8500_NR_IRQS                 112
-#define AB8505_NR_IRQS                 128
-#define AB9540_NR_IRQS                 128
+#define AB8505_NR_IRQS                 136
+#define AB9540_NR_IRQS                 136
 /* This is set to the roof of any AB8500 chip variant IRQ counts */
 #define AB8500_MAX_NR_IRQS             AB9540_NR_IRQS
 
@@ -216,6 +225,7 @@ enum ab8500_version {
  * @dev: parent device
  * @lock: read/write operations lock
  * @irq_lock: genirq bus lock
+ * @transfer_ongoing: 0 if no transfer ongoing
  * @irq: irq line
  * @version: chip version id (e.g. ab8500 or ab9540)
  * @chip_id: chip revision id
@@ -234,7 +244,7 @@ struct ab8500 {
        struct device   *dev;
        struct mutex    lock;
        struct mutex    irq_lock;
-
+       atomic_t        transfer_ongoing;
        int             irq_base;
        int             irq;
        enum ab8500_version version;
@@ -280,6 +290,8 @@ extern int __devinit ab8500_init(struct ab8500 *ab8500,
                                 enum ab8500_version version);
 extern int __devexit ab8500_exit(struct ab8500 *ab8500);
 
+extern int ab8500_suspend(struct ab8500 *ab8500);
+
 static inline int is_ab8500(struct ab8500 *ab)
 {
        return ab->version == AB8500_VERSION_AB8500;
index 22c1007d3ec56b970f51fcaf176288decf379548..7f92acf03d9e37ee420b9f211bfeb68f9bef6f65 100644 (file)
@@ -34,7 +34,7 @@ struct anatop {
        spinlock_t reglock;
 };
 
-extern u32 anatop_get_bits(struct anatop *, u32, int, int);
-extern void anatop_set_bits(struct anatop *, u32, int, int, u32);
+extern u32 anatop_read_reg(struct anatop *, u32);
+extern void anatop_write_reg(struct anatop *, u32, u32, u32);
 
 #endif /*  __LINUX_MFD_ANATOP_H */
index ef6faa5cee46d49a3df43f0567146e777dc8c07c..e1148d037e7b63a6c9ee9c0159671ad82abd38cc 100644 (file)
@@ -31,6 +31,8 @@ struct asic3_platform_data {
 
        unsigned int gpio_base;
 
+       unsigned int clock_rate;
+
        struct asic3_led *leds;
 };
 
index 8313cd9658e391487ee07d085ad585071184da63..0507c4c21a7d18859a78cfc089a1ad4b8b47a327 100644 (file)
 
 #include <linux/mfd/da9052/reg.h>
 
+/* Common - HWMON Channel Definations */
+#define DA9052_ADC_VDDOUT      0
+#define DA9052_ADC_ICH         1
+#define DA9052_ADC_TBAT        2
+#define DA9052_ADC_VBAT        3
+#define DA9052_ADC_IN4         4
+#define DA9052_ADC_IN5         5
+#define DA9052_ADC_IN6         6
+#define DA9052_ADC_TSI         7
+#define DA9052_ADC_TJUNC       8
+#define DA9052_ADC_VBBAT       9
+
 #define DA9052_IRQ_DCIN        0
 #define DA9052_IRQ_VBUS        1
 #define DA9052_IRQ_DCINREM     2
@@ -79,6 +91,9 @@ struct da9052 {
        struct device *dev;
        struct regmap *regmap;
 
+       struct mutex auxadc_lock;
+       struct completion done;
+
        int irq_base;
        struct regmap_irq_chip_data *irq_data;
        u8 chip_id;
@@ -86,6 +101,10 @@ struct da9052 {
        int chip_irq;
 };
 
+/* ADC API */
+int da9052_adc_manual_read(struct da9052 *da9052, unsigned char channel);
+int da9052_adc_read_temp(struct da9052 *da9052);
+
 /* Device I/O API */
 static inline int da9052_reg_read(struct da9052 *da9052, unsigned char reg)
 {
diff --git a/include/linux/mfd/lm3533.h b/include/linux/mfd/lm3533.h
new file mode 100644 (file)
index 0000000..594bc59
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * lm3533.h -- LM3533 interface
+ *
+ * Copyright (C) 2011-2012 Texas Instruments
+ *
+ * Author: Johan Hovold <jhovold@gmail.com>
+ *
+ * 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.
+ */
+
+#ifndef __LINUX_MFD_LM3533_H
+#define __LINUX_MFD_LM3533_H
+
+#define LM3533_ATTR_RO(_name) \
+       DEVICE_ATTR(_name, S_IRUGO, show_##_name, NULL)
+#define LM3533_ATTR_RW(_name) \
+       DEVICE_ATTR(_name, S_IRUGO | S_IWUSR , show_##_name, store_##_name)
+
+struct device;
+struct regmap;
+
+struct lm3533 {
+       struct device *dev;
+
+       struct regmap *regmap;
+
+       int gpio_hwen;
+       int irq;
+
+       unsigned have_als:1;
+       unsigned have_backlights:1;
+       unsigned have_leds:1;
+};
+
+struct lm3533_ctrlbank {
+       struct lm3533 *lm3533;
+       struct device *dev;
+       int id;
+};
+
+struct lm3533_als_platform_data {
+       unsigned pwm_mode:1;            /* PWM input mode (default analog) */
+       u8 r_select;                    /* 1 - 127 (ignored in PWM-mode) */
+};
+
+struct lm3533_bl_platform_data {
+       char *name;
+       u16 max_current;                /* 5000 - 29800 uA (800 uA step) */
+       u8 default_brightness;          /* 0 - 255 */
+       u8 pwm;                         /* 0 - 0x3f */
+};
+
+struct lm3533_led_platform_data {
+       char *name;
+       const char *default_trigger;
+       u16 max_current;                /* 5000 - 29800 uA (800 uA step) */
+       u8 pwm;                         /* 0 - 0x3f */
+};
+
+enum lm3533_boost_freq {
+       LM3533_BOOST_FREQ_500KHZ,
+       LM3533_BOOST_FREQ_1000KHZ,
+};
+
+enum lm3533_boost_ovp {
+       LM3533_BOOST_OVP_16V,
+       LM3533_BOOST_OVP_24V,
+       LM3533_BOOST_OVP_32V,
+       LM3533_BOOST_OVP_40V,
+};
+
+struct lm3533_platform_data {
+       int gpio_hwen;
+
+       enum lm3533_boost_ovp boost_ovp;
+       enum lm3533_boost_freq boost_freq;
+
+       struct lm3533_als_platform_data *als;
+
+       struct lm3533_bl_platform_data *backlights;
+       int num_backlights;
+
+       struct lm3533_led_platform_data *leds;
+       int num_leds;
+};
+
+extern int lm3533_ctrlbank_enable(struct lm3533_ctrlbank *cb);
+extern int lm3533_ctrlbank_disable(struct lm3533_ctrlbank *cb);
+
+extern int lm3533_ctrlbank_set_brightness(struct lm3533_ctrlbank *cb, u8 val);
+extern int lm3533_ctrlbank_get_brightness(struct lm3533_ctrlbank *cb, u8 *val);
+extern int lm3533_ctrlbank_set_max_current(struct lm3533_ctrlbank *cb,
+                                                               u16 imax);
+extern int lm3533_ctrlbank_set_pwm(struct lm3533_ctrlbank *cb, u8 val);
+extern int lm3533_ctrlbank_get_pwm(struct lm3533_ctrlbank *cb, u8 *val);
+
+extern int lm3533_read(struct lm3533 *lm3533, u8 reg, u8 *val);
+extern int lm3533_write(struct lm3533 *lm3533, u8 reg, u8 val);
+extern int lm3533_update(struct lm3533 *lm3533, u8 reg, u8 val, u8 mask);
+
+#endif /* __LINUX_MFD_LM3533_H */
diff --git a/include/linux/mfd/lpc_ich.h b/include/linux/mfd/lpc_ich.h
new file mode 100644 (file)
index 0000000..fec5256
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ *  linux/drivers/mfd/lpc_ich.h
+ *
+ *  Copyright (c) 2012 Extreme Engineering Solution, Inc.
+ *  Author: Aaron Sierra <asierra@xes-inc.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License 2 as published
+ *  by the Free Software Foundation.
+ *
+ *  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; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#ifndef LPC_ICH_H
+#define LPC_ICH_H
+
+/* Watchdog resources */
+#define ICH_RES_IO_TCO 0
+#define ICH_RES_IO_SMI 1
+#define ICH_RES_MEM_OFF        2
+#define ICH_RES_MEM_GCS        0
+
+/* GPIO resources */
+#define ICH_RES_GPIO   0
+#define ICH_RES_GPE0   1
+
+/* GPIO compatibility */
+#define ICH_I3100_GPIO         0x401
+#define ICH_V5_GPIO            0x501
+#define ICH_V6_GPIO            0x601
+#define ICH_V7_GPIO            0x701
+#define ICH_V9_GPIO            0x801
+#define ICH_V10CORP_GPIO       0xa01
+#define ICH_V10CONS_GPIO       0xa11
+
+struct lpc_ich_info {
+       char name[32];
+       unsigned int iTCO_version;
+       unsigned int gpio_version;
+};
+
+#endif
diff --git a/include/linux/mfd/max77693-private.h b/include/linux/mfd/max77693-private.h
new file mode 100644 (file)
index 0000000..68263c5
--- /dev/null
@@ -0,0 +1,227 @@
+/*
+ * max77693-private.h - Voltage regulator driver for the Maxim 77693
+ *
+ *  Copyright (C) 2012 Samsung Electrnoics
+ *  SangYoung Son <hello.son@samsung.com>
+ *
+ * This program is not provided / owned by Maxim Integrated Products.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef __LINUX_MFD_MAX77693_PRIV_H
+#define __LINUX_MFD_MAX77693_PRIV_H
+
+#include <linux/i2c.h>
+
+#define MAX77693_NUM_IRQ_MUIC_REGS     3
+#define MAX77693_REG_INVALID           (0xff)
+
+/* Slave addr = 0xCC: PMIC, Charger, Flash LED */
+enum max77693_pmic_reg {
+       MAX77693_LED_REG_IFLASH1                        = 0x00,
+       MAX77693_LED_REG_IFLASH2                        = 0x01,
+       MAX77693_LED_REG_ITORCH                         = 0x02,
+       MAX77693_LED_REG_ITORCHTIMER                    = 0x03,
+       MAX77693_LED_REG_FLASH_TIMER                    = 0x04,
+       MAX77693_LED_REG_FLASH_EN                       = 0x05,
+       MAX77693_LED_REG_MAX_FLASH1                     = 0x06,
+       MAX77693_LED_REG_MAX_FLASH2                     = 0x07,
+       MAX77693_LED_REG_MAX_FLASH3                     = 0x08,
+       MAX77693_LED_REG_MAX_FLASH4                     = 0x09,
+       MAX77693_LED_REG_VOUT_CNTL                      = 0x0A,
+       MAX77693_LED_REG_VOUT_FLASH1                    = 0x0B,
+       MAX77693_LED_REG_VOUT_FLASH2                    = 0x0C,
+       MAX77693_LED_REG_FLASH_INT                      = 0x0E,
+       MAX77693_LED_REG_FLASH_INT_MASK                 = 0x0F,
+       MAX77693_LED_REG_FLASH_INT_STATUS               = 0x10,
+
+       MAX77693_PMIC_REG_PMIC_ID1                      = 0x20,
+       MAX77693_PMIC_REG_PMIC_ID2                      = 0x21,
+       MAX77693_PMIC_REG_INTSRC                        = 0x22,
+       MAX77693_PMIC_REG_INTSRC_MASK                   = 0x23,
+       MAX77693_PMIC_REG_TOPSYS_INT                    = 0x24,
+       MAX77693_PMIC_REG_TOPSYS_INT_MASK               = 0x26,
+       MAX77693_PMIC_REG_TOPSYS_STAT                   = 0x28,
+       MAX77693_PMIC_REG_MAINCTRL1                     = 0x2A,
+       MAX77693_PMIC_REG_LSCNFG                        = 0x2B,
+
+       MAX77693_CHG_REG_CHG_INT                        = 0xB0,
+       MAX77693_CHG_REG_CHG_INT_MASK                   = 0xB1,
+       MAX77693_CHG_REG_CHG_INT_OK                     = 0xB2,
+       MAX77693_CHG_REG_CHG_DETAILS_00                 = 0xB3,
+       MAX77693_CHG_REG_CHG_DETAILS_01                 = 0xB4,
+       MAX77693_CHG_REG_CHG_DETAILS_02                 = 0xB5,
+       MAX77693_CHG_REG_CHG_DETAILS_03                 = 0xB6,
+       MAX77693_CHG_REG_CHG_CNFG_00                    = 0xB7,
+       MAX77693_CHG_REG_CHG_CNFG_01                    = 0xB8,
+       MAX77693_CHG_REG_CHG_CNFG_02                    = 0xB9,
+       MAX77693_CHG_REG_CHG_CNFG_03                    = 0xBA,
+       MAX77693_CHG_REG_CHG_CNFG_04                    = 0xBB,
+       MAX77693_CHG_REG_CHG_CNFG_05                    = 0xBC,
+       MAX77693_CHG_REG_CHG_CNFG_06                    = 0xBD,
+       MAX77693_CHG_REG_CHG_CNFG_07                    = 0xBE,
+       MAX77693_CHG_REG_CHG_CNFG_08                    = 0xBF,
+       MAX77693_CHG_REG_CHG_CNFG_09                    = 0xC0,
+       MAX77693_CHG_REG_CHG_CNFG_10                    = 0xC1,
+       MAX77693_CHG_REG_CHG_CNFG_11                    = 0xC2,
+       MAX77693_CHG_REG_CHG_CNFG_12                    = 0xC3,
+       MAX77693_CHG_REG_CHG_CNFG_13                    = 0xC4,
+       MAX77693_CHG_REG_CHG_CNFG_14                    = 0xC5,
+       MAX77693_CHG_REG_SAFEOUT_CTRL                   = 0xC6,
+
+       MAX77693_PMIC_REG_END,
+};
+
+/* Slave addr = 0x4A: MUIC */
+enum max77693_muic_reg {
+       MAX77693_MUIC_REG_ID            = 0x00,
+       MAX77693_MUIC_REG_INT1          = 0x01,
+       MAX77693_MUIC_REG_INT2          = 0x02,
+       MAX77693_MUIC_REG_INT3          = 0x03,
+       MAX77693_MUIC_REG_STATUS1       = 0x04,
+       MAX77693_MUIC_REG_STATUS2       = 0x05,
+       MAX77693_MUIC_REG_STATUS3       = 0x06,
+       MAX77693_MUIC_REG_INTMASK1      = 0x07,
+       MAX77693_MUIC_REG_INTMASK2      = 0x08,
+       MAX77693_MUIC_REG_INTMASK3      = 0x09,
+       MAX77693_MUIC_REG_CDETCTRL1     = 0x0A,
+       MAX77693_MUIC_REG_CDETCTRL2     = 0x0B,
+       MAX77693_MUIC_REG_CTRL1         = 0x0C,
+       MAX77693_MUIC_REG_CTRL2         = 0x0D,
+       MAX77693_MUIC_REG_CTRL3         = 0x0E,
+
+       MAX77693_MUIC_REG_END,
+};
+
+/* Slave addr = 0x90: Haptic */
+enum max77693_haptic_reg {
+       MAX77693_HAPTIC_REG_STATUS              = 0x00,
+       MAX77693_HAPTIC_REG_CONFIG1             = 0x01,
+       MAX77693_HAPTIC_REG_CONFIG2             = 0x02,
+       MAX77693_HAPTIC_REG_CONFIG_CHNL         = 0x03,
+       MAX77693_HAPTIC_REG_CONFG_CYC1          = 0x04,
+       MAX77693_HAPTIC_REG_CONFG_CYC2          = 0x05,
+       MAX77693_HAPTIC_REG_CONFIG_PER1         = 0x06,
+       MAX77693_HAPTIC_REG_CONFIG_PER2         = 0x07,
+       MAX77693_HAPTIC_REG_CONFIG_PER3         = 0x08,
+       MAX77693_HAPTIC_REG_CONFIG_PER4         = 0x09,
+       MAX77693_HAPTIC_REG_CONFIG_DUTY1        = 0x0A,
+       MAX77693_HAPTIC_REG_CONFIG_DUTY2        = 0x0B,
+       MAX77693_HAPTIC_REG_CONFIG_PWM1         = 0x0C,
+       MAX77693_HAPTIC_REG_CONFIG_PWM2         = 0x0D,
+       MAX77693_HAPTIC_REG_CONFIG_PWM3         = 0x0E,
+       MAX77693_HAPTIC_REG_CONFIG_PWM4         = 0x0F,
+       MAX77693_HAPTIC_REG_REV                 = 0x10,
+
+       MAX77693_HAPTIC_REG_END,
+};
+
+enum max77693_irq_source {
+       LED_INT = 0,
+       TOPSYS_INT,
+       CHG_INT,
+       MUIC_INT1,
+       MUIC_INT2,
+       MUIC_INT3,
+
+       MAX77693_IRQ_GROUP_NR,
+};
+
+enum max77693_irq {
+       /* PMIC - FLASH */
+       MAX77693_LED_IRQ_FLED2_OPEN,
+       MAX77693_LED_IRQ_FLED2_SHORT,
+       MAX77693_LED_IRQ_FLED1_OPEN,
+       MAX77693_LED_IRQ_FLED1_SHORT,
+       MAX77693_LED_IRQ_MAX_FLASH,
+
+       /* PMIC - TOPSYS */
+       MAX77693_TOPSYS_IRQ_T120C_INT,
+       MAX77693_TOPSYS_IRQ_T140C_INT,
+       MAX77693_TOPSYS_IRQ_LOWSYS_INT,
+
+       /* PMIC - Charger */
+       MAX77693_CHG_IRQ_BYP_I,
+       MAX77693_CHG_IRQ_THM_I,
+       MAX77693_CHG_IRQ_BAT_I,
+       MAX77693_CHG_IRQ_CHG_I,
+       MAX77693_CHG_IRQ_CHGIN_I,
+
+       /* MUIC INT1 */
+       MAX77693_MUIC_IRQ_INT1_ADC,
+       MAX77693_MUIC_IRQ_INT1_ADC_LOW,
+       MAX77693_MUIC_IRQ_INT1_ADC_ERR,
+       MAX77693_MUIC_IRQ_INT1_ADC1K,
+
+       /* MUIC INT2 */
+       MAX77693_MUIC_IRQ_INT2_CHGTYP,
+       MAX77693_MUIC_IRQ_INT2_CHGDETREUN,
+       MAX77693_MUIC_IRQ_INT2_DCDTMR,
+       MAX77693_MUIC_IRQ_INT2_DXOVP,
+       MAX77693_MUIC_IRQ_INT2_VBVOLT,
+       MAX77693_MUIC_IRQ_INT2_VIDRM,
+
+       /* MUIC INT3 */
+       MAX77693_MUIC_IRQ_INT3_EOC,
+       MAX77693_MUIC_IRQ_INT3_CGMBC,
+       MAX77693_MUIC_IRQ_INT3_OVP,
+       MAX77693_MUIC_IRQ_INT3_MBCCHG_ERR,
+       MAX77693_MUIC_IRQ_INT3_CHG_ENABLED,
+       MAX77693_MUIC_IRQ_INT3_BAT_DET,
+
+       MAX77693_IRQ_NR,
+};
+
+struct max77693_dev {
+       struct device *dev;
+       struct i2c_client *i2c;         /* 0xCC , PMIC, Charger, Flash LED */
+       struct i2c_client *muic;        /* 0x4A , MUIC */
+       struct i2c_client *haptic;      /* 0x90 , Haptic */
+       struct mutex iolock;
+
+       int type;
+
+       struct regmap *regmap;
+       struct regmap *regmap_muic;
+       struct regmap *regmap_haptic;
+
+       struct irq_domain *irq_domain;
+
+       int irq;
+       int irq_gpio;
+       bool wakeup;
+       struct mutex irqlock;
+       int irq_masks_cur[MAX77693_IRQ_GROUP_NR];
+       int irq_masks_cache[MAX77693_IRQ_GROUP_NR];
+};
+
+enum max77693_types {
+       TYPE_MAX77693,
+};
+
+extern int max77693_read_reg(struct regmap *map, u8 reg, u8 *dest);
+extern int max77693_bulk_read(struct regmap *map, u8 reg, int count,
+                               u8 *buf);
+extern int max77693_write_reg(struct regmap *map, u8 reg, u8 value);
+extern int max77693_bulk_write(struct regmap *map, u8 reg, int count,
+                               u8 *buf);
+extern int max77693_update_reg(struct regmap *map, u8 reg, u8 val, u8 mask);
+
+extern int max77693_irq_init(struct max77693_dev *max77686);
+extern void max77693_irq_exit(struct max77693_dev *max77686);
+extern int max77693_irq_resume(struct max77693_dev *max77686);
+
+#endif /*  __LINUX_MFD_MAX77693_PRIV_H */
diff --git a/include/linux/mfd/max77693.h b/include/linux/mfd/max77693.h
new file mode 100644 (file)
index 0000000..1d28ae9
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * max77693.h - Driver for the Maxim 77693
+ *
+ *  Copyright (C) 2012 Samsung Electrnoics
+ *  SangYoung Son <hello.son@samsung.com>
+ *
+ * This program is not provided / owned by Maxim Integrated Products.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * This driver is based on max8997.h
+ *
+ * MAX77693 has PMIC, Charger, Flash LED, Haptic, MUIC devices.
+ * The devices share the same I2C bus and included in
+ * this mfd driver.
+ */
+
+#ifndef __LINUX_MFD_MAX77693_H
+#define __LINUX_MFD_MAX77693_H
+
+struct max77693_platform_data {
+       int wakeup;
+};
+#endif /* __LINUX_MFD_MAX77693_H */
diff --git a/include/linux/mfd/sta2x11-mfd.h b/include/linux/mfd/sta2x11-mfd.h
new file mode 100644 (file)
index 0000000..d179227
--- /dev/null
@@ -0,0 +1,324 @@
+/*
+ * Copyright (c) 2009-2011 Wind River Systems, Inc.
+ * Copyright (c) 2011 ST Microelectronics (Alessandro Rubini)
+ *
+ * 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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * The STMicroelectronics ConneXt (STA2X11) chip has several unrelated
+ * functions in one PCI endpoint functions. This driver simply
+ * registers the platform devices in this iomemregion and exports a few
+ * functions to access common registers
+ */
+
+#ifndef __STA2X11_MFD_H
+#define __STA2X11_MFD_H
+#include <linux/types.h>
+#include <linux/pci.h>
+
+/*
+ * The MFD PCI block includes the GPIO peripherals and other register blocks.
+ * For GPIO, we have 32*4 bits (I use "gsta" for "gpio sta2x11".)
+ */
+#define GSTA_GPIO_PER_BLOCK    32
+#define GSTA_NR_BLOCKS         4
+#define GSTA_NR_GPIO           (GSTA_GPIO_PER_BLOCK * GSTA_NR_BLOCKS)
+
+/* Pinconfig is set by the board definition: altfunc, pull-up, pull-down */
+struct sta2x11_gpio_pdata {
+       unsigned pinconfig[GSTA_NR_GPIO];
+};
+
+/* Macros below lifted from sh_pfc.h, with minor differences */
+#define PINMUX_TYPE_NONE               0
+#define PINMUX_TYPE_FUNCTION           1
+#define PINMUX_TYPE_OUTPUT_LOW         2
+#define PINMUX_TYPE_OUTPUT_HIGH                3
+#define PINMUX_TYPE_INPUT              4
+#define PINMUX_TYPE_INPUT_PULLUP       5
+#define PINMUX_TYPE_INPUT_PULLDOWN     6
+
+/* Give names to GPIO pins, like PXA does, taken from the manual */
+#define STA2X11_GPIO0                  0
+#define STA2X11_GPIO1                  1
+#define STA2X11_GPIO2                  2
+#define STA2X11_GPIO3                  3
+#define STA2X11_GPIO4                  4
+#define STA2X11_GPIO5                  5
+#define STA2X11_GPIO6                  6
+#define STA2X11_GPIO7                  7
+#define STA2X11_GPIO8_RGBOUT_RED7      8
+#define STA2X11_GPIO9_RGBOUT_RED6      9
+#define STA2X11_GPIO10_RGBOUT_RED5     10
+#define STA2X11_GPIO11_RGBOUT_RED4     11
+#define STA2X11_GPIO12_RGBOUT_RED3     12
+#define STA2X11_GPIO13_RGBOUT_RED2     13
+#define STA2X11_GPIO14_RGBOUT_RED1     14
+#define STA2X11_GPIO15_RGBOUT_RED0     15
+#define STA2X11_GPIO16_RGBOUT_GREEN7   16
+#define STA2X11_GPIO17_RGBOUT_GREEN6   17
+#define STA2X11_GPIO18_RGBOUT_GREEN5   18
+#define STA2X11_GPIO19_RGBOUT_GREEN4   19
+#define STA2X11_GPIO20_RGBOUT_GREEN3   20
+#define STA2X11_GPIO21_RGBOUT_GREEN2   21
+#define STA2X11_GPIO22_RGBOUT_GREEN1   22
+#define STA2X11_GPIO23_RGBOUT_GREEN0   23
+#define STA2X11_GPIO24_RGBOUT_BLUE7    24
+#define STA2X11_GPIO25_RGBOUT_BLUE6    25
+#define STA2X11_GPIO26_RGBOUT_BLUE5    26
+#define STA2X11_GPIO27_RGBOUT_BLUE4    27
+#define STA2X11_GPIO28_RGBOUT_BLUE3    28
+#define STA2X11_GPIO29_RGBOUT_BLUE2    29
+#define STA2X11_GPIO30_RGBOUT_BLUE1    30
+#define STA2X11_GPIO31_RGBOUT_BLUE0    31
+#define STA2X11_GPIO32_RGBOUT_VSYNCH   32
+#define STA2X11_GPIO33_RGBOUT_HSYNCH   33
+#define STA2X11_GPIO34_RGBOUT_DEN      34
+#define STA2X11_GPIO35_ETH_CRS_DV      35
+#define STA2X11_GPIO36_ETH_TXD1                36
+#define STA2X11_GPIO37_ETH_TXD0                37
+#define STA2X11_GPIO38_ETH_TX_EN       38
+#define STA2X11_GPIO39_MDIO            39
+#define STA2X11_GPIO40_ETH_REF_CLK     40
+#define STA2X11_GPIO41_ETH_RXD1                41
+#define STA2X11_GPIO42_ETH_RXD0                42
+#define STA2X11_GPIO43_MDC             43
+#define STA2X11_GPIO44_CAN_TX          44
+#define STA2X11_GPIO45_CAN_RX          45
+#define STA2X11_GPIO46_MLB_DAT         46
+#define STA2X11_GPIO47_MLB_SIG         47
+#define STA2X11_GPIO48_SPI0_CLK                48
+#define STA2X11_GPIO49_SPI0_TXD                49
+#define STA2X11_GPIO50_SPI0_RXD                50
+#define STA2X11_GPIO51_SPI0_FRM                51
+#define STA2X11_GPIO52_SPI1_CLK                52
+#define STA2X11_GPIO53_SPI1_TXD                53
+#define STA2X11_GPIO54_SPI1_RXD                54
+#define STA2X11_GPIO55_SPI1_FRM                55
+#define STA2X11_GPIO56_SPI2_CLK                56
+#define STA2X11_GPIO57_SPI2_TXD                57
+#define STA2X11_GPIO58_SPI2_RXD                58
+#define STA2X11_GPIO59_SPI2_FRM                59
+#define STA2X11_GPIO60_I2C0_SCL                60
+#define STA2X11_GPIO61_I2C0_SDA                61
+#define STA2X11_GPIO62_I2C1_SCL                62
+#define STA2X11_GPIO63_I2C1_SDA                63
+#define STA2X11_GPIO64_I2C2_SCL                64
+#define STA2X11_GPIO65_I2C2_SDA                65
+#define STA2X11_GPIO66_I2C3_SCL                66
+#define STA2X11_GPIO67_I2C3_SDA                67
+#define STA2X11_GPIO68_MSP0_RCK                68
+#define STA2X11_GPIO69_MSP0_RXD                69
+#define STA2X11_GPIO70_MSP0_RFS                70
+#define STA2X11_GPIO71_MSP0_TCK                71
+#define STA2X11_GPIO72_MSP0_TXD                72
+#define STA2X11_GPIO73_MSP0_TFS                73
+#define STA2X11_GPIO74_MSP0_SCK                74
+#define STA2X11_GPIO75_MSP1_CK         75
+#define STA2X11_GPIO76_MSP1_RXD                76
+#define STA2X11_GPIO77_MSP1_FS         77
+#define STA2X11_GPIO78_MSP1_TXD                78
+#define STA2X11_GPIO79_MSP2_CK         79
+#define STA2X11_GPIO80_MSP2_RXD                80
+#define STA2X11_GPIO81_MSP2_FS         81
+#define STA2X11_GPIO82_MSP2_TXD                82
+#define STA2X11_GPIO83_MSP3_CK         83
+#define STA2X11_GPIO84_MSP3_RXD                84
+#define STA2X11_GPIO85_MSP3_FS         85
+#define STA2X11_GPIO86_MSP3_TXD                86
+#define STA2X11_GPIO87_MSP4_CK         87
+#define STA2X11_GPIO88_MSP4_RXD                88
+#define STA2X11_GPIO89_MSP4_FS         89
+#define STA2X11_GPIO90_MSP4_TXD                90
+#define STA2X11_GPIO91_MSP5_CK         91
+#define STA2X11_GPIO92_MSP5_RXD                92
+#define STA2X11_GPIO93_MSP5_FS         93
+#define STA2X11_GPIO94_MSP5_TXD                94
+#define STA2X11_GPIO95_SDIO3_DAT3      95
+#define STA2X11_GPIO96_SDIO3_DAT2      96
+#define STA2X11_GPIO97_SDIO3_DAT1      97
+#define STA2X11_GPIO98_SDIO3_DAT0      98
+#define STA2X11_GPIO99_SDIO3_CLK       99
+#define STA2X11_GPIO100_SDIO3_CMD      100
+#define STA2X11_GPIO101                        101
+#define STA2X11_GPIO102                        102
+#define STA2X11_GPIO103                        103
+#define STA2X11_GPIO104                        104
+#define STA2X11_GPIO105_SDIO2_DAT3     105
+#define STA2X11_GPIO106_SDIO2_DAT2     106
+#define STA2X11_GPIO107_SDIO2_DAT1     107
+#define STA2X11_GPIO108_SDIO2_DAT0     108
+#define STA2X11_GPIO109_SDIO2_CLK      109
+#define STA2X11_GPIO110_SDIO2_CMD      110
+#define STA2X11_GPIO111                        111
+#define STA2X11_GPIO112                        112
+#define STA2X11_GPIO113                        113
+#define STA2X11_GPIO114                        114
+#define STA2X11_GPIO115_SDIO1_DAT3     115
+#define STA2X11_GPIO116_SDIO1_DAT2     116
+#define STA2X11_GPIO117_SDIO1_DAT1     117
+#define STA2X11_GPIO118_SDIO1_DAT0     118
+#define STA2X11_GPIO119_SDIO1_CLK      119
+#define STA2X11_GPIO120_SDIO1_CMD      120
+#define STA2X11_GPIO121                        121
+#define STA2X11_GPIO122                        122
+#define STA2X11_GPIO123                        123
+#define STA2X11_GPIO124                        124
+#define STA2X11_GPIO125_UART2_TXD      125
+#define STA2X11_GPIO126_UART2_RXD      126
+#define STA2X11_GPIO127_UART3_TXD      127
+
+/*
+ * The APB bridge has its own registers, needed by our users as well.
+ * They are accessed with the following read/mask/write function.
+ */
+u32 sta2x11_apbreg_mask(struct pci_dev *pdev, u32 reg, u32 mask, u32 val);
+
+/* CAN and MLB */
+#define APBREG_BSR     0x00    /* Bridge Status Reg */
+#define APBREG_PAER    0x08    /* Peripherals Address Error Reg */
+#define APBREG_PWAC    0x20    /* Peripheral Write Access Control reg */
+#define APBREG_PRAC    0x40    /* Peripheral Read Access Control reg */
+#define APBREG_PCG     0x60    /* Peripheral Clock Gating Reg */
+#define APBREG_PUR     0x80    /* Peripheral Under Reset Reg */
+#define APBREG_EMU_PCG 0xA0    /* Emulator Peripheral Clock Gating Reg */
+
+#define APBREG_CAN     (1 << 1)
+#define APBREG_MLB     (1 << 3)
+
+/* SARAC */
+#define APBREG_BSR_SARAC     0x100 /* Bridge Status Reg */
+#define APBREG_PAER_SARAC    0x108 /* Peripherals Address Error Reg */
+#define APBREG_PWAC_SARAC    0x120 /* Peripheral Write Access Control reg */
+#define APBREG_PRAC_SARAC    0x140 /* Peripheral Read Access Control reg */
+#define APBREG_PCG_SARAC     0x160 /* Peripheral Clock Gating Reg */
+#define APBREG_PUR_SARAC     0x180 /* Peripheral Under Reset Reg */
+#define APBREG_EMU_PCG_SARAC 0x1A0 /* Emulator Peripheral Clock Gating Reg */
+
+#define APBREG_SARAC   (1 << 2)
+
+/*
+ * The system controller has its own registers. Some of these are accessed
+ * by out users as well, using the following read/mask/write/function
+ */
+u32 sta2x11_sctl_mask(struct pci_dev *pdev, u32 reg, u32 mask, u32 val);
+
+#define SCTL_SCCTL             0x00    /* System controller control register */
+#define SCTL_ARMCFG            0x04    /* ARM configuration register */
+#define SCTL_SCPLLCTL          0x08    /* PLL control status register */
+#define SCTL_SCPLLFCTRL                0x0c    /* PLL frequency control register */
+#define SCTL_SCRESFRACT                0x10    /* PLL fractional input register */
+#define SCTL_SCRESCTRL1                0x14    /* Peripheral reset control 1 */
+#define SCTL_SCRESXTRL2                0x18    /* Peripheral reset control 2 */
+#define SCTL_SCPEREN0          0x1c    /* Peripheral clock enable register 0 */
+#define SCTL_SCPEREN1          0x20    /* Peripheral clock enable register 1 */
+#define SCTL_SCPEREN2          0x24    /* Peripheral clock enable register 2 */
+#define SCTL_SCGRST            0x28    /* Peripheral global reset */
+#define SCTL_SCPCIPMCR1                0x30    /* PCI power management control 1 */
+#define SCTL_SCPCIPMCR2                0x34    /* PCI power management control 2 */
+#define SCTL_SCPCIPMSR1                0x38    /* PCI power management status 1 */
+#define SCTL_SCPCIPMSR2                0x3c    /* PCI power management status 2 */
+#define SCTL_SCPCIPMSR3                0x40    /* PCI power management status 3 */
+#define SCTL_SCINTREN          0x44    /* Interrupt enable */
+#define SCTL_SCRISR            0x48    /* RAW interrupt status */
+#define SCTL_SCCLKSTAT0                0x4c    /* Peripheral clocks status 0 */
+#define SCTL_SCCLKSTAT1                0x50    /* Peripheral clocks status 1 */
+#define SCTL_SCCLKSTAT2                0x54    /* Peripheral clocks status 2 */
+#define SCTL_SCRSTSTA          0x58    /* Reset status register */
+
+#define SCTL_SCRESCTRL1_USB_PHY_POR    (1 << 0)
+#define SCTL_SCRESCTRL1_USB_OTG        (1 << 1)
+#define SCTL_SCRESCTRL1_USB_HRST       (1 << 2)
+#define SCTL_SCRESCTRL1_USB_PHY_HOST   (1 << 3)
+#define SCTL_SCRESCTRL1_SATAII (1 << 4)
+#define SCTL_SCRESCTRL1_VIP            (1 << 5)
+#define SCTL_SCRESCTRL1_PER_MMC0       (1 << 6)
+#define SCTL_SCRESCTRL1_PER_MMC1       (1 << 7)
+#define SCTL_SCRESCTRL1_PER_GPIO0      (1 << 8)
+#define SCTL_SCRESCTRL1_PER_GPIO1      (1 << 9)
+#define SCTL_SCRESCTRL1_PER_GPIO2      (1 << 10)
+#define SCTL_SCRESCTRL1_PER_GPIO3      (1 << 11)
+#define SCTL_SCRESCTRL1_PER_MTU0       (1 << 12)
+#define SCTL_SCRESCTRL1_KER_SPI0       (1 << 13)
+#define SCTL_SCRESCTRL1_KER_SPI1       (1 << 14)
+#define SCTL_SCRESCTRL1_KER_SPI2       (1 << 15)
+#define SCTL_SCRESCTRL1_KER_MCI0       (1 << 16)
+#define SCTL_SCRESCTRL1_KER_MCI1       (1 << 17)
+#define SCTL_SCRESCTRL1_PRE_HSI2C0     (1 << 18)
+#define SCTL_SCRESCTRL1_PER_HSI2C1     (1 << 19)
+#define SCTL_SCRESCTRL1_PER_HSI2C2     (1 << 20)
+#define SCTL_SCRESCTRL1_PER_HSI2C3     (1 << 21)
+#define SCTL_SCRESCTRL1_PER_MSP0       (1 << 22)
+#define SCTL_SCRESCTRL1_PER_MSP1       (1 << 23)
+#define SCTL_SCRESCTRL1_PER_MSP2       (1 << 24)
+#define SCTL_SCRESCTRL1_PER_MSP3       (1 << 25)
+#define SCTL_SCRESCTRL1_PER_MSP4       (1 << 26)
+#define SCTL_SCRESCTRL1_PER_MSP5       (1 << 27)
+#define SCTL_SCRESCTRL1_PER_MMC        (1 << 28)
+#define SCTL_SCRESCTRL1_KER_MSP0       (1 << 29)
+#define SCTL_SCRESCTRL1_KER_MSP1       (1 << 30)
+#define SCTL_SCRESCTRL1_KER_MSP2       (1 << 31)
+
+#define SCTL_SCPEREN0_UART0            (1 << 0)
+#define SCTL_SCPEREN0_UART1            (1 << 1)
+#define SCTL_SCPEREN0_UART2            (1 << 2)
+#define SCTL_SCPEREN0_UART3            (1 << 3)
+#define SCTL_SCPEREN0_MSP0             (1 << 4)
+#define SCTL_SCPEREN0_MSP1             (1 << 5)
+#define SCTL_SCPEREN0_MSP2             (1 << 6)
+#define SCTL_SCPEREN0_MSP3             (1 << 7)
+#define SCTL_SCPEREN0_MSP4             (1 << 8)
+#define SCTL_SCPEREN0_MSP5             (1 << 9)
+#define SCTL_SCPEREN0_SPI0             (1 << 10)
+#define SCTL_SCPEREN0_SPI1             (1 << 11)
+#define SCTL_SCPEREN0_SPI2             (1 << 12)
+#define SCTL_SCPEREN0_I2C0             (1 << 13)
+#define SCTL_SCPEREN0_I2C1             (1 << 14)
+#define SCTL_SCPEREN0_I2C2             (1 << 15)
+#define SCTL_SCPEREN0_I2C3             (1 << 16)
+#define SCTL_SCPEREN0_SVDO_LVDS                (1 << 17)
+#define SCTL_SCPEREN0_USB_HOST         (1 << 18)
+#define SCTL_SCPEREN0_USB_OTG          (1 << 19)
+#define SCTL_SCPEREN0_MCI0             (1 << 20)
+#define SCTL_SCPEREN0_MCI1             (1 << 21)
+#define SCTL_SCPEREN0_MCI2             (1 << 22)
+#define SCTL_SCPEREN0_MCI3             (1 << 23)
+#define SCTL_SCPEREN0_SATA             (1 << 24)
+#define SCTL_SCPEREN0_ETHERNET         (1 << 25)
+#define SCTL_SCPEREN0_VIC              (1 << 26)
+#define SCTL_SCPEREN0_DMA_AUDIO                (1 << 27)
+#define SCTL_SCPEREN0_DMA_SOC          (1 << 28)
+#define SCTL_SCPEREN0_RAM              (1 << 29)
+#define SCTL_SCPEREN0_VIP              (1 << 30)
+#define SCTL_SCPEREN0_ARM              (1 << 31)
+
+#define SCTL_SCPEREN1_UART0            (1 << 0)
+#define SCTL_SCPEREN1_UART1            (1 << 1)
+#define SCTL_SCPEREN1_UART2            (1 << 2)
+#define SCTL_SCPEREN1_UART3            (1 << 3)
+#define SCTL_SCPEREN1_MSP0             (1 << 4)
+#define SCTL_SCPEREN1_MSP1             (1 << 5)
+#define SCTL_SCPEREN1_MSP2             (1 << 6)
+#define SCTL_SCPEREN1_MSP3             (1 << 7)
+#define SCTL_SCPEREN1_MSP4             (1 << 8)
+#define SCTL_SCPEREN1_MSP5             (1 << 9)
+#define SCTL_SCPEREN1_SPI0             (1 << 10)
+#define SCTL_SCPEREN1_SPI1             (1 << 11)
+#define SCTL_SCPEREN1_SPI2             (1 << 12)
+#define SCTL_SCPEREN1_I2C0             (1 << 13)
+#define SCTL_SCPEREN1_I2C1             (1 << 14)
+#define SCTL_SCPEREN1_I2C2             (1 << 15)
+#define SCTL_SCPEREN1_I2C3             (1 << 16)
+#define SCTL_SCPEREN1_USB_PHY          (1 << 17)
+
+#endif /* __STA2X11_MFD_H */
index 8516fd1eaabc7882127d269bf4db58366e93bb57..f8d5b4d5843fc02ad7728c207992d0b33ac0c5a7 100644 (file)
@@ -117,7 +117,7 @@ struct matrix_keymap_data;
  * @no_autorepeat: disable key autorepeat
  */
 struct stmpe_keypad_platform_data {
-       struct matrix_keymap_data *keymap_data;
+       const struct matrix_keymap_data *keymap_data;
        unsigned int debounce_ms;
        unsigned int scan_count;
        bool no_autorepeat;
index 1c6c2860d1a60a6350ff46e7d45321ed8be95a80..dd8dc0a6c46243141ea59e325cde465fb993bce5 100644 (file)
@@ -18,6 +18,7 @@
 #define __LINUX_MFD_TPS65910_H
 
 #include <linux/gpio.h>
+#include <linux/regmap.h>
 
 /* TPS chip id list */
 #define TPS65910                       0
 #define TPS65910_SLEEP_CONTROL_EXT_INPUT_EN3           0x4
 #define TPS65911_SLEEP_CONTROL_EXT_INPUT_SLEEP         0x8
 
+/*
+ * Sleep keepon data: Maintains the state in sleep mode
+ * @therm_keepon: Keep on the thermal monitoring in sleep state.
+ * @clkout32k_keepon: Keep on the 32KHz clock output in sleep state.
+ * @i2chs_keepon: Keep on high speed internal clock in sleep state.
+ */
+struct tps65910_sleep_keepon_data {
+       unsigned therm_keepon:1;
+       unsigned clkout32k_keepon:1;
+       unsigned i2chs_keepon:1;
+};
+
 /**
  * struct tps65910_board
  * Board platform data may be used to initialize regulators.
@@ -794,6 +807,8 @@ struct tps65910_board {
        int irq_base;
        int vmbch_threshold;
        int vmbch2_threshold;
+       bool en_dev_slp;
+       struct tps65910_sleep_keepon_data *slp_keepon;
        bool en_gpio_sleep[TPS6591X_MAX_NUM_GPIO];
        unsigned long regulator_ext_sleep_control[TPS65910_NUM_REGS];
        struct regulator_init_data *tps65910_pmic_init_data[TPS65910_NUM_REGS];
@@ -809,16 +824,14 @@ struct tps65910 {
        struct regmap *regmap;
        struct mutex io_mutex;
        unsigned int id;
-       int (*read)(struct tps65910 *tps65910, u8 reg, int size, void *dest);
-       int (*write)(struct tps65910 *tps65910, u8 reg, int size, void *src);
 
        /* Client devices */
        struct tps65910_pmic *pmic;
        struct tps65910_rtc *rtc;
        struct tps65910_power *power;
 
-       /* GPIO Handling */
-       struct gpio_chip gpio;
+       /* Device node parsed board data */
+       struct tps65910_board *of_plat_data;
 
        /* IRQ Handling */
        struct mutex irq_lock;
@@ -826,6 +839,7 @@ struct tps65910 {
        int irq_base;
        int irq_num;
        u32 irq_mask;
+       struct irq_domain *domain;
 };
 
 struct tps65910_platform_data {
@@ -833,9 +847,6 @@ struct tps65910_platform_data {
        int irq_base;
 };
 
-int tps65910_set_bits(struct tps65910 *tps65910, u8 reg, u8 mask);
-int tps65910_clear_bits(struct tps65910 *tps65910, u8 reg, u8 mask);
-void tps65910_gpio_init(struct tps65910 *tps65910, int gpio_base);
 int tps65910_irq_init(struct tps65910 *tps65910, int irq,
                struct tps65910_platform_data *pdata);
 int tps65910_irq_exit(struct tps65910 *tps65910);
@@ -845,4 +856,28 @@ static inline int tps65910_chip_id(struct tps65910 *tps65910)
        return tps65910->id;
 }
 
+static inline int tps65910_reg_read(struct tps65910 *tps65910, u8 reg,
+               unsigned int *val)
+{
+       return regmap_read(tps65910->regmap, reg, val);
+}
+
+static inline int tps65910_reg_write(struct tps65910 *tps65910, u8 reg,
+               unsigned int val)
+{
+       return regmap_write(tps65910->regmap, reg, val);
+}
+
+static inline int tps65910_reg_set_bits(struct tps65910 *tps65910, u8 reg,
+               u8 mask)
+{
+       return regmap_update_bits(tps65910->regmap, reg, mask, mask);
+}
+
+static inline int tps65910_reg_clear_bits(struct tps65910 *tps65910, u8 reg,
+               u8 mask)
+{
+       return regmap_update_bits(tps65910->regmap, reg, mask, 0);
+}
+
 #endif /*  __LINUX_MFD_TPS65910_H */
index b15b5f03f5c44c74473f30e46411876000bf5863..6659487c31e7a010c96bcef991f05b546fa103d1 100644 (file)
@@ -27,6 +27,7 @@
 
 #include <linux/interrupt.h>
 #include <linux/mfd/core.h>
+#include <linux/regulator/consumer.h>
 
 #define TWL6040_REG_ASICID             0x01
 #define TWL6040_REG_ASICREV            0x02
@@ -203,6 +204,7 @@ struct regmap;
 struct twl6040 {
        struct device *dev;
        struct regmap *regmap;
+       struct regulator_bulk_data supplies[2]; /* supplies for vio, v2v1 */
        struct mutex mutex;
        struct mutex io_mutex;
        struct mutex irq_mutex;
index 4b1211859f74a9dad29a555c758b868e29a392d1..4a3b83a776148eb2ace611436b38ab42e931450e 100644 (file)
@@ -17,6 +17,7 @@
 
 #include <linux/completion.h>
 #include <linux/interrupt.h>
+#include <linux/irqdomain.h>
 #include <linux/list.h>
 #include <linux/regmap.h>
 
 #define WM831X_FLL_CLK_SRC_WIDTH                     2  /* FLL_CLK_SRC - [1:0] */
 
 struct regulator_dev;
+struct irq_domain;
 
 #define WM831X_NUM_IRQ_REGS 5
 #define WM831X_NUM_GPIO_REGS 16
@@ -367,7 +369,7 @@ struct wm831x {
 
        int irq;  /* Our chip IRQ */
        struct mutex irq_lock;
-       int irq_base;
+       struct irq_domain *irq_domain;
        int irq_masks_cur[WM831X_NUM_IRQ_REGS];   /* Currently active value */
        int irq_masks_cache[WM831X_NUM_IRQ_REGS]; /* Cached hardware value */
 
@@ -382,7 +384,8 @@ struct wm831x {
 
        /* Used by the interrupt controller code to post writes */
        int gpio_update[WM831X_NUM_GPIO_REGS];
-       bool gpio_level[WM831X_NUM_GPIO_REGS];
+       bool gpio_level_high[WM831X_NUM_GPIO_REGS];
+       bool gpio_level_low[WM831X_NUM_GPIO_REGS];
 
        struct mutex auxadc_lock;
        struct list_head auxadc_pending;
@@ -417,6 +420,11 @@ int wm831x_irq_init(struct wm831x *wm831x, int irq);
 void wm831x_irq_exit(struct wm831x *wm831x);
 void wm831x_auxadc_init(struct wm831x *wm831x);
 
+static inline int wm831x_irq(struct wm831x *wm831x, int irq)
+{
+       return irq_create_mapping(wm831x->irq_domain, irq);
+}
+
 extern struct regmap_config wm831x_regmap_config;
 
 #endif
index 98fcc977e82be5acd0805d3474ef3e12f4b9fb3f..9192b6404a7347d5b6e665925a421cf1024d3c24 100644 (file)
@@ -602,6 +602,7 @@ extern const u16 wm8352_mode2_defaults[];
 extern const u16 wm8352_mode3_defaults[];
 
 struct wm8350;
+struct regmap;
 
 struct wm8350_hwmon {
        struct platform_device *pdev;
@@ -612,13 +613,7 @@ struct wm8350 {
        struct device *dev;
 
        /* device IO */
-       union {
-               struct i2c_client *i2c_client;
-               struct spi_device *spi_device;
-       };
-       int (*read_dev)(struct wm8350 *wm8350, char reg, int size, void *dest);
-       int (*write_dev)(struct wm8350 *wm8350, char reg, int size,
-                        void *src);
+       struct regmap *regmap;
        u16 *reg_cache;
 
        struct mutex auxadc_mutex;
index 0147b696851072fde8a6074cb762257aa1654aad..2de565b94d0c39e9c0e3b049bbcf0237cceddb8f 100644 (file)
 #include <linux/mfd/wm8400.h>
 #include <linux/mutex.h>
 #include <linux/platform_device.h>
-
-struct regmap;
+#include <linux/regmap.h>
 
 #define WM8400_REGISTER_COUNT 0x55
 
 struct wm8400 {
        struct device *dev;
-
-       struct mutex io_lock;
        struct regmap *regmap;
 
-       u16 reg_cache[WM8400_REGISTER_COUNT];
-
        struct platform_device regulators[6];
 };
 
@@ -930,6 +925,11 @@ struct wm8400 {
 
 u16 wm8400_reg_read(struct wm8400 *wm8400, u8 reg);
 int wm8400_block_read(struct wm8400 *wm8400, u8 reg, int count, u16 *data);
-int wm8400_set_bits(struct wm8400 *wm8400, u8 reg, u16 mask, u16 val);
+
+static inline int wm8400_set_bits(struct wm8400 *wm8400, u8 reg,
+                                 u16 mask, u16 val)
+{
+       return regmap_update_bits(wm8400->regmap, reg, mask, val);
+}
 
 #endif
index 6695c3ec4518ccfe73ebe1c307a24721d7fef943..1f173306bf0508ddeb3a18de8175fac44c857ee3 100644 (file)
@@ -57,6 +57,7 @@ struct wm8994 {
 
        enum wm8994_type type;
        int revision;
+       int cust_id;
 
        struct device *dev;
        struct regmap *regmap;
index 86e6a032a07833f3dbee4f8d399a6e63e61ef93a..053548961c15df6c81b1e6128e056f2806806cbc 100644 (file)
 /*
  * R256 (0x100) - Chip Revision
  */
+#define WM8994_CUST_ID_MASK                     0xFF00  /* CUST_ID - [15:8] */
+#define WM8994_CUST_ID_SHIFT                         8  /* CUST_ID - [15:8] */
+#define WM8994_CUST_ID_WIDTH                         8  /* CUST_ID - [15:8] */
 #define WM8994_CHIP_REV_MASK                    0x000F  /* CHIP_REV - [3:0] */
 #define WM8994_CHIP_REV_SHIFT                        0  /* CHIP_REV - [3:0] */
 #define WM8994_CHIP_REV_WIDTH                        4  /* CHIP_REV - [3:0] */
index 7d5c37f24c63af1915b2eb32056f55d63fb04aa6..b36d08ce5c578dcd18e224828217ded481de54ee 100644 (file)
@@ -321,6 +321,7 @@ static inline int is_vmalloc_or_module_addr(const void *x)
 static inline void compound_lock(struct page *page)
 {
 #ifdef CONFIG_TRANSPARENT_HUGEPAGE
+       VM_BUG_ON(PageSlab(page));
        bit_spin_lock(PG_compound_lock, &page->flags);
 #endif
 }
@@ -328,6 +329,7 @@ static inline void compound_lock(struct page *page)
 static inline void compound_unlock(struct page *page)
 {
 #ifdef CONFIG_TRANSPARENT_HUGEPAGE
+       VM_BUG_ON(PageSlab(page));
        bit_spin_unlock(PG_compound_lock, &page->flags);
 #endif
 }
@@ -871,8 +873,6 @@ extern void pagefault_out_of_memory(void);
 extern void show_free_areas(unsigned int flags);
 extern bool skip_free_areas_node(unsigned int flags, int nid);
 
-int shmem_lock(struct file *file, int lock, struct user_struct *user);
-struct file *shmem_file_setup(const char *name, loff_t size, unsigned long flags);
 int shmem_zero_setup(struct vm_area_struct *);
 
 extern int can_do_mlock(void);
@@ -951,11 +951,9 @@ static inline void unmap_shared_mapping_range(struct address_space *mapping,
 extern void truncate_pagecache(struct inode *inode, loff_t old, loff_t new);
 extern void truncate_setsize(struct inode *inode, loff_t newsize);
 extern int vmtruncate(struct inode *inode, loff_t offset);
-extern int vmtruncate_range(struct inode *inode, loff_t offset, loff_t end);
 void truncate_pagecache_range(struct inode *inode, loff_t offset, loff_t end);
 int truncate_inode_page(struct address_space *mapping, struct page *page);
 int generic_error_remove_page(struct address_space *mapping, struct page *page);
-
 int invalidate_inode_page(struct page *page);
 
 #ifdef CONFIG_MMU
@@ -1394,7 +1392,7 @@ extern unsigned long get_unmapped_area(struct file *, unsigned long, unsigned lo
 extern unsigned long mmap_region(struct file *file, unsigned long addr,
        unsigned long len, unsigned long flags,
        vm_flags_t vm_flags, unsigned long pgoff);
-extern unsigned long do_mmap(struct file *, unsigned long,
+extern unsigned long do_mmap_pgoff(struct file *, unsigned long,
         unsigned long, unsigned long,
         unsigned long, unsigned long);
 extern int do_munmap(struct mm_struct *, unsigned long, size_t);
index 227fd3e9a9c9370398478005d01344f5a42cb02d..1397ccf81e91f16d937c3f0b2e7d361104dce5f1 100644 (file)
@@ -21,22 +21,22 @@ static inline int page_is_file_cache(struct page *page)
        return !PageSwapBacked(page);
 }
 
-static inline void
-add_page_to_lru_list(struct zone *zone, struct page *page, enum lru_list lru)
+static __always_inline void add_page_to_lru_list(struct page *page,
+                               struct lruvec *lruvec, enum lru_list lru)
 {
-       struct lruvec *lruvec;
-
-       lruvec = mem_cgroup_lru_add_list(zone, page, lru);
+       int nr_pages = hpage_nr_pages(page);
+       mem_cgroup_update_lru_size(lruvec, lru, nr_pages);
        list_add(&page->lru, &lruvec->lists[lru]);
-       __mod_zone_page_state(zone, NR_LRU_BASE + lru, hpage_nr_pages(page));
+       __mod_zone_page_state(lruvec_zone(lruvec), NR_LRU_BASE + lru, nr_pages);
 }
 
-static inline void
-del_page_from_lru_list(struct zone *zone, struct page *page, enum lru_list lru)
+static __always_inline void del_page_from_lru_list(struct page *page,
+                               struct lruvec *lruvec, enum lru_list lru)
 {
-       mem_cgroup_lru_del_list(page, lru);
+       int nr_pages = hpage_nr_pages(page);
+       mem_cgroup_update_lru_size(lruvec, lru, -nr_pages);
        list_del(&page->lru);
-       __mod_zone_page_state(zone, NR_LRU_BASE + lru, -hpage_nr_pages(page));
+       __mod_zone_page_state(lruvec_zone(lruvec), NR_LRU_BASE + lru, -nr_pages);
 }
 
 /**
@@ -61,7 +61,7 @@ static inline enum lru_list page_lru_base_type(struct page *page)
  * Returns the LRU list a page was on, as an index into the array of LRU
  * lists; and clears its Unevictable or Active flags, ready for freeing.
  */
-static inline enum lru_list page_off_lru(struct page *page)
+static __always_inline enum lru_list page_off_lru(struct page *page)
 {
        enum lru_list lru;
 
@@ -85,7 +85,7 @@ static inline enum lru_list page_off_lru(struct page *page)
  * Returns the LRU list a page should be on, as an index
  * into the array of LRU lists.
  */
-static inline enum lru_list page_lru(struct page *page)
+static __always_inline enum lru_list page_lru(struct page *page)
 {
        enum lru_list lru;
 
index 26574c726121cb9faab6b6fd3af517c73840869f..dad95bdd06d798545cea969d9cd4b9091e8a3089 100644 (file)
@@ -345,17 +345,6 @@ struct mm_struct {
        /* Architecture-specific MM context */
        mm_context_t context;
 
-       /* Swap token stuff */
-       /*
-        * Last value of global fault stamp as seen by this process.
-        * In other words, this value gives an indication of how long
-        * it has been since this task got the token.
-        * Look at mm/thrash.c
-        */
-       unsigned int faultstamp;
-       unsigned int token_priority;
-       unsigned int last_interval;
-
        unsigned long flags; /* Must use atomic bitops to access the bits */
 
        struct core_state *core_state; /* coredumping support */
index c04ecfe03f7ffeca4781049faa3a0018a91ea368..580bd587d916cfa28116e814b24639e1719efe5c 100644 (file)
@@ -4,7 +4,7 @@
 #ifdef CONFIG_DEBUG_VM
 #define VM_BUG_ON(cond) BUG_ON(cond)
 #else
-#define VM_BUG_ON(cond) do { (void)(cond); } while (0)
+#define VM_BUG_ON(cond) BUILD_BUG_ON_INVALID(cond)
 #endif
 
 #ifdef CONFIG_DEBUG_VIRTUAL
index 4871e31ae27780ffcc5efd7e7a5d24a912f43afa..2427706f78b4d7043b5476b310d5a203631dbf90 100644 (file)
@@ -185,8 +185,25 @@ static inline int is_unevictable_lru(enum lru_list lru)
        return (lru == LRU_UNEVICTABLE);
 }
 
+struct zone_reclaim_stat {
+       /*
+        * The pageout code in vmscan.c keeps track of how many of the
+        * mem/swap backed and file backed pages are refeferenced.
+        * The higher the rotated/scanned ratio, the more valuable
+        * that cache is.
+        *
+        * The anon LRU stats live in [0], file LRU stats in [1]
+        */
+       unsigned long           recent_rotated[2];
+       unsigned long           recent_scanned[2];
+};
+
 struct lruvec {
        struct list_head lists[NR_LRU_LISTS];
+       struct zone_reclaim_stat reclaim_stat;
+#ifdef CONFIG_CGROUP_MEM_RES_CTLR
+       struct zone *zone;
+#endif
 };
 
 /* Mask used at gathering information at once (see memcontrol.c) */
@@ -195,16 +212,12 @@ struct lruvec {
 #define LRU_ALL_EVICTABLE (LRU_ALL_FILE | LRU_ALL_ANON)
 #define LRU_ALL             ((1 << NR_LRU_LISTS) - 1)
 
-/* Isolate inactive pages */
-#define ISOLATE_INACTIVE       ((__force isolate_mode_t)0x1)
-/* Isolate active pages */
-#define ISOLATE_ACTIVE         ((__force isolate_mode_t)0x2)
 /* Isolate clean file */
-#define ISOLATE_CLEAN          ((__force isolate_mode_t)0x4)
+#define ISOLATE_CLEAN          ((__force isolate_mode_t)0x1)
 /* Isolate unmapped file */
-#define ISOLATE_UNMAPPED       ((__force isolate_mode_t)0x8)
+#define ISOLATE_UNMAPPED       ((__force isolate_mode_t)0x2)
 /* Isolate for asynchronous migration */
-#define ISOLATE_ASYNC_MIGRATE  ((__force isolate_mode_t)0x10)
+#define ISOLATE_ASYNC_MIGRATE  ((__force isolate_mode_t)0x4)
 
 /* LRU Isolation modes. */
 typedef unsigned __bitwise__ isolate_mode_t;
@@ -313,19 +326,6 @@ enum zone_type {
 #error ZONES_SHIFT -- too many zones configured adjust calculation
 #endif
 
-struct zone_reclaim_stat {
-       /*
-        * The pageout code in vmscan.c keeps track of how many of the
-        * mem/swap backed and file backed pages are refeferenced.
-        * The higher the rotated/scanned ratio, the more valuable
-        * that cache is.
-        *
-        * The anon LRU stats live in [0], file LRU stats in [1]
-        */
-       unsigned long           recent_rotated[2];
-       unsigned long           recent_scanned[2];
-};
-
 struct zone {
        /* Fields commonly accessed by the page allocator */
 
@@ -407,8 +407,6 @@ struct zone {
        spinlock_t              lru_lock;
        struct lruvec           lruvec;
 
-       struct zone_reclaim_stat reclaim_stat;
-
        unsigned long           pages_scanned;     /* since last reclaim */
        unsigned long           flags;             /* zone flags, see below */
 
@@ -734,6 +732,17 @@ extern int init_currently_empty_zone(struct zone *zone, unsigned long start_pfn,
                                     unsigned long size,
                                     enum memmap_context context);
 
+extern void lruvec_init(struct lruvec *lruvec, struct zone *zone);
+
+static inline struct zone *lruvec_zone(struct lruvec *lruvec)
+{
+#ifdef CONFIG_CGROUP_MEM_RES_CTLR
+       return lruvec->zone;
+#else
+       return container_of(lruvec, struct zone, lruvec);
+#endif
+}
+
 #ifdef CONFIG_HAVE_MEMORY_PRESENT
 void memory_present(int nid, unsigned long start, unsigned long end);
 #else
index 34066e65fdeb327b4da8e0fd58e4dc143d985fc4..11cc2ac67e756af2b633a8badabcc49f4ffe8cb7 100644 (file)
@@ -21,8 +21,9 @@
 #define CT_LE_W(v)     cpu_to_le16(v)
 #define CT_LE_L(v)     cpu_to_le32(v)
 
+#define MSDOS_ROOT_INO  1      /* The root inode number */
+#define MSDOS_FSINFO_INO 2     /* Used for managing the FSINFO block */
 
-#define MSDOS_ROOT_INO 1       /* == MINIX_ROOT_INO */
 #define MSDOS_DIR_BITS 5       /* log2(sizeof(struct msdos_dir_entry)) */
 
 /* directory limit */
index 2d7510f389346a1987b60ded53ca3bf33ad8f603..e9ac2df079ba7517b8d5c8da00e3a234075f5063 100644 (file)
@@ -313,5 +313,8 @@ extern int kernel_sock_shutdown(struct socket *sock,
        MODULE_ALIAS("net-pf-" __stringify(pf) "-proto-" __stringify(proto) \
                     "-type-" __stringify(type))
 
+#define MODULE_ALIAS_NET_PF_PROTO_NAME(pf, proto, name) \
+       MODULE_ALIAS("net-pf-" __stringify(pf) "-proto-" __stringify(proto) \
+                    name)
 #endif /* __KERNEL__ */
 #endif /* _LINUX_NET_H */
index e7fd468f71268f5f2727260638980c2bf8aef315..d94cb14315196f7a35e64651bdd9465aba1c477e 100644 (file)
@@ -2795,15 +2795,15 @@ do {                                                            \
 #define netif_info(priv, type, dev, fmt, args...)              \
        netif_level(info, priv, type, dev, fmt, ##args)
 
-#if defined(DEBUG)
-#define netif_dbg(priv, type, dev, format, args...)            \
-       netif_printk(priv, type, KERN_DEBUG, dev, format, ##args)
-#elif defined(CONFIG_DYNAMIC_DEBUG)
+#if defined(CONFIG_DYNAMIC_DEBUG)
 #define netif_dbg(priv, type, netdev, format, args...)         \
 do {                                                           \
        if (netif_msg_##type(priv))                             \
                dynamic_netdev_dbg(netdev, format, ##args);     \
 } while (0)
+#elif defined(DEBUG)
+#define netif_dbg(priv, type, dev, format, args...)            \
+       netif_printk(priv, type, KERN_DEBUG, dev, format, ##args)
 #else
 #define netif_dbg(priv, type, dev, format, args...)                    \
 ({                                                                     \
index 0987146b0637a1fd1f9a4ea03fb7040b1d4314cd..af2d2fa30eee960a6b0f263f43e6b4a39ad5f245 100644 (file)
 #define NFS4_CDFC4_FORE_OR_BOTH 0x3
 #define NFS4_CDFC4_BACK_OR_BOTH 0x7
 
+#define NFS4_CDFS4_FORE 0x1
+#define NFS4_CDFS4_BACK 0x2
+#define NFS4_CDFS4_BOTH 0x3
+
 #define NFS4_SET_TO_SERVER_TIME        0
 #define NFS4_SET_TO_CLIENT_TIME        1
 
@@ -526,6 +530,13 @@ enum lock_type4 {
 #define FATTR4_WORD1_MOUNTED_ON_FILEID  (1UL << 23)
 #define FATTR4_WORD1_FS_LAYOUT_TYPES    (1UL << 30)
 #define FATTR4_WORD2_LAYOUT_BLKSIZE     (1UL << 1)
+#define FATTR4_WORD2_MDSTHRESHOLD       (1UL << 4)
+
+/* MDS threshold bitmap bits */
+#define THRESHOLD_RD                    (1UL << 0)
+#define THRESHOLD_WR                    (1UL << 1)
+#define THRESHOLD_RD_IO                 (1UL << 2)
+#define THRESHOLD_WR_IO                 (1UL << 3)
 
 #define NFSPROC4_NULL 0
 #define NFSPROC4_COMPOUND 1
@@ -596,6 +607,8 @@ enum {
        NFSPROC4_CLNT_TEST_STATEID,
        NFSPROC4_CLNT_FREE_STATEID,
        NFSPROC4_CLNT_GETDEVICELIST,
+       NFSPROC4_CLNT_BIND_CONN_TO_SESSION,
+       NFSPROC4_CLNT_DESTROY_CLIENTID,
 };
 
 /* nfs41 types */
index 52a1bdb4ee2bad0a668262c7b67bf8003f738095..b23cfc120edb46c5b285de63edeff61fb03242ec 100644 (file)
@@ -102,6 +102,7 @@ struct nfs_open_context {
        int error;
 
        struct list_head list;
+       struct nfs4_threshold   *mdsthreshold;
 };
 
 struct nfs_open_dir_context {
@@ -179,8 +180,7 @@ struct nfs_inode {
        __be32                  cookieverf[2];
 
        unsigned long           npages;
-       unsigned long           ncommit;
-       struct list_head        commit_list;
+       struct nfs_mds_commit_info commit_info;
 
        /* Open contexts for shared mmap writes */
        struct list_head        open_files;
@@ -201,8 +201,10 @@ struct nfs_inode {
 
        /* pNFS layout information */
        struct pnfs_layout_hdr *layout;
-       atomic_t                commits_outstanding;
 #endif /* CONFIG_NFS_V4*/
+       /* how many bytes have been written/read and how many bytes queued up */
+       __u64 write_io;
+       __u64 read_io;
 #ifdef CONFIG_NFS_FSCACHE
        struct fscache_cookie   *fscache;
 #endif
@@ -230,7 +232,6 @@ struct nfs_inode {
 #define NFS_INO_FSCACHE                (5)             /* inode can be cached by FS-Cache */
 #define NFS_INO_FSCACHE_LOCK   (6)             /* FS-Cache cookie management lock */
 #define NFS_INO_COMMIT         (7)             /* inode is committing unstable writes */
-#define NFS_INO_PNFS_COMMIT    (8)             /* use pnfs code for commit */
 #define NFS_INO_LAYOUTCOMMIT   (9)             /* layoutcommit required */
 #define NFS_INO_LAYOUTCOMMITTING (10)          /* layoutcommit inflight */
 
@@ -317,11 +318,6 @@ static inline int nfs_server_capable(struct inode *inode, int cap)
        return NFS_SERVER(inode)->caps & cap;
 }
 
-static inline int NFS_USE_READDIRPLUS(struct inode *inode)
-{
-       return test_bit(NFS_INO_ADVISE_RDPLUS, &NFS_I(inode)->flags);
-}
-
 static inline void nfs_set_verifier(struct dentry * dentry, unsigned long verf)
 {
        dentry->d_time = verf;
@@ -552,8 +548,8 @@ extern int nfs_wb_page(struct inode *inode, struct page* page);
 extern int nfs_wb_page_cancel(struct inode *inode, struct page* page);
 #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4)
 extern int  nfs_commit_inode(struct inode *, int);
-extern struct nfs_write_data *nfs_commitdata_alloc(void);
-extern void nfs_commit_free(struct nfs_write_data *wdata);
+extern struct nfs_commit_data *nfs_commitdata_alloc(void);
+extern void nfs_commit_free(struct nfs_commit_data *data);
 #else
 static inline int
 nfs_commit_inode(struct inode *inode, int how)
@@ -568,12 +564,6 @@ nfs_have_writebacks(struct inode *inode)
        return NFS_I(inode)->npages != 0;
 }
 
-/*
- * Allocate nfs_write_data structures
- */
-extern struct nfs_write_data *nfs_writedata_alloc(unsigned int npages);
-extern void nfs_writedata_free(struct nfs_write_data *);
-
 /*
  * linux/fs/nfs/read.c
  */
@@ -584,12 +574,6 @@ extern int  nfs_readpage_result(struct rpc_task *, struct nfs_read_data *);
 extern int  nfs_readpage_async(struct nfs_open_context *, struct inode *,
                               struct page *);
 
-/*
- * Allocate nfs_read_data structures
- */
-extern struct nfs_read_data *nfs_readdata_alloc(unsigned int npages);
-extern void nfs_readdata_free(struct nfs_read_data *);
-
 /*
  * linux/fs/nfs3proc.c
  */
@@ -654,6 +638,7 @@ nfs_fileid_to_ino_t(u64 fileid)
 #define NFSDBG_FSCACHE         0x0800
 #define NFSDBG_PNFS            0x1000
 #define NFSDBG_PNFS_LD         0x2000
+#define NFSDBG_STATE           0x4000
 #define NFSDBG_ALL             0xFFFF
 
 #ifdef __KERNEL__
index 7073fc74481cb6e1d69b0278e26c87c52cbc349e..fbb78fb09bd25c925d65207643bf61da614167d8 100644 (file)
@@ -17,7 +17,7 @@ struct nfs4_sequence_args;
 struct nfs4_sequence_res;
 struct nfs_server;
 struct nfs4_minor_version_ops;
-struct server_scope;
+struct nfs41_server_scope;
 struct nfs41_impl_id;
 
 /*
@@ -35,6 +35,9 @@ struct nfs_client {
 #define NFS_CS_RENEWD          3               /* - renewd started */
 #define NFS_CS_STOP_RENEW      4               /* no more state to renew */
 #define NFS_CS_CHECK_LEASE_TIME        5               /* need to check lease time */
+       unsigned long           cl_flags;       /* behavior switches */
+#define NFS_CS_NORESVPORT      0               /* - use ephemeral src port */
+#define NFS_CS_DISCRTRY                1               /* - disconnect on RPC retry */
        struct sockaddr_storage cl_addr;        /* server identifier */
        size_t                  cl_addrlen;
        char *                  cl_hostname;    /* hostname of server */
@@ -61,9 +64,6 @@ struct nfs_client {
 
        struct rpc_wait_queue   cl_rpcwaitq;
 
-       /* used for the setclientid verifier */
-       struct timespec         cl_boot_time;
-
        /* idmapper */
        struct idmap *          cl_idmap;
 
@@ -79,16 +79,17 @@ struct nfs_client {
        u32                     cl_seqid;
        /* The flags used for obtaining the clientid during EXCHANGE_ID */
        u32                     cl_exchange_flags;
-       struct nfs4_session     *cl_session;    /* sharred session */
+       struct nfs4_session     *cl_session;    /* shared session */
+       struct nfs41_server_owner *cl_serverowner;
+       struct nfs41_server_scope *cl_serverscope;
+       struct nfs41_impl_id    *cl_implid;
 #endif /* CONFIG_NFS_V4 */
 
 #ifdef CONFIG_NFS_FSCACHE
        struct fscache_cookie   *fscache;       /* client index cache cookie */
 #endif
 
-       struct server_scope     *server_scope;  /* from exchange_id */
-       struct nfs41_impl_id    *impl_id;       /* from exchange_id */
-       struct net              *net;
+       struct net              *cl_net;
 };
 
 /*
index eac30d6bec17c78db77a050e269ae0336e00e372..88d166b555e8539fa8ec5cb896c56ecea7417bdc 100644 (file)
@@ -27,7 +27,6 @@ enum {
        PG_CLEAN,
        PG_NEED_COMMIT,
        PG_NEED_RESCHED,
-       PG_PARTIAL_READ_FAILED,
        PG_COMMIT_TO_DS,
 };
 
@@ -37,7 +36,6 @@ struct nfs_page {
        struct page             *wb_page;       /* page to read in/write out */
        struct nfs_open_context *wb_context;    /* File state context info */
        struct nfs_lock_context *wb_lock_context;       /* lock context info */
-       atomic_t                wb_complete;    /* i/os we're waiting for */
        pgoff_t                 wb_index;       /* Offset >> PAGE_CACHE_SHIFT */
        unsigned int            wb_offset,      /* Offset & ~PAGE_CACHE_MASK */
                                wb_pgbase,      /* Start of page data */
@@ -68,7 +66,9 @@ struct nfs_pageio_descriptor {
        int                     pg_ioflags;
        int                     pg_error;
        const struct rpc_call_ops *pg_rpc_callops;
+       const struct nfs_pgio_completion_ops *pg_completion_ops;
        struct pnfs_layout_segment *pg_lseg;
+       struct nfs_direct_req   *pg_dreq;
 };
 
 #define NFS_WBACK_BUSY(req)    (test_bit(PG_BUSY,&(req)->wb_flags))
@@ -84,6 +84,7 @@ extern        void nfs_release_request(struct nfs_page *req);
 extern void nfs_pageio_init(struct nfs_pageio_descriptor *desc,
                             struct inode *inode,
                             const struct nfs_pageio_ops *pg_ops,
+                            const struct nfs_pgio_completion_ops *compl_ops,
                             size_t bsize,
                             int how);
 extern int nfs_pageio_add_request(struct nfs_pageio_descriptor *,
@@ -95,26 +96,17 @@ extern bool nfs_generic_pg_test(struct nfs_pageio_descriptor *desc,
                                struct nfs_page *req);
 extern  int nfs_wait_on_request(struct nfs_page *);
 extern void nfs_unlock_request(struct nfs_page *req);
+extern void nfs_unlock_and_release_request(struct nfs_page *req);
 
 /*
- * Lock the page of an asynchronous request without getting a new reference
+ * Lock the page of an asynchronous request
  */
-static inline int
-nfs_lock_request_dontget(struct nfs_page *req)
-{
-       return !test_and_set_bit(PG_BUSY, &req->wb_flags);
-}
-
 static inline int
 nfs_lock_request(struct nfs_page *req)
 {
-       if (test_and_set_bit(PG_BUSY, &req->wb_flags))
-               return 0;
-       kref_get(&req->wb_kref);
-       return 1;
+       return !test_and_set_bit(PG_BUSY, &req->wb_flags);
 }
 
-
 /**
  * nfs_list_add_request - Insert a request into a list
  * @req: request
index 7ba3551a0414a867cffe38e52cc720004e32592e..d1a7bf51c326dc7f103aae60874a667f3307b373 100644 (file)
@@ -35,6 +35,15 @@ static inline int nfs_fsid_equal(const struct nfs_fsid *a, const struct nfs_fsid
        return a->major == b->major && a->minor == b->minor;
 }
 
+struct nfs4_threshold {
+       __u32   bm;
+       __u32   l_type;
+       __u64   rd_sz;
+       __u64   wr_sz;
+       __u64   rd_io_sz;
+       __u64   wr_io_sz;
+};
+
 struct nfs_fattr {
        unsigned int            valid;          /* which fields are valid */
        umode_t                 mode;
@@ -67,6 +76,7 @@ struct nfs_fattr {
        unsigned long           gencount;
        struct nfs4_string      *owner_name;
        struct nfs4_string      *group_name;
+       struct nfs4_threshold   *mdsthreshold;  /* pNFS threshold hints */
 };
 
 #define NFS_ATTR_FATTR_TYPE            (1U << 0)
@@ -106,14 +116,14 @@ struct nfs_fattr {
                | NFS_ATTR_FATTR_FILEID \
                | NFS_ATTR_FATTR_ATIME \
                | NFS_ATTR_FATTR_MTIME \
-               | NFS_ATTR_FATTR_CTIME)
+               | NFS_ATTR_FATTR_CTIME \
+               | NFS_ATTR_FATTR_CHANGE)
 #define NFS_ATTR_FATTR_V2 (NFS_ATTR_FATTR \
                | NFS_ATTR_FATTR_BLOCKS_USED)
 #define NFS_ATTR_FATTR_V3 (NFS_ATTR_FATTR \
                | NFS_ATTR_FATTR_SPACE_USED)
 #define NFS_ATTR_FATTR_V4 (NFS_ATTR_FATTR \
-               | NFS_ATTR_FATTR_SPACE_USED \
-               | NFS_ATTR_FATTR_CHANGE)
+               | NFS_ATTR_FATTR_SPACE_USED)
 
 /*
  * Info on the file system
@@ -338,7 +348,6 @@ struct nfs_openargs {
        const struct qstr *     name;
        const struct nfs_server *server;         /* Needed for ID mapping */
        const u32 *             bitmask;
-       const u32 *             dir_bitmask;
        __u32                   claim;
        struct nfs4_sequence_args       seq_args;
 };
@@ -349,7 +358,6 @@ struct nfs_openres {
        struct nfs4_change_info cinfo;
        __u32                   rflags;
        struct nfs_fattr *      f_attr;
-       struct nfs_fattr *      dir_attr;
        struct nfs_seqid *      seqid;
        const struct nfs_server *server;
        fmode_t                 delegation_type;
@@ -518,13 +526,30 @@ struct nfs_writeres {
        struct nfs4_sequence_res        seq_res;
 };
 
+/*
+ * Arguments to the commit call.
+ */
+struct nfs_commitargs {
+       struct nfs_fh           *fh;
+       __u64                   offset;
+       __u32                   count;
+       const u32               *bitmask;
+       struct nfs4_sequence_args       seq_args;
+};
+
+struct nfs_commitres {
+       struct nfs_fattr        *fattr;
+       struct nfs_writeverf    *verf;
+       const struct nfs_server *server;
+       struct nfs4_sequence_res        seq_res;
+};
+
 /*
  * Common arguments to the unlink call
  */
 struct nfs_removeargs {
        const struct nfs_fh     *fh;
        struct qstr             name;
-       const u32 *             bitmask;
        struct nfs4_sequence_args       seq_args;
 };
 
@@ -543,7 +568,6 @@ struct nfs_renameargs {
        const struct nfs_fh             *new_dir;
        const struct qstr               *old_name;
        const struct qstr               *new_name;
-       const u32                       *bitmask;
        struct nfs4_sequence_args       seq_args;
 };
 
@@ -839,7 +863,6 @@ struct nfs4_create_res {
        struct nfs_fh *                 fh;
        struct nfs_fattr *              fattr;
        struct nfs4_change_info         dir_cinfo;
-       struct nfs_fattr *              dir_fattr;
        struct nfs4_sequence_res        seq_res;
 };
 
@@ -1061,6 +1084,21 @@ struct nfstime4 {
 };
 
 #ifdef CONFIG_NFS_V4_1
+
+struct pnfs_commit_bucket {
+       struct list_head written;
+       struct list_head committing;
+       struct pnfs_layout_segment *wlseg;
+       struct pnfs_layout_segment *clseg;
+};
+
+struct pnfs_ds_commit_info {
+       int nwritten;
+       int ncommitting;
+       int nbuckets;
+       struct pnfs_commit_bucket *buckets;
+};
+
 #define NFS4_EXCHANGE_ID_LEN   (48)
 struct nfs41_exchange_id_args {
        struct nfs_client               *client;
@@ -1070,13 +1108,13 @@ struct nfs41_exchange_id_args {
        u32                             flags;
 };
 
-struct server_owner {
+struct nfs41_server_owner {
        uint64_t                        minor_id;
        uint32_t                        major_id_sz;
        char                            major_id[NFS4_OPAQUE_LIMIT];
 };
 
-struct server_scope {
+struct nfs41_server_scope {
        uint32_t                        server_scope_sz;
        char                            server_scope[NFS4_OPAQUE_LIMIT];
 };
@@ -1087,10 +1125,18 @@ struct nfs41_impl_id {
        struct nfstime4                 date;
 };
 
+struct nfs41_bind_conn_to_session_res {
+       struct nfs4_session             *session;
+       u32                             dir;
+       bool                            use_conn_in_rdma_mode;
+};
+
 struct nfs41_exchange_id_res {
-       struct nfs_client               *client;
+       u64                             clientid;
+       u32                             seqid;
        u32                             flags;
-       struct server_scope             *server_scope;
+       struct nfs41_server_owner       *server_owner;
+       struct nfs41_server_scope       *server_scope;
        struct nfs41_impl_id            *impl_id;
 };
 
@@ -1143,35 +1189,114 @@ struct nfs41_free_stateid_res {
        struct nfs4_sequence_res        seq_res;
 };
 
+#else
+
+struct pnfs_ds_commit_info {
+};
+
 #endif /* CONFIG_NFS_V4_1 */
 
 struct nfs_page;
 
 #define NFS_PAGEVEC_SIZE       (8U)
 
+struct nfs_page_array {
+       struct page             **pagevec;
+       unsigned int            npages;         /* Max length of pagevec */
+       struct page             *page_array[NFS_PAGEVEC_SIZE];
+};
+
 struct nfs_read_data {
+       struct nfs_pgio_header  *header;
+       struct list_head        list;
        struct rpc_task         task;
-       struct inode            *inode;
-       struct rpc_cred         *cred;
        struct nfs_fattr        fattr;  /* fattr storage */
-       struct list_head        pages;  /* Coalesced read requests */
-       struct list_head        list;   /* lists of struct nfs_read_data */
-       struct nfs_page         *req;   /* multi ops per nfs_page */
-       struct page             **pagevec;
-       unsigned int            npages; /* Max length of pagevec */
        struct nfs_readargs args;
        struct nfs_readres  res;
        unsigned long           timestamp;      /* For lease renewal */
-       struct pnfs_layout_segment *lseg;
-       struct nfs_client       *ds_clp;        /* pNFS data server */
-       const struct rpc_call_ops *mds_ops;
        int (*read_done_cb) (struct rpc_task *task, struct nfs_read_data *data);
        __u64                   mds_offset;
+       struct nfs_page_array   pages;
+       struct nfs_client       *ds_clp;        /* pNFS data server */
+};
+
+/* used as flag bits in nfs_pgio_header */
+enum {
+       NFS_IOHDR_ERROR = 0,
+       NFS_IOHDR_EOF,
+       NFS_IOHDR_REDO,
+       NFS_IOHDR_NEED_COMMIT,
+       NFS_IOHDR_NEED_RESCHED,
+};
+
+struct nfs_pgio_header {
+       struct inode            *inode;
+       struct rpc_cred         *cred;
+       struct list_head        pages;
+       struct list_head        rpc_list;
+       atomic_t                refcnt;
+       struct nfs_page         *req;
+       struct pnfs_layout_segment *lseg;
+       loff_t                  io_start;
+       const struct rpc_call_ops *mds_ops;
+       void (*release) (struct nfs_pgio_header *hdr);
+       const struct nfs_pgio_completion_ops *completion_ops;
+       struct nfs_direct_req   *dreq;
+       spinlock_t              lock;
+       /* fields protected by lock */
        int                     pnfs_error;
-       struct page             *page_array[NFS_PAGEVEC_SIZE];
+       int                     error;          /* merge with pnfs_error */
+       unsigned long           good_bytes;     /* boundary of good data */
+       unsigned long           flags;
+};
+
+struct nfs_read_header {
+       struct nfs_pgio_header  header;
+       struct nfs_read_data    rpc_data;
 };
 
 struct nfs_write_data {
+       struct nfs_pgio_header  *header;
+       struct list_head        list;
+       struct rpc_task         task;
+       struct nfs_fattr        fattr;
+       struct nfs_writeverf    verf;
+       struct nfs_writeargs    args;           /* argument struct */
+       struct nfs_writeres     res;            /* result struct */
+       unsigned long           timestamp;      /* For lease renewal */
+       int (*write_done_cb) (struct rpc_task *task, struct nfs_write_data *data);
+       __u64                   mds_offset;     /* Filelayout dense stripe */
+       struct nfs_page_array   pages;
+       struct nfs_client       *ds_clp;        /* pNFS data server */
+};
+
+struct nfs_write_header {
+       struct nfs_pgio_header  header;
+       struct nfs_write_data   rpc_data;
+};
+
+struct nfs_mds_commit_info {
+       atomic_t rpcs_out;
+       unsigned long           ncommit;
+       struct list_head        list;
+};
+
+struct nfs_commit_data;
+struct nfs_inode;
+struct nfs_commit_completion_ops {
+       void (*error_cleanup) (struct nfs_inode *nfsi);
+       void (*completion) (struct nfs_commit_data *data);
+};
+
+struct nfs_commit_info {
+       spinlock_t                      *lock;
+       struct nfs_mds_commit_info      *mds;
+       struct pnfs_ds_commit_info      *ds;
+       struct nfs_direct_req           *dreq;  /* O_DIRECT request */
+       const struct nfs_commit_completion_ops *completion_ops;
+};
+
+struct nfs_commit_data {
        struct rpc_task         task;
        struct inode            *inode;
        struct rpc_cred         *cred;
@@ -1179,22 +1304,22 @@ struct nfs_write_data {
        struct nfs_writeverf    verf;
        struct list_head        pages;          /* Coalesced requests we wish to flush */
        struct list_head        list;           /* lists of struct nfs_write_data */
-       struct nfs_page         *req;           /* multi ops per nfs_page */
-       struct page             **pagevec;
-       unsigned int            npages;         /* Max length of pagevec */
-       struct nfs_writeargs    args;           /* argument struct */
-       struct nfs_writeres     res;            /* result struct */
+       struct nfs_direct_req   *dreq;          /* O_DIRECT request */
+       struct nfs_commitargs   args;           /* argument struct */
+       struct nfs_commitres    res;            /* result struct */
+       struct nfs_open_context *context;
        struct pnfs_layout_segment *lseg;
        struct nfs_client       *ds_clp;        /* pNFS data server */
        int                     ds_commit_index;
        const struct rpc_call_ops *mds_ops;
-       int (*write_done_cb) (struct rpc_task *task, struct nfs_write_data *data);
-#ifdef CONFIG_NFS_V4
-       unsigned long           timestamp;      /* For lease renewal */
-#endif
-       __u64                   mds_offset;     /* Filelayout dense stripe */
-       int                     pnfs_error;
-       struct page             *page_array[NFS_PAGEVEC_SIZE];
+       const struct nfs_commit_completion_ops *completion_ops;
+       int (*commit_done_cb) (struct rpc_task *task, struct nfs_commit_data *data);
+};
+
+struct nfs_pgio_completion_ops {
+       void    (*error_cleanup)(struct list_head *head);
+       void    (*init_hdr)(struct nfs_pgio_header *hdr);
+       void    (*completion)(struct nfs_pgio_header *hdr);
 };
 
 struct nfs_unlinkdata {
@@ -1234,11 +1359,13 @@ struct nfs_rpc_ops {
 
        int     (*getroot) (struct nfs_server *, struct nfs_fh *,
                            struct nfs_fsinfo *);
+       struct vfsmount *(*submount) (struct nfs_server *, struct dentry *,
+                                     struct nfs_fh *, struct nfs_fattr *);
        int     (*getattr) (struct nfs_server *, struct nfs_fh *,
                            struct nfs_fattr *);
        int     (*setattr) (struct dentry *, struct nfs_fattr *,
                            struct iattr *);
-       int     (*lookup)  (struct rpc_clnt *clnt, struct inode *, struct qstr *,
+       int     (*lookup)  (struct inode *, struct qstr *,
                            struct nfs_fh *, struct nfs_fattr *);
        int     (*access)  (struct inode *, struct nfs_access_entry *);
        int     (*readlink)(struct inode *, struct page *, unsigned int,
@@ -1277,8 +1404,9 @@ struct nfs_rpc_ops {
        void    (*write_setup)  (struct nfs_write_data *, struct rpc_message *);
        void    (*write_rpc_prepare)(struct rpc_task *, struct nfs_write_data *);
        int     (*write_done)  (struct rpc_task *, struct nfs_write_data *);
-       void    (*commit_setup) (struct nfs_write_data *, struct rpc_message *);
-       int     (*commit_done) (struct rpc_task *, struct nfs_write_data *);
+       void    (*commit_setup) (struct nfs_commit_data *, struct rpc_message *);
+       void    (*commit_rpc_prepare)(struct rpc_task *, struct nfs_commit_data *);
+       int     (*commit_done) (struct rpc_task *, struct nfs_commit_data *);
        int     (*lock)(struct file *, int, struct file_lock *);
        int     (*lock_check_bounds)(const struct file_lock *);
        void    (*clear_acl_cache)(struct inode *);
@@ -1287,9 +1415,9 @@ struct nfs_rpc_ops {
                                struct nfs_open_context *ctx,
                                int open_flags,
                                struct iattr *iattr);
-       int     (*init_client) (struct nfs_client *, const struct rpc_timeout *,
-                               const char *, rpc_authflavor_t, int);
-       int     (*secinfo)(struct inode *, const struct qstr *, struct nfs4_secinfo_flavors *);
+       struct nfs_client *
+               (*init_client) (struct nfs_client *, const struct rpc_timeout *,
+                               const char *, rpc_authflavor_t);
 };
 
 /*
index f85308e688fd712f039ac45a5f442240113ad0bf..e33f747b173c500d02639dfd5257de093a79fff0 100644 (file)
@@ -103,6 +103,7 @@ struct svc_export {
        struct nfsd4_fs_locations ex_fslocs;
        int                     ex_nflavors;
        struct exp_flavor_info  ex_flavors[MAX_SECINFO_LIST];
+       struct cache_detail     *cd;
 };
 
 /* an "export key" (expkey) maps a filehandlefragement to an
@@ -129,24 +130,22 @@ __be32 check_nfsd_access(struct svc_export *exp, struct svc_rqst *rqstp);
 /*
  * Function declarations
  */
-int                    nfsd_export_init(void);
-void                   nfsd_export_shutdown(void);
-void                   nfsd_export_flush(void);
+int                    nfsd_export_init(struct net *);
+void                   nfsd_export_shutdown(struct net *);
+void                   nfsd_export_flush(struct net *);
 struct svc_export *    rqst_exp_get_by_name(struct svc_rqst *,
                                             struct path *);
 struct svc_export *    rqst_exp_parent(struct svc_rqst *,
                                        struct path *);
 struct svc_export *    rqst_find_fsidzero_export(struct svc_rqst *);
-int                    exp_rootfh(struct auth_domain *, 
+int                    exp_rootfh(struct net *, struct auth_domain *,
                                        char *path, struct knfsd_fh *, int maxsize);
 __be32                 exp_pseudoroot(struct svc_rqst *, struct svc_fh *);
 __be32                 nfserrno(int errno);
 
-extern struct cache_detail svc_export_cache;
-
 static inline void exp_put(struct svc_export *exp)
 {
-       cache_put(&exp->h, &svc_export_cache);
+       cache_put(&exp->h, exp->cd);
 }
 
 static inline void exp_get(struct svc_export *exp)
index f93e21700d3eeeb4848f3ff3419fee6de59fe2be..bb115deb7612815d016aa2ec99c0bbe7c1cab212 100644 (file)
@@ -5,7 +5,7 @@
 
 struct pci_dev;
 struct of_irq;
-int of_irq_map_pci(struct pci_dev *pdev, struct of_irq *out_irq);
+int of_irq_map_pci(const struct pci_dev *pdev, struct of_irq *out_irq);
 
 struct device_node;
 struct device_node *of_pci_find_child_device(struct device_node *parent,
index 3d7647536b0304ba40013aa2399d551b6779b4cf..e4c29bc72e70297af00eb276538840e72b163eda 100644 (file)
@@ -43,8 +43,9 @@ enum oom_constraint {
 extern void compare_swap_oom_score_adj(int old_val, int new_val);
 extern int test_set_oom_score_adj(int new_val);
 
-extern unsigned int oom_badness(struct task_struct *p, struct mem_cgroup *memcg,
-                       const nodemask_t *nodemask, unsigned long totalpages);
+extern unsigned long oom_badness(struct task_struct *p,
+               struct mem_cgroup *memcg, const nodemask_t *nodemask,
+               unsigned long totalpages);
 extern int try_set_zonelist_oom(struct zonelist *zonelist, gfp_t gfp_flags);
 extern void clear_zonelist_oom(struct zonelist *zonelist, gfp_t gfp_flags);
 
index efa26b4da8d2b9d2479284bc9cbaeff61913d634..7cfad3bbb0cc214d37c6312a86a0a317fde12edd 100644 (file)
@@ -460,11 +460,11 @@ static inline int fault_in_pages_readable(const char __user *uaddr, int size)
  */
 static inline int fault_in_multipages_writeable(char __user *uaddr, int size)
 {
-       int ret;
+       int ret = 0;
        char __user *end = uaddr + size - 1;
 
        if (unlikely(size == 0))
-               return 0;
+               return ret;
 
        /*
         * Writing zeroes into userspace here is OK, because we know that if
@@ -489,11 +489,11 @@ static inline int fault_in_multipages_readable(const char __user *uaddr,
                                               int size)
 {
        volatile char c;
-       int ret;
+       int ret = 0;
        const char __user *end = uaddr + size - 1;
 
        if (unlikely(size == 0))
-               return 0;
+               return ret;
 
        while (uaddr <= end) {
                ret = __get_user(c, uaddr);
index 17b7b5b01b4ad5feaf5ba91ef554406496b9fd91..d8c379dba6adbb36ae9df6c18adea304e2c6b45c 100644 (file)
@@ -687,7 +687,7 @@ int __must_check pci_bus_add_device(struct pci_dev *dev);
 void pci_read_bridge_bases(struct pci_bus *child);
 struct resource *pci_find_parent_resource(const struct pci_dev *dev,
                                          struct resource *res);
-u8 pci_swizzle_interrupt_pin(struct pci_dev *dev, u8 pin);
+u8 pci_swizzle_interrupt_pin(const struct pci_dev *dev, u8 pin);
 int pci_get_interrupt_pin(struct pci_dev *dev, struct pci_dev **bridge);
 u8 pci_common_swizzle(struct pci_dev *dev, u8 *pinp);
 extern struct pci_dev *pci_dev_get(struct pci_dev *dev);
@@ -1692,7 +1692,8 @@ extern void pci_release_bus_of_node(struct pci_bus *bus);
 /* Arch may override this (weak) */
 extern struct device_node * __weak pcibios_get_phb_of_node(struct pci_bus *bus);
 
-static inline struct device_node *pci_device_to_OF_node(struct pci_dev *pdev)
+static inline struct device_node *
+pci_device_to_OF_node(const struct pci_dev *pdev)
 {
        return pdev ? pdev->dev.of_node : NULL;
 }
index 3329965ed63f3aa1c2b54e49d82917d8f34a3839..ab741b0d007402daf8feee824e0aae91751bafe6 100644 (file)
 #define PCI_DEVICE_ID_INTEL_MRST_SD2   0x084F
 #define PCI_DEVICE_ID_INTEL_I960       0x0960
 #define PCI_DEVICE_ID_INTEL_I960RM     0x0962
+#define PCI_DEVICE_ID_INTEL_CENTERTON_ILB      0x0c60
 #define PCI_DEVICE_ID_INTEL_8257X_SOL  0x1062
 #define PCI_DEVICE_ID_INTEL_82573E_SOL 0x1085
 #define PCI_DEVICE_ID_INTEL_82573L_SOL 0x108F
index 4f75e531c112c176b7a29146c6581e857442dba2..241065c9ce51832962f0fce4c93696d4b09dcb9d 100644 (file)
@@ -18,6 +18,8 @@
 #include <linux/power_supply.h>
 
 enum data_source {
+       CM_BATTERY_PRESENT,
+       CM_NO_BATTERY,
        CM_FUEL_GAUGE,
        CM_CHARGER_STAT,
 };
@@ -29,6 +31,16 @@ enum polling_modes {
        CM_POLL_CHARGING_ONLY,
 };
 
+enum cm_event_types {
+       CM_EVENT_UNKNOWN = 0,
+       CM_EVENT_BATT_FULL,
+       CM_EVENT_BATT_IN,
+       CM_EVENT_BATT_OUT,
+       CM_EVENT_EXT_PWR_IN_OUT,
+       CM_EVENT_CHG_START_STOP,
+       CM_EVENT_OTHERS,
+};
+
 /**
  * struct charger_global_desc
  * @rtc_name: the name of RTC used to wake up the system from suspend.
@@ -38,11 +50,18 @@ enum polling_modes {
  *     rtc_only_wakeup() returning false.
  *     If the RTC given to CM is the only wakeup reason,
  *     rtc_only_wakeup should return true.
+ * @assume_timer_stops_in_suspend:
+ *     Assume that the jiffy timer stops in suspend-to-RAM.
+ *     When enabled, CM does not rely on jiffies value in
+ *     suspend_again and assumes that jiffies value does not
+ *     change during suspend.
  */
 struct charger_global_desc {
        char *rtc_name;
 
        bool (*rtc_only_wakeup)(void);
+
+       bool assume_timer_stops_in_suspend;
 };
 
 /**
@@ -50,6 +69,11 @@ struct charger_global_desc {
  * @psy_name: the name of power-supply-class for charger manager
  * @polling_mode:
  *     Determine which polling mode will be used
+ * @fullbatt_vchkdrop_ms:
+ * @fullbatt_vchkdrop_uV:
+ *     Check voltage drop after the battery is fully charged.
+ *     If it has dropped more than fullbatt_vchkdrop_uV after
+ *     fullbatt_vchkdrop_ms, CM will restart charging.
  * @fullbatt_uV: voltage in microvolt
  *     If it is not being charged and VBATT >= fullbatt_uV,
  *     it is assumed to be full.
@@ -76,6 +100,8 @@ struct charger_desc {
        enum polling_modes polling_mode;
        unsigned int polling_interval_ms;
 
+       unsigned int fullbatt_vchkdrop_ms;
+       unsigned int fullbatt_vchkdrop_uV;
        unsigned int fullbatt_uV;
 
        enum data_source battery_present;
@@ -101,6 +127,11 @@ struct charger_desc {
  * @fuel_gauge: power_supply for fuel gauge
  * @charger_stat: array of power_supply for chargers
  * @charger_enabled: the state of charger
+ * @fullbatt_vchk_jiffies_at:
+ *     jiffies at the time full battery check will occur.
+ * @fullbatt_vchk_uV: voltage in microvolt
+ *     criteria for full battery
+ * @fullbatt_vchk_work: work queue for full battery check
  * @emergency_stop:
  *     When setting true, stop charging
  * @last_temp_mC: the measured temperature in milli-Celsius
@@ -121,6 +152,10 @@ struct charger_manager {
 
        bool charger_enabled;
 
+       unsigned long fullbatt_vchk_jiffies_at;
+       unsigned int fullbatt_vchk_uV;
+       struct delayed_work fullbatt_vchk_work;
+
        int emergency_stop;
        int last_temp_mC;
 
@@ -134,14 +169,13 @@ struct charger_manager {
 #ifdef CONFIG_CHARGER_MANAGER
 extern int setup_charger_manager(struct charger_global_desc *gd);
 extern bool cm_suspend_again(void);
+extern void cm_notify_event(struct power_supply *psy,
+                               enum cm_event_types type, char *msg);
 #else
-static void __maybe_unused setup_charger_manager(struct charger_global_desc *gd)
-{ }
-
-static bool __maybe_unused cm_suspend_again(void)
-{
-       return false;
-}
+static inline int setup_charger_manager(struct charger_global_desc *gd)
+{ return 0; }
+static inline bool cm_suspend_again(void) { return false; }
+static inline void cm_notify_event(struct power_supply *psy,
+                               enum cm_event_types type, char *msg) { }
 #endif
-
 #endif /* _CHARGER_MANAGER_H */
index e01b167e66f068223f86321109b77687ce5ef50c..89dd84f47c6ed6041cde8b9259c8e737072a6ffa 100644 (file)
@@ -116,6 +116,18 @@ enum max17042_register {
        MAX17042_VFSOC          = 0xFF,
 };
 
+/* Registers specific to max17047/50 */
+enum max17047_register {
+       MAX17047_QRTbl00        = 0x12,
+       MAX17047_FullSOCThr     = 0x13,
+       MAX17047_QRTbl10        = 0x22,
+       MAX17047_QRTbl20        = 0x32,
+       MAX17047_V_empty        = 0x3A,
+       MAX17047_QRTbl30        = 0x42,
+};
+
+enum max170xx_chip_type {MAX17042, MAX17047};
+
 /*
  * used for setting a register to a desired value
  * addr : address for a register
@@ -144,6 +156,7 @@ struct max17042_config_data {
        u16     shdntimer;      /* 0x03F */
 
        /* App data */
+       u16     full_soc_thresh;        /* 0x13 */
        u16     design_cap;     /* 0x18 */
        u16     ichgt_term;     /* 0x1E */
 
@@ -162,6 +175,10 @@ struct max17042_config_data {
        u16     lavg_empty;     /* 0x36 */
        u16     dqacc;          /* 0x45 */
        u16     dpacc;          /* 0x46 */
+       u16     qrtbl00;        /* 0x12 */
+       u16     qrtbl10;        /* 0x22 */
+       u16     qrtbl20;        /* 0x32 */
+       u16     qrtbl30;        /* 0x42 */
 
        /* Cell technology from power_supply.h */
        u16     cell_technology;
index c38c13db8832e7b3c15440807e7e7719a5603792..3b912bee28d1693b8c6617f637354ed2869d306f 100644 (file)
@@ -96,6 +96,7 @@ enum power_supply_property {
        POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
        POWER_SUPPLY_PROP_VOLTAGE_NOW,
        POWER_SUPPLY_PROP_VOLTAGE_AVG,
+       POWER_SUPPLY_PROP_VOLTAGE_OCV,
        POWER_SUPPLY_PROP_CURRENT_MAX,
        POWER_SUPPLY_PROP_CURRENT_NOW,
        POWER_SUPPLY_PROP_CURRENT_AVG,
@@ -211,7 +212,7 @@ extern void power_supply_changed(struct power_supply *psy);
 extern int power_supply_am_i_supplied(struct power_supply *psy);
 extern int power_supply_set_battery_charged(struct power_supply *psy);
 
-#if defined(CONFIG_POWER_SUPPLY) || defined(CONFIG_POWER_SUPPLY_MODULE)
+#ifdef CONFIG_POWER_SUPPLY
 extern int power_supply_is_system_supplied(void);
 #else
 static inline int power_supply_is_system_supplied(void) { return -ENOSYS; }
@@ -261,6 +262,7 @@ static inline bool power_supply_is_watt_property(enum power_supply_property psp)
        case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
        case POWER_SUPPLY_PROP_VOLTAGE_NOW:
        case POWER_SUPPLY_PROP_VOLTAGE_AVG:
+       case POWER_SUPPLY_PROP_VOLTAGE_OCV:
        case POWER_SUPPLY_PROP_POWER_NOW:
                return 1;
        default:
index 78b76e24cc7eed661d7696200c4c4f2291425a0d..711e0a30aaccc84b3a1bf5d15794b139433b5309 100644 (file)
 # define PR_SET_MM_START_STACK         5
 # define PR_SET_MM_START_BRK           6
 # define PR_SET_MM_BRK                 7
+# define PR_SET_MM_ARG_START           8
+# define PR_SET_MM_ARG_END             9
+# define PR_SET_MM_ENV_START           10
+# define PR_SET_MM_ENV_END             11
+# define PR_SET_MM_AUXV                        12
+# define PR_SET_MM_EXE_FILE            13
 
 /*
  * Set specific pid that is allowed to ptrace the current task.
index fb201896a8b07136db13bcef486fdb382b7ee3c2..7d7fbe2ef7822089c802c5654b4e0ec243f24a80 100644 (file)
@@ -119,7 +119,7 @@ int __must_check res_counter_charge_locked(struct res_counter *counter,
                                           unsigned long val, bool force);
 int __must_check res_counter_charge(struct res_counter *counter,
                unsigned long val, struct res_counter **limit_fail_at);
-int __must_check res_counter_charge_nofail(struct res_counter *counter,
+int res_counter_charge_nofail(struct res_counter *counter,
                unsigned long val, struct res_counter **limit_fail_at);
 
 /*
@@ -135,6 +135,9 @@ int __must_check res_counter_charge_nofail(struct res_counter *counter,
 void res_counter_uncharge_locked(struct res_counter *counter, unsigned long val);
 void res_counter_uncharge(struct res_counter *counter, unsigned long val);
 
+void res_counter_uncharge_until(struct res_counter *counter,
+                               struct res_counter *top,
+                               unsigned long val);
 /**
  * res_counter_margin - calculate chargeable space of a counter
  * @cnt: the counter
index 4d50611112ba118e69df87888a81f764aee4c346..a90ebadd9da055bb5130782246872a0ef53d8438 100644 (file)
@@ -20,6 +20,9 @@
 #include <linux/errno.h>
 #include <linux/device.h>
 #include <linux/rio_regs.h>
+#ifdef CONFIG_RAPIDIO_DMA_ENGINE
+#include <linux/dmaengine.h>
+#endif
 
 #define RIO_NO_HOPCOUNT                -1
 #define RIO_INVALID_DESTID     0xffff
@@ -254,6 +257,9 @@ struct rio_mport {
        u32 phys_efptr;
        unsigned char name[40];
        void *priv;             /* Master port private data */
+#ifdef CONFIG_RAPIDIO_DMA_ENGINE
+       struct dma_device       dma;
+#endif
 };
 
 /**
@@ -395,6 +401,47 @@ union rio_pw_msg {
        u32 raw[RIO_PW_MSG_SIZE/sizeof(u32)];
 };
 
+#ifdef CONFIG_RAPIDIO_DMA_ENGINE
+
+/**
+ * enum rio_write_type - RIO write transaction types used in DMA transfers
+ *
+ * Note: RapidIO specification defines write (NWRITE) and
+ * write-with-response (NWRITE_R) data transfer operations.
+ * Existing DMA controllers that service RapidIO may use one of these operations
+ * for entire data transfer or their combination with only the last data packet
+ * requires response.
+ */
+enum rio_write_type {
+       RDW_DEFAULT,            /* default method used by DMA driver */
+       RDW_ALL_NWRITE,         /* all packets use NWRITE */
+       RDW_ALL_NWRITE_R,       /* all packets use NWRITE_R */
+       RDW_LAST_NWRITE_R,      /* last packet uses NWRITE_R, others - NWRITE */
+};
+
+struct rio_dma_ext {
+       u16 destid;
+       u64 rio_addr;   /* low 64-bits of 66-bit RapidIO address */
+       u8  rio_addr_u;  /* upper 2-bits of 66-bit RapidIO address */
+       enum rio_write_type wr_type; /* preferred RIO write operation type */
+};
+
+struct rio_dma_data {
+       /* Local data (as scatterlist) */
+       struct scatterlist      *sg;    /* I/O scatter list */
+       unsigned int            sg_len; /* size of scatter list */
+       /* Remote device address (flat buffer) */
+       u64 rio_addr;   /* low 64-bits of 66-bit RapidIO address */
+       u8  rio_addr_u;  /* upper 2-bits of 66-bit RapidIO address */
+       enum rio_write_type wr_type; /* preferred RIO write operation type */
+};
+
+static inline struct rio_mport *dma_to_mport(struct dma_device *ddev)
+{
+       return container_of(ddev, struct rio_mport, dma);
+}
+#endif /* CONFIG_RAPIDIO_DMA_ENGINE */
+
 /* Architecture and hardware-specific functions */
 extern int rio_register_mport(struct rio_mport *);
 extern int rio_open_inb_mbox(struct rio_mport *, void *, int, int);
index 7f07470e1ed9443e20cbc1cafe0d696e04bdb8c6..31ad146be3168bd127bdd083a0288f940f1b154b 100644 (file)
@@ -377,6 +377,15 @@ void rio_unregister_driver(struct rio_driver *);
 struct rio_dev *rio_dev_get(struct rio_dev *);
 void rio_dev_put(struct rio_dev *);
 
+#ifdef CONFIG_RAPIDIO_DMA_ENGINE
+extern struct dma_chan *rio_request_dma(struct rio_dev *rdev);
+extern void rio_release_dma(struct dma_chan *dchan);
+extern struct dma_async_tx_descriptor *rio_dma_prep_slave_sg(
+               struct rio_dev *rdev, struct dma_chan *dchan,
+               struct rio_dma_data *data,
+               enum dma_transfer_direction direction, unsigned long flags);
+#endif
+
 /**
  * rio_name - Get the unique RIO device identifier
  * @rdev: RIO device
index fd07c4542cee4f60784081267fb069787919a5bc..3fce545df394c61b3c8a7f4babfdbbee212131a8 100644 (file)
@@ -173,8 +173,6 @@ enum ttu_flags {
 };
 #define TTU_ACTION(x) ((x) & TTU_ACTION_MASK)
 
-bool is_vma_temporary_stack(struct vm_area_struct *vma);
-
 int try_to_unmap(struct page *, enum ttu_flags flags);
 int try_to_unmap_one(struct page *, struct vm_area_struct *,
                        unsigned long address, enum ttu_flags flags);
index fcabfb4873c8dd6e8466233de9d557be4bf2101e..f071b3922c67f7a253c0b5f978b4bec1b7a690df 100644 (file)
@@ -91,6 +91,9 @@ struct rtc_pll_info {
 #define RTC_PLL_GET    _IOR('p', 0x11, struct rtc_pll_info)  /* Get PLL correction */
 #define RTC_PLL_SET    _IOW('p', 0x12, struct rtc_pll_info)  /* Set PLL correction */
 
+#define RTC_VL_READ    _IOR('p', 0x13, int)    /* Voltage low detector */
+#define RTC_VL_CLR     _IO('p', 0x14)          /* Clear voltage low information */
+
 /* interrupt flags */
 #define RTC_IRQF 0x80  /* Any of the following is active */
 #define RTC_PF 0x40    /* Periodic interrupt */
diff --git a/include/linux/rtc/ds1307.h b/include/linux/rtc/ds1307.h
new file mode 100644 (file)
index 0000000..291b1c4
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * ds1307.h - platform_data for the ds1307 (and variants) rtc driver
+ * (C) Copyright 2012 by Wolfram Sang, Pengutronix e.K.
+ * same license as the driver
+ */
+
+#ifndef _LINUX_DS1307_H
+#define _LINUX_DS1307_H
+
+#include <linux/types.h>
+
+#define DS1307_TRICKLE_CHARGER_250_OHM 0x01
+#define DS1307_TRICKLE_CHARGER_2K_OHM  0x02
+#define DS1307_TRICKLE_CHARGER_4K_OHM  0x03
+#define DS1307_TRICKLE_CHARGER_NO_DIODE        0x04
+#define DS1307_TRICKLE_CHARGER_DIODE   0x08
+
+struct ds1307_platform_data {
+       u8 trickle_charger_setup;
+};
+
+#endif /* _LINUX_DS1307_H */
index f45c0b280b5d39873aaca3a3d67b1a01362adba8..f34437e835a7069dfdc4660dbed593ebb371d0e9 100644 (file)
@@ -1301,11 +1301,6 @@ struct task_struct {
        unsigned sched_reset_on_fork:1;
        unsigned sched_contributes_to_load:1;
 
-#ifdef CONFIG_GENERIC_HARDIRQS
-       /* IRQ handler threads */
-       unsigned irq_thread:1;
-#endif
-
        pid_t pid;
        pid_t tgid;
 
@@ -1313,10 +1308,9 @@ struct task_struct {
        /* Canary value for the -fstack-protector gcc feature */
        unsigned long stack_canary;
 #endif
-
-       /* 
+       /*
         * pointers to (original) parent process, youngest child, younger sibling,
-        * older sibling, respectively.  (p->father can be replaced with 
+        * older sibling, respectively.  (p->father can be replaced with
         * p->real_parent->pid)
         */
        struct task_struct __rcu *real_parent; /* real parent process */
@@ -1363,8 +1357,6 @@ struct task_struct {
                                         * credentials (COW) */
        const struct cred __rcu *cred;  /* effective (overridable) subjective task
                                         * credentials (COW) */
-       struct cred *replacement_session_keyring; /* for KEYCTL_SESSION_TO_PARENT */
-
        char comm[TASK_COMM_LEN]; /* executable name excluding path
                                     - access with [gs]et_task_comm (which lock
                                       it with task_lock())
@@ -1400,6 +1392,8 @@ struct task_struct {
        int (*notifier)(void *priv);
        void *notifier_data;
        sigset_t *notifier_mask;
+       struct hlist_head task_works;
+
        struct audit_context *audit_context;
 #ifdef CONFIG_AUDITSYSCALL
        uid_t loginuid;
@@ -2213,6 +2207,20 @@ extern int send_sigqueue(struct sigqueue *,  struct task_struct *, int group);
 extern int do_sigaction(int, struct k_sigaction *, struct k_sigaction *);
 extern int do_sigaltstack(const stack_t __user *, stack_t __user *, unsigned long);
 
+static inline void restore_saved_sigmask(void)
+{
+       if (test_and_clear_restore_sigmask())
+               __set_current_blocked(&current->saved_sigmask);
+}
+
+static inline sigset_t *sigmask_to_save(void)
+{
+       sigset_t *res = &current->blocked;
+       if (unlikely(test_restore_sigmask()))
+               res = &current->saved_sigmask;
+       return res;
+}
+
 static inline int kill_cad_pid(int sig, int priv)
 {
        return kill_pid(cad_pid, sig, priv);
index ab0e091ce5facf0047c57191f9e631fd5c4bb791..4e5a73cdbbef18463920022626931d02c0540eb9 100644 (file)
@@ -86,9 +86,9 @@ extern int cap_inode_setxattr(struct dentry *dentry, const char *name,
 extern int cap_inode_removexattr(struct dentry *dentry, const char *name);
 extern int cap_inode_need_killpriv(struct dentry *dentry);
 extern int cap_inode_killpriv(struct dentry *dentry);
-extern int cap_file_mmap(struct file *file, unsigned long reqprot,
-                        unsigned long prot, unsigned long flags,
-                        unsigned long addr, unsigned long addr_only);
+extern int cap_mmap_addr(unsigned long addr);
+extern int cap_mmap_file(struct file *file, unsigned long reqprot,
+                        unsigned long prot, unsigned long flags);
 extern int cap_task_fix_setuid(struct cred *new, const struct cred *old, int flags);
 extern int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3,
                          unsigned long arg4, unsigned long arg5);
@@ -586,15 +586,17 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
  *     simple integer value.  When @arg represents a user space pointer, it
  *     should never be used by the security module.
  *     Return 0 if permission is granted.
- * @file_mmap :
+ * @mmap_addr :
+ *     Check permissions for a mmap operation at @addr.
+ *     @addr contains virtual address that will be used for the operation.
+ *     Return 0 if permission is granted.
+ * @mmap_file :
  *     Check permissions for a mmap operation.  The @file may be NULL, e.g.
  *     if mapping anonymous memory.
  *     @file contains the file structure for file to map (may be NULL).
  *     @reqprot contains the protection requested by the application.
  *     @prot contains the protection that will be applied by the kernel.
  *     @flags contains the operational flags.
- *     @addr contains virtual address that will be used for the operation.
- *     @addr_only contains a boolean: 0 if file-backed VMA, otherwise 1.
  *     Return 0 if permission is granted.
  * @file_mprotect:
  *     Check permissions before changing memory access permissions.
@@ -1481,10 +1483,10 @@ struct security_operations {
        void (*file_free_security) (struct file *file);
        int (*file_ioctl) (struct file *file, unsigned int cmd,
                           unsigned long arg);
-       int (*file_mmap) (struct file *file,
+       int (*mmap_addr) (unsigned long addr);
+       int (*mmap_file) (struct file *file,
                          unsigned long reqprot, unsigned long prot,
-                         unsigned long flags, unsigned long addr,
-                         unsigned long addr_only);
+                         unsigned long flags);
        int (*file_mprotect) (struct vm_area_struct *vma,
                              unsigned long reqprot,
                              unsigned long prot);
@@ -1743,9 +1745,9 @@ int security_file_permission(struct file *file, int mask);
 int security_file_alloc(struct file *file);
 void security_file_free(struct file *file);
 int security_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
-int security_file_mmap(struct file *file, unsigned long reqprot,
-                       unsigned long prot, unsigned long flags,
-                       unsigned long addr, unsigned long addr_only);
+int security_mmap_file(struct file *file, unsigned long prot,
+                       unsigned long flags);
+int security_mmap_addr(unsigned long addr);
 int security_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot,
                           unsigned long prot);
 int security_file_lock(struct file *file, unsigned int cmd);
@@ -2181,13 +2183,15 @@ static inline int security_file_ioctl(struct file *file, unsigned int cmd,
        return 0;
 }
 
-static inline int security_file_mmap(struct file *file, unsigned long reqprot,
-                                    unsigned long prot,
-                                    unsigned long flags,
-                                    unsigned long addr,
-                                    unsigned long addr_only)
+static inline int security_mmap_file(struct file *file, unsigned long prot,
+                                    unsigned long flags)
+{
+       return 0;
+}
+
+static inline int security_mmap_addr(unsigned long addr)
 {
-       return cap_file_mmap(file, reqprot, prot, flags, addr, addr_only);
+       return cap_mmap_addr(addr);
 }
 
 static inline int security_file_mprotect(struct vm_area_struct *vma,
index 17046cc484bced6426c36b81f43ce43fdf0ffde4..26b424adc84299b6a53c9ad986e4d9350d16b68a 100644 (file)
@@ -250,12 +250,13 @@ extern long do_sigpending(void __user *, unsigned long);
 extern int do_sigtimedwait(const sigset_t *, siginfo_t *,
                                const struct timespec *);
 extern int sigprocmask(int, sigset_t *, sigset_t *);
-extern void set_current_blocked(const sigset_t *);
+extern void set_current_blocked(sigset_t *);
+extern void __set_current_blocked(const sigset_t *);
 extern int show_unhandled_signals;
 extern int sigsuspend(sigset_t *);
 
 extern int get_signal_to_deliver(siginfo_t *info, struct k_sigaction *return_ka, struct pt_regs *regs, void *cookie);
-extern void block_sigmask(struct k_sigaction *ka, int signr);
+extern void signal_delivered(int sig, siginfo_t *info, struct k_sigaction *ka, struct pt_regs *regs, int stepping);
 extern void exit_signals(struct task_struct *tsk);
 
 extern struct kmem_cache *sighand_cachep;
index 0e501714d47fa1a885b79079d7eb82bbbb71d831..b534a1be540a0e254e39adf7443e2fdf3151b7f6 100644 (file)
@@ -1896,8 +1896,6 @@ static inline int __skb_cow(struct sk_buff *skb, unsigned int headroom,
 {
        int delta = 0;
 
-       if (headroom < NET_SKB_PAD)
-               headroom = NET_SKB_PAD;
        if (headroom > skb_headroom(skb))
                delta = headroom - skb_headroom(skb);
 
index a595dce6b0c7596d1481e2c87a2b55028c66a449..67d5d94b783a4b4ba97b53fc9d0adf9fd885af34 100644 (file)
@@ -242,7 +242,7 @@ size_t ksize(const void *);
  */
 static inline void *kmalloc_array(size_t n, size_t size, gfp_t flags)
 {
-       if (size != 0 && n > ULONG_MAX / size)
+       if (size != 0 && n > SIZE_MAX / size)
                return NULL;
        return __kmalloc(n * size, flags);
 }
index 51b29ac45a8e7b26583df0217ab37a0d939ad6da..40e0a273faea3c07470e19fd23673fda89543f9b 100644 (file)
@@ -232,7 +232,6 @@ struct svc_rqst {
        struct svc_pool *       rq_pool;        /* thread pool */
        struct svc_procedure *  rq_procinfo;    /* procedure info */
        struct auth_ops *       rq_authop;      /* authentication flavour */
-       u32                     rq_flavor;      /* pseudoflavor */
        struct svc_cred         rq_cred;        /* auth info */
        void *                  rq_xprt_ctxt;   /* transport specific context ptr */
        struct svc_deferred_req*rq_deferred;    /* deferred request we are replaying */
@@ -416,6 +415,7 @@ struct svc_procedure {
  */
 int svc_rpcb_setup(struct svc_serv *serv, struct net *net);
 void svc_rpcb_cleanup(struct svc_serv *serv, struct net *net);
+int svc_bind(struct svc_serv *serv, struct net *net);
 struct svc_serv *svc_create(struct svc_program *, unsigned int,
                            void (*shutdown)(struct svc_serv *, struct net *net));
 struct svc_rqst *svc_prepare_thread(struct svc_serv *serv,
index 548790e9113b317dbc8de0c46a691df3c0030269..dd74084a9799891309f54db25b8259ae3388f3c8 100644 (file)
 #include <linux/sunrpc/msg_prot.h>
 #include <linux/sunrpc/cache.h>
 #include <linux/hash.h>
+#include <linux/cred.h>
 
-#define SVC_CRED_NGROUPS       32
 struct svc_cred {
        uid_t                   cr_uid;
        gid_t                   cr_gid;
        struct group_info       *cr_group_info;
+       u32                     cr_flavor; /* pseudoflavor */
+       char                    *cr_principal; /* for gss */
 };
 
+static inline void free_svc_cred(struct svc_cred *cred)
+{
+       if (cred->cr_group_info)
+               put_group_info(cred->cr_group_info);
+       kfree(cred->cr_principal);
+}
+
 struct svc_rqst;               /* forward decl */
 struct in6_addr;
 
@@ -131,7 +140,7 @@ extern struct auth_domain *auth_domain_lookup(char *name, struct auth_domain *ne
 extern struct auth_domain *auth_domain_find(char *name);
 extern struct auth_domain *auth_unix_lookup(struct net *net, struct in6_addr *addr);
 extern int auth_unix_forget_old(struct auth_domain *dom);
-extern void svcauth_unix_purge(void);
+extern void svcauth_unix_purge(struct net *net);
 extern void svcauth_unix_info_release(struct svc_xprt *xpt);
 extern int svcauth_unix_set_client(struct svc_rqst *rqstp);
 
index 7c32daa025eb07b644d8185a27c8ea10d8b7c55f..726aff1a52011fcdfd3ab1e11b8a82ff1dbea703 100644 (file)
@@ -22,7 +22,6 @@ int gss_svc_init_net(struct net *net);
 void gss_svc_shutdown_net(struct net *net);
 int svcauth_gss_register_pseudoflavor(u32 pseudoflavor, char * name);
 u32 svcauth_gss_flavor(struct auth_domain *dom);
-char *svc_gss_principal(struct svc_rqst *);
 
 #endif /* __KERNEL__ */
 #endif /* _LINUX_SUNRPC_SVCAUTH_GSS_H */
index b1fd5c7925feab91948ee46f0b5140b4c0ae5399..b6661933e252643956cf3e8d389267f535d49653 100644 (file)
@@ -221,8 +221,8 @@ extern unsigned int nr_free_pagecache_pages(void);
 /* linux/mm/swap.c */
 extern void __lru_cache_add(struct page *, enum lru_list lru);
 extern void lru_cache_add_lru(struct page *, enum lru_list lru);
-extern void lru_add_page_tail(struct zone* zone,
-                             struct page *page, struct page *page_tail);
+extern void lru_add_page_tail(struct page *page, struct page *page_tail,
+                             struct lruvec *lruvec);
 extern void activate_page(struct page *);
 extern void mark_page_accessed(struct page *);
 extern void lru_add_drain(void);
@@ -251,7 +251,7 @@ static inline void lru_cache_add_file(struct page *page)
 /* linux/mm/vmscan.c */
 extern unsigned long try_to_free_pages(struct zonelist *zonelist, int order,
                                        gfp_t gfp_mask, nodemask_t *mask);
-extern int __isolate_lru_page(struct page *page, isolate_mode_t mode, int file);
+extern int __isolate_lru_page(struct page *page, isolate_mode_t mode);
 extern unsigned long try_to_free_mem_cgroup_pages(struct mem_cgroup *mem,
                                                  gfp_t gfp_mask, bool noswap);
 extern unsigned long mem_cgroup_shrink_node_zone(struct mem_cgroup *mem,
@@ -351,31 +351,14 @@ extern int swap_type_of(dev_t, sector_t, struct block_device **);
 extern unsigned int count_swap_pages(int, int);
 extern sector_t map_swap_page(struct page *, struct block_device **);
 extern sector_t swapdev_block(int, pgoff_t);
+extern int page_swapcount(struct page *);
 extern int reuse_swap_page(struct page *);
 extern int try_to_free_swap(struct page *);
 struct backing_dev_info;
 
-/* linux/mm/thrash.c */
-extern struct mm_struct *swap_token_mm;
-extern void grab_swap_token(struct mm_struct *);
-extern void __put_swap_token(struct mm_struct *);
-extern void disable_swap_token(struct mem_cgroup *memcg);
-
-static inline int has_swap_token(struct mm_struct *mm)
-{
-       return (mm == swap_token_mm);
-}
-
-static inline void put_swap_token(struct mm_struct *mm)
-{
-       if (has_swap_token(mm))
-               __put_swap_token(mm);
-}
-
 #ifdef CONFIG_CGROUP_MEM_RES_CTLR
 extern void
 mem_cgroup_uncharge_swapcache(struct page *page, swp_entry_t ent, bool swapout);
-extern int mem_cgroup_count_swap_user(swp_entry_t ent, struct page **pagep);
 #else
 static inline void
 mem_cgroup_uncharge_swapcache(struct page *page, swp_entry_t ent, bool swapout)
@@ -462,6 +445,11 @@ static inline void delete_from_swap_cache(struct page *page)
 {
 }
 
+static inline int page_swapcount(struct page *page)
+{
+       return 0;
+}
+
 #define reuse_swap_page(page)  (page_mapcount(page) == 1)
 
 static inline int try_to_free_swap(struct page *page)
@@ -476,37 +464,11 @@ static inline swp_entry_t get_swap_page(void)
        return entry;
 }
 
-/* linux/mm/thrash.c */
-static inline void put_swap_token(struct mm_struct *mm)
-{
-}
-
-static inline void grab_swap_token(struct mm_struct *mm)
-{
-}
-
-static inline int has_swap_token(struct mm_struct *mm)
-{
-       return 0;
-}
-
-static inline void disable_swap_token(struct mem_cgroup *memcg)
-{
-}
-
 static inline void
 mem_cgroup_uncharge_swapcache(struct page *page, swp_entry_t ent)
 {
 }
 
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR
-static inline int
-mem_cgroup_count_swap_user(swp_entry_t ent, struct page **pagep)
-{
-       return 0;
-}
-#endif
-
 #endif /* CONFIG_SWAP */
 #endif /* __KERNEL__*/
 #endif /* _LINUX_SWAP_H */
index 3de3acb84a952ead111b90391756873efc15ebcb..19439c75c5b255751e2467b5405861763f131fd5 100644 (file)
@@ -858,4 +858,6 @@ asmlinkage long sys_process_vm_writev(pid_t pid,
                                      unsigned long riovcnt,
                                      unsigned long flags);
 
+asmlinkage long sys_kcmp(pid_t pid1, pid_t pid2, int type,
+                        unsigned long idx1, unsigned long idx2);
 #endif
diff --git a/include/linux/task_work.h b/include/linux/task_work.h
new file mode 100644 (file)
index 0000000..294d5d5
--- /dev/null
@@ -0,0 +1,33 @@
+#ifndef _LINUX_TASK_WORK_H
+#define _LINUX_TASK_WORK_H
+
+#include <linux/list.h>
+#include <linux/sched.h>
+
+struct task_work;
+typedef void (*task_work_func_t)(struct task_work *);
+
+struct task_work {
+       struct hlist_node hlist;
+       task_work_func_t func;
+       void *data;
+};
+
+static inline void
+init_task_work(struct task_work *twork, task_work_func_t func, void *data)
+{
+       twork->func = func;
+       twork->data = data;
+}
+
+int task_work_add(struct task_struct *task, struct task_work *twork, bool);
+struct task_work *task_work_cancel(struct task_struct *, task_work_func_t);
+void task_work_run(void);
+
+static inline void exit_task_work(struct task_struct *task)
+{
+       if (unlikely(!hlist_empty(&task->task_works)))
+               task_work_run();
+}
+
+#endif /* _LINUX_TASK_WORK_H */
index db78775eff3b209b534d157d4cec7d90eec8b175..ccc1899bd62e991e4649b72e7145010f93f948f9 100644 (file)
@@ -8,6 +8,7 @@
 #define _LINUX_THREAD_INFO_H
 
 #include <linux/types.h>
+#include <linux/bug.h>
 
 struct timespec;
 struct compat_timespec;
@@ -125,10 +126,26 @@ static inline int test_ti_thread_flag(struct thread_info *ti, int flag)
 static inline void set_restore_sigmask(void)
 {
        set_thread_flag(TIF_RESTORE_SIGMASK);
-       set_thread_flag(TIF_SIGPENDING);
+       WARN_ON(!test_thread_flag(TIF_SIGPENDING));
+}
+static inline void clear_restore_sigmask(void)
+{
+       clear_thread_flag(TIF_RESTORE_SIGMASK);
+}
+static inline bool test_restore_sigmask(void)
+{
+       return test_thread_flag(TIF_RESTORE_SIGMASK);
+}
+static inline bool test_and_clear_restore_sigmask(void)
+{
+       return test_and_clear_thread_flag(TIF_RESTORE_SIGMASK);
 }
 #endif /* TIF_RESTORE_SIGMASK && !HAVE_SET_RESTORE_SIGMASK */
 
+#ifndef HAVE_SET_RESTORE_SIGMASK
+#error "no set_restore_sigmask() provided and default one won't work"
+#endif
+
 #endif /* __KERNEL__ */
 
 #endif /* _LINUX_THREAD_INFO_H */
index 51bd91d911c3b3233e90301a4092a84750827776..6a4d82bedb03d4f6e9742069c40324c0441265f0 100644 (file)
@@ -49,6 +49,7 @@
 #include <linux/sched.h>
 #include <linux/ptrace.h>
 #include <linux/security.h>
+#include <linux/task_work.h>
 struct linux_binprm;
 
 /*
@@ -153,7 +154,6 @@ static inline void tracehook_signal_handler(int sig, siginfo_t *info,
                ptrace_notify(SIGTRAP);
 }
 
-#ifdef TIF_NOTIFY_RESUME
 /**
  * set_notify_resume - cause tracehook_notify_resume() to be called
  * @task:              task that will call tracehook_notify_resume()
@@ -165,8 +165,10 @@ static inline void tracehook_signal_handler(int sig, siginfo_t *info,
  */
 static inline void set_notify_resume(struct task_struct *task)
 {
+#ifdef TIF_NOTIFY_RESUME
        if (!test_and_set_tsk_thread_flag(task, TIF_NOTIFY_RESUME))
                kick_process(task);
+#endif
 }
 
 /**
@@ -184,7 +186,14 @@ static inline void set_notify_resume(struct task_struct *task)
  */
 static inline void tracehook_notify_resume(struct pt_regs *regs)
 {
+       /*
+        * The caller just cleared TIF_NOTIFY_RESUME. This barrier
+        * pairs with task_work_add()->set_notify_resume() after
+        * hlist_add_head(task->task_works);
+        */
+       smp_mb__after_clear_bit();
+       if (unlikely(!hlist_empty(&current->task_works)))
+               task_work_run();
 }
-#endif /* TIF_NOTIFY_RESUME */
 
 #endif /* <linux/tracehook.h> */
index 7f480db60231a714b9e520f3a16856c5d4e4a5e1..9c1bd539ea70e780e0e926b54bfc9320d3ec34a4 100644 (file)
@@ -25,7 +25,7 @@ typedef __kernel_dev_t                dev_t;
 typedef __kernel_ino_t         ino_t;
 typedef __kernel_mode_t                mode_t;
 typedef unsigned short         umode_t;
-typedef __kernel_nlink_t       nlink_t;
+typedef __u32                  nlink_t;
 typedef __kernel_off_t         off_t;
 typedef __kernel_pid_t         pid_t;
 typedef __kernel_daddr_t       daddr_t;
index ac40716b44e9a2a9ee1c13d1e878a195ce7215b0..da70f0facd2b77215e79860e5af8104da30b03a2 100644 (file)
@@ -45,6 +45,8 @@ struct watchdog_info {
 #define        WDIOF_SETTIMEOUT        0x0080  /* Set timeout (in seconds) */
 #define        WDIOF_MAGICCLOSE        0x0100  /* Supports magic close char */
 #define        WDIOF_PRETIMEOUT        0x0200  /* Pretimeout (in seconds), get/set */
+#define        WDIOF_ALARMONLY         0x0400  /* Watchdog triggers a management or
+                                          other external alarm not a reboot */
 #define        WDIOF_KEEPALIVEPING     0x8000  /* Keep alive ping reply */
 
 #define        WDIOS_DISABLECARD       0x0001  /* Turn off the watchdog timer */
@@ -54,6 +56,8 @@ struct watchdog_info {
 #ifdef __KERNEL__
 
 #include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/cdev.h>
 
 struct watchdog_ops;
 struct watchdog_device;
@@ -67,6 +71,8 @@ struct watchdog_device;
  * @status:    The routine that shows the status of the watchdog device.
  * @set_timeout:The routine for setting the watchdog devices timeout value.
  * @get_timeleft:The routine that get's the time that's left before a reset.
+ * @ref:       The ref operation for dyn. allocated watchdog_device structs
+ * @unref:     The unref operation for dyn. allocated watchdog_device structs
  * @ioctl:     The routines that handles extra ioctl calls.
  *
  * The watchdog_ops structure contains a list of low-level operations
@@ -84,11 +90,17 @@ struct watchdog_ops {
        unsigned int (*status)(struct watchdog_device *);
        int (*set_timeout)(struct watchdog_device *, unsigned int);
        unsigned int (*get_timeleft)(struct watchdog_device *);
+       void (*ref)(struct watchdog_device *);
+       void (*unref)(struct watchdog_device *);
        long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long);
 };
 
 /** struct watchdog_device - The structure that defines a watchdog device
  *
+ * @id:                The watchdog's ID. (Allocated by watchdog_register_device)
+ * @cdev:      The watchdog's Character device.
+ * @dev:       The device for our watchdog
+ * @parent:    The parent bus device
  * @info:      Pointer to a watchdog_info structure.
  * @ops:       Pointer to the list of watchdog operations.
  * @bootstatus:        Status of the watchdog device at boot.
@@ -96,6 +108,7 @@ struct watchdog_ops {
  * @min_timeout:The watchdog devices minimum timeout value.
  * @max_timeout:The watchdog devices maximum timeout value.
  * @driver-data:Pointer to the drivers private data.
+ * @lock:      Lock for watchdog core internal use only.
  * @status:    Field that contains the devices internal status bits.
  *
  * The watchdog_device structure contains all information about a
@@ -103,8 +116,15 @@ struct watchdog_ops {
  *
  * The driver-data field may not be accessed directly. It must be accessed
  * via the watchdog_set_drvdata and watchdog_get_drvdata helpers.
+ *
+ * The lock field is for watchdog core internal use only and should not be
+ * touched.
  */
 struct watchdog_device {
+       int id;
+       struct cdev cdev;
+       struct device *dev;
+       struct device *parent;
        const struct watchdog_info *info;
        const struct watchdog_ops *ops;
        unsigned int bootstatus;
@@ -112,12 +132,14 @@ struct watchdog_device {
        unsigned int min_timeout;
        unsigned int max_timeout;
        void *driver_data;
+       struct mutex lock;
        unsigned long status;
 /* Bit numbers for status flags */
 #define WDOG_ACTIVE            0       /* Is the watchdog running/active */
 #define WDOG_DEV_OPEN          1       /* Opened via /dev/watchdog ? */
 #define WDOG_ALLOW_RELEASE     2       /* Did we receive the magic char ? */
 #define WDOG_NO_WAY_OUT                3       /* Is 'nowayout' feature set ? */
+#define WDOG_UNREGISTERED      4       /* Has the device been unregistered */
 };
 
 #ifdef CONFIG_WATCHDOG_NOWAYOUT
@@ -128,6 +150,12 @@ struct watchdog_device {
 #define WATCHDOG_NOWAYOUT_INIT_STATUS  0
 #endif
 
+/* Use the following function to check wether or not the watchdog is active */
+static inline bool watchdog_active(struct watchdog_device *wdd)
+{
+       return test_bit(WDOG_ACTIVE, &wdd->status);
+}
+
 /* Use the following function to set the nowayout feature */
 static inline void watchdog_set_nowayout(struct watchdog_device *wdd, bool nowayout)
 {
index bed833d9796aed86bac5ca7d45ad53cde3447e52..8197eadca819633eb97f3919286a7e6b19bbcddd 100644 (file)
@@ -60,6 +60,7 @@ struct dst_entry {
 #define DST_NOCOUNT            0x0020
 #define DST_NOPEER             0x0040
 #define DST_FAKE_RTABLE                0x0080
+#define DST_XFRM_TUNNEL                0x0100
 
        short                   error;
        short                   obsolete;
index d89f0582b6b6f1a907d108712ddf44eaa13b08d6..4a45216995635cccc4a919b5e5506a87fe90a49c 100644 (file)
@@ -46,6 +46,7 @@
 #include <linux/list_nulls.h>
 #include <linux/timer.h>
 #include <linux/cache.h>
+#include <linux/bitops.h>
 #include <linux/lockdep.h>
 #include <linux/netdevice.h>
 #include <linux/skbuff.h>      /* struct sk_buff */
@@ -921,12 +922,23 @@ struct proto {
 #endif
 };
 
+/*
+ * Bits in struct cg_proto.flags
+ */
+enum cg_proto_flags {
+       /* Currently active and new sockets should be assigned to cgroups */
+       MEMCG_SOCK_ACTIVE,
+       /* It was ever activated; we must disarm static keys on destruction */
+       MEMCG_SOCK_ACTIVATED,
+};
+
 struct cg_proto {
        void                    (*enter_memory_pressure)(struct sock *sk);
        struct res_counter      *memory_allocated;      /* Current allocated memory. */
        struct percpu_counter   *sockets_allocated;     /* Current number of sockets. */
        int                     *memory_pressure;
        long                    *sysctl_mem;
+       unsigned long           flags;
        /*
         * memcg field is used to find which memcg we belong directly
         * Each memcg struct can hold more than one cg_proto, so container_of
@@ -942,6 +954,16 @@ struct cg_proto {
 extern int proto_register(struct proto *prot, int alloc_slab);
 extern void proto_unregister(struct proto *prot);
 
+static inline bool memcg_proto_active(struct cg_proto *cg_proto)
+{
+       return test_bit(MEMCG_SOCK_ACTIVE, &cg_proto->flags);
+}
+
+static inline bool memcg_proto_activated(struct cg_proto *cg_proto)
+{
+       return test_bit(MEMCG_SOCK_ACTIVATED, &cg_proto->flags);
+}
+
 #ifdef SOCK_REFCNT_DEBUG
 static inline void sk_refcnt_debug_inc(struct sock *sk)
 {
diff --git a/include/scsi/fcoe_sysfs.h b/include/scsi/fcoe_sysfs.h
new file mode 100644 (file)
index 0000000..604cb9b
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2011-2012 Intel Corporation.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Maintained at www.Open-FCoE.org
+ */
+
+#ifndef FCOE_SYSFS
+#define FCOE_SYSFS
+
+#include <linux/if_ether.h>
+#include <linux/device.h>
+#include <scsi/fc/fc_fcoe.h>
+
+struct fcoe_ctlr_device;
+struct fcoe_fcf_device;
+
+struct fcoe_sysfs_function_template {
+       void (*get_fcoe_ctlr_link_fail)(struct fcoe_ctlr_device *);
+       void (*get_fcoe_ctlr_vlink_fail)(struct fcoe_ctlr_device *);
+       void (*get_fcoe_ctlr_miss_fka)(struct fcoe_ctlr_device *);
+       void (*get_fcoe_ctlr_symb_err)(struct fcoe_ctlr_device *);
+       void (*get_fcoe_ctlr_err_block)(struct fcoe_ctlr_device *);
+       void (*get_fcoe_ctlr_fcs_error)(struct fcoe_ctlr_device *);
+       void (*get_fcoe_ctlr_mode)(struct fcoe_ctlr_device *);
+       void (*get_fcoe_fcf_selected)(struct fcoe_fcf_device *);
+       void (*get_fcoe_fcf_vlan_id)(struct fcoe_fcf_device *);
+};
+
+#define dev_to_ctlr(d)                                 \
+       container_of((d), struct fcoe_ctlr_device, dev)
+
+enum fip_conn_type {
+       FIP_CONN_TYPE_UNKNOWN,
+       FIP_CONN_TYPE_FABRIC,
+       FIP_CONN_TYPE_VN2VN,
+};
+
+struct fcoe_ctlr_device {
+       u32                             id;
+
+       struct device                   dev;
+       struct fcoe_sysfs_function_template *f;
+
+       struct list_head                fcfs;
+       char                            work_q_name[20];
+       struct workqueue_struct         *work_q;
+       char                            devloss_work_q_name[20];
+       struct workqueue_struct         *devloss_work_q;
+       struct mutex                    lock;
+
+       int                             fcf_dev_loss_tmo;
+       enum fip_conn_type              mode;
+
+       /* expected in host order for displaying */
+       struct fcoe_fc_els_lesb         lesb;
+};
+
+static inline void *fcoe_ctlr_device_priv(const struct fcoe_ctlr_device *ctlr)
+{
+       return (void *)(ctlr + 1);
+}
+
+/* fcf states */
+enum fcf_state {
+       FCOE_FCF_STATE_UNKNOWN,
+       FCOE_FCF_STATE_DISCONNECTED,
+       FCOE_FCF_STATE_CONNECTED,
+       FCOE_FCF_STATE_DELETED,
+};
+
+struct fcoe_fcf_device {
+       u32                 id;
+       struct device       dev;
+       struct list_head    peers;
+       struct work_struct  delete_work;
+       struct delayed_work dev_loss_work;
+       u32                 dev_loss_tmo;
+       void                *priv;
+       enum fcf_state      state;
+
+       u64                 fabric_name;
+       u64                 switch_name;
+       u32                 fc_map;
+       u16                 vfid;
+       u8                  mac[ETH_ALEN];
+       u8                  priority;
+       u32                 fka_period;
+       u8                  selected;
+       u16                 vlan_id;
+};
+
+#define dev_to_fcf(d)                                  \
+       container_of((d), struct fcoe_fcf_device, dev)
+/* parentage should never be missing */
+#define fcoe_fcf_dev_to_ctlr_dev(x)            \
+       dev_to_ctlr((x)->dev.parent)
+#define fcoe_fcf_device_priv(x)                        \
+       ((x)->priv)
+
+struct fcoe_ctlr_device *fcoe_ctlr_device_add(struct device *parent,
+                           struct fcoe_sysfs_function_template *f,
+                           int priv_size);
+void fcoe_ctlr_device_delete(struct fcoe_ctlr_device *);
+struct fcoe_fcf_device *fcoe_fcf_device_add(struct fcoe_ctlr_device *,
+                                           struct fcoe_fcf_device *);
+void fcoe_fcf_device_delete(struct fcoe_fcf_device *);
+
+int __init fcoe_sysfs_setup(void);
+void __exit fcoe_sysfs_teardown(void);
+
+#endif /* FCOE_SYSFS */
index cfdb55f0937e37002d21be6eaf9f81833683aa4b..22b07cc99808562c86a3de2728ec0557291e56dc 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/random.h>
 #include <scsi/fc/fc_fcoe.h>
 #include <scsi/libfc.h>
+#include <scsi/fcoe_sysfs.h>
 
 #define FCOE_MAX_CMD_LEN       16      /* Supported CDB length */
 
@@ -158,9 +159,25 @@ struct fcoe_ctlr {
        spinlock_t ctlr_lock;
 };
 
+/**
+ * fcoe_ctlr_priv() - Return the private data from a fcoe_ctlr
+ * @cltr: The fcoe_ctlr whose private data will be returned
+ */
+static inline void *fcoe_ctlr_priv(const struct fcoe_ctlr *ctlr)
+{
+       return (void *)(ctlr + 1);
+}
+
+#define fcoe_ctlr_to_ctlr_dev(x)                                       \
+       (struct fcoe_ctlr_device *)(((struct fcoe_ctlr_device *)(x)) - 1)
+
 /**
  * struct fcoe_fcf - Fibre-Channel Forwarder
  * @list:       list linkage
+ * @event_work:  Work for FC Transport actions queue
+ * @event:       The event to be processed
+ * @fip:         The controller that the FCF was discovered on
+ * @fcf_dev:     The associated fcoe_fcf_device instance
  * @time:       system time (jiffies) when an advertisement was last received
  * @switch_name: WWN of switch from advertisement
  * @fabric_name: WWN of fabric from advertisement
@@ -182,6 +199,9 @@ struct fcoe_ctlr {
  */
 struct fcoe_fcf {
        struct list_head list;
+       struct work_struct event_work;
+       struct fcoe_ctlr *fip;
+       struct fcoe_fcf_device *fcf_dev;
        unsigned long time;
 
        u64 switch_name;
@@ -198,6 +218,9 @@ struct fcoe_fcf {
        u8 fd_flags:1;
 };
 
+#define fcoe_fcf_to_fcf_dev(x)                 \
+       ((x)->fcf_dev)
+
 /**
  * struct fcoe_rport - VN2VN remote port
  * @time:      time of create or last beacon packet received from node
@@ -333,6 +356,10 @@ void fcoe_queue_timer(ulong lport);
 int fcoe_get_paged_crc_eof(struct sk_buff *skb, int tlen,
                           struct fcoe_percpu_s *fps);
 
+/* FCoE Sysfs helpers */
+void fcoe_fcf_get_selected(struct fcoe_fcf_device *);
+void fcoe_ctlr_get_fip_mode(struct fcoe_ctlr_device *);
+
 /**
  * struct netdev_list
  * A mapping from netdevice to fcoe_transport
index f64560e204bc1f84cd6c8ba5e6ba7f1f6938c656..bab3b87e4064e9fd804182474127c2a84e49ef29 100644 (file)
@@ -13,7 +13,7 @@
 #define RECLAIM_WB_ANON                0x0001u
 #define RECLAIM_WB_FILE                0x0002u
 #define RECLAIM_WB_MIXED       0x0010u
-#define RECLAIM_WB_SYNC                0x0004u
+#define RECLAIM_WB_SYNC                0x0004u /* Unused, all reclaim async */
 #define RECLAIM_WB_ASYNC       0x0008u
 
 #define show_reclaim_flags(flags)                              \
                {RECLAIM_WB_ASYNC,      "RECLAIM_WB_ASYNC"}     \
                ) : "RECLAIM_WB_NONE"
 
-#define trace_reclaim_flags(page, sync) ( \
+#define trace_reclaim_flags(page) ( \
        (page_is_file_cache(page) ? RECLAIM_WB_FILE : RECLAIM_WB_ANON) | \
-       (sync & RECLAIM_MODE_SYNC ? RECLAIM_WB_SYNC : RECLAIM_WB_ASYNC)   \
+       (RECLAIM_WB_ASYNC) \
        )
 
-#define trace_shrink_flags(file, sync) ( \
-       (sync & RECLAIM_MODE_SYNC ? RECLAIM_WB_MIXED : \
-                       (file ? RECLAIM_WB_FILE : RECLAIM_WB_ANON)) |  \
-       (sync & RECLAIM_MODE_SYNC ? RECLAIM_WB_SYNC : RECLAIM_WB_ASYNC) \
+#define trace_shrink_flags(file) \
+       ( \
+               (file ? RECLAIM_WB_FILE : RECLAIM_WB_ANON) | \
+               (RECLAIM_WB_ASYNC) \
        )
 
 TRACE_EVENT(mm_vmscan_kswapd_sleep,
@@ -263,22 +263,16 @@ DECLARE_EVENT_CLASS(mm_vmscan_lru_isolate_template,
                unsigned long nr_requested,
                unsigned long nr_scanned,
                unsigned long nr_taken,
-               unsigned long nr_lumpy_taken,
-               unsigned long nr_lumpy_dirty,
-               unsigned long nr_lumpy_failed,
                isolate_mode_t isolate_mode,
                int file),
 
-       TP_ARGS(order, nr_requested, nr_scanned, nr_taken, nr_lumpy_taken, nr_lumpy_dirty, nr_lumpy_failed, isolate_mode, file),
+       TP_ARGS(order, nr_requested, nr_scanned, nr_taken, isolate_mode, file),
 
        TP_STRUCT__entry(
                __field(int, order)
                __field(unsigned long, nr_requested)
                __field(unsigned long, nr_scanned)
                __field(unsigned long, nr_taken)
-               __field(unsigned long, nr_lumpy_taken)
-               __field(unsigned long, nr_lumpy_dirty)
-               __field(unsigned long, nr_lumpy_failed)
                __field(isolate_mode_t, isolate_mode)
                __field(int, file)
        ),
@@ -288,22 +282,16 @@ DECLARE_EVENT_CLASS(mm_vmscan_lru_isolate_template,
                __entry->nr_requested = nr_requested;
                __entry->nr_scanned = nr_scanned;
                __entry->nr_taken = nr_taken;
-               __entry->nr_lumpy_taken = nr_lumpy_taken;
-               __entry->nr_lumpy_dirty = nr_lumpy_dirty;
-               __entry->nr_lumpy_failed = nr_lumpy_failed;
                __entry->isolate_mode = isolate_mode;
                __entry->file = file;
        ),
 
-       TP_printk("isolate_mode=%d order=%d nr_requested=%lu nr_scanned=%lu nr_taken=%lu contig_taken=%lu contig_dirty=%lu contig_failed=%lu file=%d",
+       TP_printk("isolate_mode=%d order=%d nr_requested=%lu nr_scanned=%lu nr_taken=%lu file=%d",
                __entry->isolate_mode,
                __entry->order,
                __entry->nr_requested,
                __entry->nr_scanned,
                __entry->nr_taken,
-               __entry->nr_lumpy_taken,
-               __entry->nr_lumpy_dirty,
-               __entry->nr_lumpy_failed,
                __entry->file)
 );
 
@@ -313,13 +301,10 @@ DEFINE_EVENT(mm_vmscan_lru_isolate_template, mm_vmscan_lru_isolate,
                unsigned long nr_requested,
                unsigned long nr_scanned,
                unsigned long nr_taken,
-               unsigned long nr_lumpy_taken,
-               unsigned long nr_lumpy_dirty,
-               unsigned long nr_lumpy_failed,
                isolate_mode_t isolate_mode,
                int file),
 
-       TP_ARGS(order, nr_requested, nr_scanned, nr_taken, nr_lumpy_taken, nr_lumpy_dirty, nr_lumpy_failed, isolate_mode, file)
+       TP_ARGS(order, nr_requested, nr_scanned, nr_taken, isolate_mode, file)
 
 );
 
@@ -329,13 +314,10 @@ DEFINE_EVENT(mm_vmscan_lru_isolate_template, mm_vmscan_memcg_isolate,
                unsigned long nr_requested,
                unsigned long nr_scanned,
                unsigned long nr_taken,
-               unsigned long nr_lumpy_taken,
-               unsigned long nr_lumpy_dirty,
-               unsigned long nr_lumpy_failed,
                isolate_mode_t isolate_mode,
                int file),
 
-       TP_ARGS(order, nr_requested, nr_scanned, nr_taken, nr_lumpy_taken, nr_lumpy_dirty, nr_lumpy_failed, isolate_mode, file)
+       TP_ARGS(order, nr_requested, nr_scanned, nr_taken, isolate_mode, file)
 
 );
 
@@ -395,88 +377,6 @@ TRACE_EVENT(mm_vmscan_lru_shrink_inactive,
                show_reclaim_flags(__entry->reclaim_flags))
 );
 
-TRACE_EVENT(replace_swap_token,
-       TP_PROTO(struct mm_struct *old_mm,
-                struct mm_struct *new_mm),
-
-       TP_ARGS(old_mm, new_mm),
-
-       TP_STRUCT__entry(
-               __field(struct mm_struct*,      old_mm)
-               __field(unsigned int,           old_prio)
-               __field(struct mm_struct*,      new_mm)
-               __field(unsigned int,           new_prio)
-       ),
-
-       TP_fast_assign(
-               __entry->old_mm   = old_mm;
-               __entry->old_prio = old_mm ? old_mm->token_priority : 0;
-               __entry->new_mm   = new_mm;
-               __entry->new_prio = new_mm->token_priority;
-       ),
-
-       TP_printk("old_token_mm=%p old_prio=%u new_token_mm=%p new_prio=%u",
-                 __entry->old_mm, __entry->old_prio,
-                 __entry->new_mm, __entry->new_prio)
-);
-
-DECLARE_EVENT_CLASS(put_swap_token_template,
-       TP_PROTO(struct mm_struct *swap_token_mm),
-
-       TP_ARGS(swap_token_mm),
-
-       TP_STRUCT__entry(
-               __field(struct mm_struct*, swap_token_mm)
-       ),
-
-       TP_fast_assign(
-               __entry->swap_token_mm = swap_token_mm;
-       ),
-
-       TP_printk("token_mm=%p", __entry->swap_token_mm)
-);
-
-DEFINE_EVENT(put_swap_token_template, put_swap_token,
-       TP_PROTO(struct mm_struct *swap_token_mm),
-       TP_ARGS(swap_token_mm)
-);
-
-DEFINE_EVENT_CONDITION(put_swap_token_template, disable_swap_token,
-       TP_PROTO(struct mm_struct *swap_token_mm),
-       TP_ARGS(swap_token_mm),
-       TP_CONDITION(swap_token_mm != NULL)
-);
-
-TRACE_EVENT_CONDITION(update_swap_token_priority,
-       TP_PROTO(struct mm_struct *mm,
-                unsigned int old_prio,
-                struct mm_struct *swap_token_mm),
-
-       TP_ARGS(mm, old_prio, swap_token_mm),
-
-       TP_CONDITION(mm->token_priority != old_prio),
-
-       TP_STRUCT__entry(
-               __field(struct mm_struct*, mm)
-               __field(unsigned int, old_prio)
-               __field(unsigned int, new_prio)
-               __field(struct mm_struct*, swap_token_mm)
-               __field(unsigned int, swap_token_prio)
-       ),
-
-       TP_fast_assign(
-               __entry->mm             = mm;
-               __entry->old_prio       = old_prio;
-               __entry->new_prio       = mm->token_priority;
-               __entry->swap_token_mm  = swap_token_mm;
-               __entry->swap_token_prio = swap_token_mm ? swap_token_mm->token_priority : 0;
-       ),
-
-       TP_printk("mm=%p old_prio=%u new_prio=%u swap_token_mm=%p token_prio=%u",
-                 __entry->mm, __entry->old_prio, __entry->new_prio,
-                 __entry->swap_token_mm, __entry->swap_token_prio)
-);
-
 #endif /* _TRACE_VMSCAN_H */
 
 /* This part must be outside protection */
index 81816b82860b51a7cb0a064d939d4702c4daaaed..d07dcf9fc8a9a8f05a570298d106c29e28f7167f 100644 (file)
@@ -167,7 +167,7 @@ config KERNEL_BZIP2
        depends on HAVE_KERNEL_BZIP2
        help
          Its compression ratio and speed is intermediate.
-         Decompression speed is slowest among the three.  The kernel
+         Decompression speed is slowest among the choices.  The kernel
          size is about 10% smaller with bzip2, in comparison to gzip.
          Bzip2 uses a large amount of memory. For modern kernels you
          will need at least 8MB RAM or more for booting.
@@ -176,10 +176,9 @@ config KERNEL_LZMA
        bool "LZMA"
        depends on HAVE_KERNEL_LZMA
        help
-         The most recent compression algorithm.
-         Its ratio is best, decompression speed is between the other
-         two. Compression is slowest.  The kernel size is about 33%
-         smaller with LZMA in comparison to gzip.
+         This compression algorithm's ratio is best.  Decompression speed
+         is between gzip and bzip2.  Compression is slowest.
+         The kernel size is about 33% smaller with LZMA in comparison to gzip.
 
 config KERNEL_XZ
        bool "XZ"
@@ -200,7 +199,7 @@ config KERNEL_LZO
        bool "LZO"
        depends on HAVE_KERNEL_LZO
        help
-         Its compression ratio is the poorest among the 4. The kernel
+         Its compression ratio is the poorest among the choices. The kernel
          size is about 10% bigger than gzip; however its speed
          (both compression and decompression) is the fastest.
 
@@ -803,7 +802,7 @@ config RT_GROUP_SCHED
 endif #CGROUP_SCHED
 
 config BLK_CGROUP
-       tristate "Block IO controller"
+       bool "Block IO controller"
        depends on BLOCK
        default n
        ---help---
index 42b0707c348108b98f6ce05ae1a98ad4f64a0e86..d3f0aeed2d39fe06aa07cb4147f747af8b7597ee 100644 (file)
@@ -1,3 +1,13 @@
+/*
+ * Many of the syscalls used in this file expect some of the arguments
+ * to be __user pointers not __kernel pointers.  To limit the sparse
+ * noise, turn off sparse checking for this file.
+ */
+#ifdef __CHECKER__
+#undef __CHECKER__
+#warning "Sparse checking disabled for this file"
+#endif
+
 #include <linux/module.h>
 #include <linux/sched.h>
 #include <linux/ctype.h>
@@ -330,7 +340,7 @@ static int __init do_mount_root(char *name, char *fs, int flags, void *data)
        if (err)
                return err;
 
-       sys_chdir((const char __user __force *)"/root");
+       sys_chdir("/root");
        s = current->fs->pwd.dentry->d_sb;
        ROOT_DEV = s->s_dev;
        printk(KERN_INFO
@@ -556,5 +566,5 @@ void __init prepare_namespace(void)
 out:
        devtmpfs_mount("dev");
        sys_mount(".", "/", NULL, MS_MOVE, NULL);
-       sys_chroot((const char __user __force *)".");
+       sys_chroot(".");
 }
index 9047330c73e9b8fed1098131513d6a8a1bdf0f8b..135959a276bef21628556119247be2e4d0d83610 100644 (file)
@@ -1,3 +1,13 @@
+/*
+ * Many of the syscalls used in this file expect some of the arguments
+ * to be __user pointers not __kernel pointers.  To limit the sparse
+ * noise, turn off sparse checking for this file.
+ */
+#ifdef __CHECKER__
+#undef __CHECKER__
+#warning "Sparse checking disabled for this file"
+#endif
+
 #include <linux/unistd.h>
 #include <linux/kernel.h>
 #include <linux/fs.h>
index 32c4799b8c91bb483f418cd6a39e1e11eda0cd7a..8cb6db54285ba64f81af9ba2b388a7c216ceec61 100644 (file)
@@ -1,3 +1,13 @@
+/*
+ * Many of the syscalls used in this file expect some of the arguments
+ * to be __user pointers not __kernel pointers.  To limit the sparse
+ * noise, turn off sparse checking for this file.
+ */
+#ifdef __CHECKER__
+#undef __CHECKER__
+#warning "Sparse checking disabled for this file"
+#endif
+
 #include <linux/delay.h>
 #include <linux/raid/md_u.h>
 #include <linux/raid/md_p.h>
@@ -283,7 +293,7 @@ static void __init autodetect_raid(void)
 
        wait_for_device_probe();
 
-       fd = sys_open((const char __user __force *) "/dev/md0", 0, 0);
+       fd = sys_open("/dev/md0", 0, 0);
        if (fd >= 0) {
                sys_ioctl(fd, RAID_AUTORUN, raid_autopart);
                sys_close(fd);
index 6212586df29ace81e239b71d01b95826cb636507..6be2879cca66971859b1c51ff80451616ab13975 100644 (file)
@@ -1,3 +1,12 @@
+/*
+ * Many of the syscalls used in this file expect some of the arguments
+ * to be __user pointers not __kernel pointers.  To limit the sparse
+ * noise, turn off sparse checking for this file.
+ */
+#ifdef __CHECKER__
+#undef __CHECKER__
+#warning "Sparse checking disabled for this file"
+#endif
 
 #include <linux/kernel.h>
 #include <linux/fs.h>
@@ -181,7 +190,7 @@ int __init rd_load_image(char *from)
        char rotator[4] = { '|' , '/' , '-' , '\\' };
 #endif
 
-       out_fd = sys_open((const char __user __force *) "/dev/ram", O_RDWR, 0);
+       out_fd = sys_open("/dev/ram", O_RDWR, 0);
        if (out_fd < 0)
                goto out;
 
@@ -280,7 +289,7 @@ noclose_input:
        sys_close(out_fd);
 out:
        kfree(buf);
-       sys_unlink((const char __user __force *) "/dev/ram");
+       sys_unlink("/dev/ram");
        return res;
 }
 
index 8216c303b0821b15f1a353a2fea7af84abb8f4bf..84c6bf111300878a095a9fb3f8f91678dbe10b65 100644 (file)
@@ -1,3 +1,13 @@
+/*
+ * Many of the syscalls used in this file expect some of the arguments
+ * to be __user pointers not __kernel pointers.  To limit the sparse
+ * noise, turn off sparse checking for this file.
+ */
+#ifdef __CHECKER__
+#undef __CHECKER__
+#warning "Sparse checking disabled for this file"
+#endif
+
 #include <linux/init.h>
 #include <linux/fs.h>
 #include <linux/slab.h>
@@ -74,7 +84,7 @@ static void __init free_hash(void)
        }
 }
 
-static long __init do_utime(char __user *filename, time_t mtime)
+static long __init do_utime(char *filename, time_t mtime)
 {
        struct timespec t[2];
 
@@ -529,7 +539,7 @@ static void __init clean_rootfs(void)
        struct linux_dirent64 *dirp;
        int num;
 
-       fd = sys_open((const char __user __force *) "/", O_RDONLY, 0);
+       fd = sys_open("/", O_RDONLY, 0);
        WARN_ON(fd < 0);
        if (fd < 0)
                return;
@@ -589,7 +599,7 @@ static int __init populate_rootfs(void)
                }
                printk(KERN_INFO "rootfs image is not initramfs (%s)"
                                "; looks like an initrd\n", err);
-               fd = sys_open((const char __user __force *) "/initrd.image",
+               fd = sys_open("/initrd.image",
                              O_WRONLY|O_CREAT, 0700);
                if (fd >= 0) {
                        sys_write(fd, (char *)initrd_start,
index 0c09366b96f3a634365c945c1a2d987a5afbf55e..383d638340b8417c8e31f53b935f8e8d833b2707 100644 (file)
 #include <linux/ipc_namespace.h>
 #include <linux/sysctl.h>
 
-/*
- * Define the ranges various user-specified maximum values can
- * be set to.
- */
-#define MIN_MSGMAX     1               /* min value for msg_max */
-#define MAX_MSGMAX     HARD_MSGMAX     /* max value for msg_max */
-#define MIN_MSGSIZEMAX 128             /* min value for msgsize_max */
-#define MAX_MSGSIZEMAX (8192*128)      /* max value for msgsize_max */
-
 #ifdef CONFIG_PROC_SYSCTL
 static void *get_mq(ctl_table *table)
 {
@@ -31,16 +22,6 @@ static void *get_mq(ctl_table *table)
        return which;
 }
 
-static int proc_mq_dointvec(ctl_table *table, int write,
-       void __user *buffer, size_t *lenp, loff_t *ppos)
-{
-       struct ctl_table mq_table;
-       memcpy(&mq_table, table, sizeof(mq_table));
-       mq_table.data = get_mq(table);
-
-       return proc_dointvec(&mq_table, write, buffer, lenp, ppos);
-}
-
 static int proc_mq_dointvec_minmax(ctl_table *table, int write,
        void __user *buffer, size_t *lenp, loff_t *ppos)
 {
@@ -52,15 +33,17 @@ static int proc_mq_dointvec_minmax(ctl_table *table, int write,
                                        lenp, ppos);
 }
 #else
-#define proc_mq_dointvec NULL
 #define proc_mq_dointvec_minmax NULL
 #endif
 
+static int msg_queues_limit_min = MIN_QUEUESMAX;
+static int msg_queues_limit_max = HARD_QUEUESMAX;
+
 static int msg_max_limit_min = MIN_MSGMAX;
-static int msg_max_limit_max = MAX_MSGMAX;
+static int msg_max_limit_max = HARD_MSGMAX;
 
 static int msg_maxsize_limit_min = MIN_MSGSIZEMAX;
-static int msg_maxsize_limit_max = MAX_MSGSIZEMAX;
+static int msg_maxsize_limit_max = HARD_MSGSIZEMAX;
 
 static ctl_table mq_sysctls[] = {
        {
@@ -68,7 +51,9 @@ static ctl_table mq_sysctls[] = {
                .data           = &init_ipc_ns.mq_queues_max,
                .maxlen         = sizeof(int),
                .mode           = 0644,
-               .proc_handler   = proc_mq_dointvec,
+               .proc_handler   = proc_mq_dointvec_minmax,
+               .extra1         = &msg_queues_limit_min,
+               .extra2         = &msg_queues_limit_max,
        },
        {
                .procname       = "msg_max",
@@ -88,6 +73,24 @@ static ctl_table mq_sysctls[] = {
                .extra1         = &msg_maxsize_limit_min,
                .extra2         = &msg_maxsize_limit_max,
        },
+       {
+               .procname       = "msg_default",
+               .data           = &init_ipc_ns.mq_msg_default,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_mq_dointvec_minmax,
+               .extra1         = &msg_max_limit_min,
+               .extra2         = &msg_max_limit_max,
+       },
+       {
+               .procname       = "msgsize_default",
+               .data           = &init_ipc_ns.mq_msgsize_default,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_mq_dointvec_minmax,
+               .extra1         = &msg_maxsize_limit_min,
+               .extra2         = &msg_maxsize_limit_max,
+       },
        {}
 };
 
index a2757d4ab7734dd7f177e695f66f454964e8222d..8ce57691e7b60994d9cc97620b7550c603df5391 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/mqueue.h>
 #include <linux/msg.h>
 #include <linux/skbuff.h>
+#include <linux/vmalloc.h>
 #include <linux/netlink.h>
 #include <linux/syscalls.h>
 #include <linux/audit.h>
 #define STATE_PENDING  1
 #define STATE_READY    2
 
+struct posix_msg_tree_node {
+       struct rb_node          rb_node;
+       struct list_head        msg_list;
+       int                     priority;
+};
+
 struct ext_wait_queue {                /* queue of sleeping tasks */
        struct task_struct *task;
        struct list_head list;
@@ -61,7 +68,8 @@ struct mqueue_inode_info {
        struct inode vfs_inode;
        wait_queue_head_t wait_q;
 
-       struct msg_msg **messages;
+       struct rb_root msg_tree;
+       struct posix_msg_tree_node *node_cache;
        struct mq_attr attr;
 
        struct sigevent notify;
@@ -109,6 +117,103 @@ static struct ipc_namespace *get_ns_from_inode(struct inode *inode)
        return ns;
 }
 
+/* Auxiliary functions to manipulate messages' list */
+static int msg_insert(struct msg_msg *msg, struct mqueue_inode_info *info)
+{
+       struct rb_node **p, *parent = NULL;
+       struct posix_msg_tree_node *leaf;
+
+       p = &info->msg_tree.rb_node;
+       while (*p) {
+               parent = *p;
+               leaf = rb_entry(parent, struct posix_msg_tree_node, rb_node);
+
+               if (likely(leaf->priority == msg->m_type))
+                       goto insert_msg;
+               else if (msg->m_type < leaf->priority)
+                       p = &(*p)->rb_left;
+               else
+                       p = &(*p)->rb_right;
+       }
+       if (info->node_cache) {
+               leaf = info->node_cache;
+               info->node_cache = NULL;
+       } else {
+               leaf = kmalloc(sizeof(*leaf), GFP_ATOMIC);
+               if (!leaf)
+                       return -ENOMEM;
+               rb_init_node(&leaf->rb_node);
+               INIT_LIST_HEAD(&leaf->msg_list);
+               info->qsize += sizeof(*leaf);
+       }
+       leaf->priority = msg->m_type;
+       rb_link_node(&leaf->rb_node, parent, p);
+       rb_insert_color(&leaf->rb_node, &info->msg_tree);
+insert_msg:
+       info->attr.mq_curmsgs++;
+       info->qsize += msg->m_ts;
+       list_add_tail(&msg->m_list, &leaf->msg_list);
+       return 0;
+}
+
+static inline struct msg_msg *msg_get(struct mqueue_inode_info *info)
+{
+       struct rb_node **p, *parent = NULL;
+       struct posix_msg_tree_node *leaf;
+       struct msg_msg *msg;
+
+try_again:
+       p = &info->msg_tree.rb_node;
+       while (*p) {
+               parent = *p;
+               /*
+                * During insert, low priorities go to the left and high to the
+                * right.  On receive, we want the highest priorities first, so
+                * walk all the way to the right.
+                */
+               p = &(*p)->rb_right;
+       }
+       if (!parent) {
+               if (info->attr.mq_curmsgs) {
+                       pr_warn_once("Inconsistency in POSIX message queue, "
+                                    "no tree element, but supposedly messages "
+                                    "should exist!\n");
+                       info->attr.mq_curmsgs = 0;
+               }
+               return NULL;
+       }
+       leaf = rb_entry(parent, struct posix_msg_tree_node, rb_node);
+       if (unlikely(list_empty(&leaf->msg_list))) {
+               pr_warn_once("Inconsistency in POSIX message queue, "
+                            "empty leaf node but we haven't implemented "
+                            "lazy leaf delete!\n");
+               rb_erase(&leaf->rb_node, &info->msg_tree);
+               if (info->node_cache) {
+                       info->qsize -= sizeof(*leaf);
+                       kfree(leaf);
+               } else {
+                       info->node_cache = leaf;
+               }
+               goto try_again;
+       } else {
+               msg = list_first_entry(&leaf->msg_list,
+                                      struct msg_msg, m_list);
+               list_del(&msg->m_list);
+               if (list_empty(&leaf->msg_list)) {
+                       rb_erase(&leaf->rb_node, &info->msg_tree);
+                       if (info->node_cache) {
+                               info->qsize -= sizeof(*leaf);
+                               kfree(leaf);
+                       } else {
+                               info->node_cache = leaf;
+                       }
+               }
+       }
+       info->attr.mq_curmsgs--;
+       info->qsize -= msg->m_ts;
+       return msg;
+}
+
 static struct inode *mqueue_get_inode(struct super_block *sb,
                struct ipc_namespace *ipc_ns, umode_t mode,
                struct mq_attr *attr)
@@ -129,7 +234,7 @@ static struct inode *mqueue_get_inode(struct super_block *sb,
 
        if (S_ISREG(mode)) {
                struct mqueue_inode_info *info;
-               unsigned long mq_bytes, mq_msg_tblsz;
+               unsigned long mq_bytes, mq_treesize;
 
                inode->i_fop = &mqueue_file_operations;
                inode->i_size = FILENT_SIZE;
@@ -143,20 +248,36 @@ static struct inode *mqueue_get_inode(struct super_block *sb,
                info->notify_user_ns = NULL;
                info->qsize = 0;
                info->user = NULL;      /* set when all is ok */
+               info->msg_tree = RB_ROOT;
+               info->node_cache = NULL;
                memset(&info->attr, 0, sizeof(info->attr));
-               info->attr.mq_maxmsg = ipc_ns->mq_msg_max;
-               info->attr.mq_msgsize = ipc_ns->mq_msgsize_max;
+               info->attr.mq_maxmsg = min(ipc_ns->mq_msg_max,
+                                          ipc_ns->mq_msg_default);
+               info->attr.mq_msgsize = min(ipc_ns->mq_msgsize_max,
+                                           ipc_ns->mq_msgsize_default);
                if (attr) {
                        info->attr.mq_maxmsg = attr->mq_maxmsg;
                        info->attr.mq_msgsize = attr->mq_msgsize;
                }
-               mq_msg_tblsz = info->attr.mq_maxmsg * sizeof(struct msg_msg *);
-               info->messages = kmalloc(mq_msg_tblsz, GFP_KERNEL);
-               if (!info->messages)
-                       goto out_inode;
+               /*
+                * We used to allocate a static array of pointers and account
+                * the size of that array as well as one msg_msg struct per
+                * possible message into the queue size. That's no longer
+                * accurate as the queue is now an rbtree and will grow and
+                * shrink depending on usage patterns.  We can, however, still
+                * account one msg_msg struct per message, but the nodes are
+                * allocated depending on priority usage, and most programs
+                * only use one, or a handful, of priorities.  However, since
+                * this is pinned memory, we need to assume worst case, so
+                * that means the min(mq_maxmsg, max_priorities) * struct
+                * posix_msg_tree_node.
+                */
+               mq_treesize = info->attr.mq_maxmsg * sizeof(struct msg_msg) +
+                       min_t(unsigned int, info->attr.mq_maxmsg, MQ_PRIO_MAX) *
+                       sizeof(struct posix_msg_tree_node);
 
-               mq_bytes = (mq_msg_tblsz +
-                       (info->attr.mq_maxmsg * info->attr.mq_msgsize));
+               mq_bytes = mq_treesize + (info->attr.mq_maxmsg *
+                                         info->attr.mq_msgsize);
 
                spin_lock(&mq_lock);
                if (u->mq_bytes + mq_bytes < u->mq_bytes ||
@@ -247,9 +368,9 @@ static void mqueue_evict_inode(struct inode *inode)
 {
        struct mqueue_inode_info *info;
        struct user_struct *user;
-       unsigned long mq_bytes;
-       int i;
+       unsigned long mq_bytes, mq_treesize;
        struct ipc_namespace *ipc_ns;
+       struct msg_msg *msg;
 
        clear_inode(inode);
 
@@ -259,14 +380,19 @@ static void mqueue_evict_inode(struct inode *inode)
        ipc_ns = get_ns_from_inode(inode);
        info = MQUEUE_I(inode);
        spin_lock(&info->lock);
-       for (i = 0; i < info->attr.mq_curmsgs; i++)
-               free_msg(info->messages[i]);
-       kfree(info->messages);
+       while ((msg = msg_get(info)) != NULL)
+               free_msg(msg);
+       kfree(info->node_cache);
        spin_unlock(&info->lock);
 
        /* Total amount of bytes accounted for the mqueue */
-       mq_bytes = info->attr.mq_maxmsg * (sizeof(struct msg_msg *)
-           + info->attr.mq_msgsize);
+       mq_treesize = info->attr.mq_maxmsg * sizeof(struct msg_msg) +
+               min_t(unsigned int, info->attr.mq_maxmsg, MQ_PRIO_MAX) *
+               sizeof(struct posix_msg_tree_node);
+
+       mq_bytes = mq_treesize + (info->attr.mq_maxmsg *
+                                 info->attr.mq_msgsize);
+
        user = info->user;
        if (user) {
                spin_lock(&mq_lock);
@@ -300,8 +426,9 @@ static int mqueue_create(struct inode *dir, struct dentry *dentry,
                error = -EACCES;
                goto out_unlock;
        }
-       if (ipc_ns->mq_queues_count >= ipc_ns->mq_queues_max &&
-                       !capable(CAP_SYS_RESOURCE)) {
+       if (ipc_ns->mq_queues_count >= HARD_QUEUESMAX ||
+           (ipc_ns->mq_queues_count >= ipc_ns->mq_queues_max &&
+            !capable(CAP_SYS_RESOURCE))) {
                error = -ENOSPC;
                goto out_unlock;
        }
@@ -485,26 +612,6 @@ static struct ext_wait_queue *wq_get_first_waiter(
        return list_entry(ptr, struct ext_wait_queue, list);
 }
 
-/* Auxiliary functions to manipulate messages' list */
-static void msg_insert(struct msg_msg *ptr, struct mqueue_inode_info *info)
-{
-       int k;
-
-       k = info->attr.mq_curmsgs - 1;
-       while (k >= 0 && info->messages[k]->m_type >= ptr->m_type) {
-               info->messages[k + 1] = info->messages[k];
-               k--;
-       }
-       info->attr.mq_curmsgs++;
-       info->qsize += ptr->m_ts;
-       info->messages[k + 1] = ptr;
-}
-
-static inline struct msg_msg *msg_get(struct mqueue_inode_info *info)
-{
-       info->qsize -= info->messages[--info->attr.mq_curmsgs]->m_ts;
-       return info->messages[info->attr.mq_curmsgs];
-}
 
 static inline void set_cookie(struct sk_buff *skb, char code)
 {
@@ -585,24 +692,30 @@ static void remove_notification(struct mqueue_inode_info *info)
 
 static int mq_attr_ok(struct ipc_namespace *ipc_ns, struct mq_attr *attr)
 {
+       int mq_treesize;
+       unsigned long total_size;
+
        if (attr->mq_maxmsg <= 0 || attr->mq_msgsize <= 0)
-               return 0;
+               return -EINVAL;
        if (capable(CAP_SYS_RESOURCE)) {
-               if (attr->mq_maxmsg > HARD_MSGMAX)
-                       return 0;
+               if (attr->mq_maxmsg > HARD_MSGMAX ||
+                   attr->mq_msgsize > HARD_MSGSIZEMAX)
+                       return -EINVAL;
        } else {
                if (attr->mq_maxmsg > ipc_ns->mq_msg_max ||
                                attr->mq_msgsize > ipc_ns->mq_msgsize_max)
-                       return 0;
+                       return -EINVAL;
        }
        /* check for overflow */
        if (attr->mq_msgsize > ULONG_MAX/attr->mq_maxmsg)
-               return 0;
-       if ((unsigned long)(attr->mq_maxmsg * (attr->mq_msgsize
-           + sizeof (struct msg_msg *))) <
-           (unsigned long)(attr->mq_maxmsg * attr->mq_msgsize))
-               return 0;
-       return 1;
+               return -EOVERFLOW;
+       mq_treesize = attr->mq_maxmsg * sizeof(struct msg_msg) +
+               min_t(unsigned int, attr->mq_maxmsg, MQ_PRIO_MAX) *
+               sizeof(struct posix_msg_tree_node);
+       total_size = attr->mq_maxmsg * attr->mq_msgsize;
+       if (total_size + mq_treesize < total_size)
+               return -EOVERFLOW;
+       return 0;
 }
 
 /*
@@ -617,12 +730,21 @@ static struct file *do_create(struct ipc_namespace *ipc_ns, struct dentry *dir,
        int ret;
 
        if (attr) {
-               if (!mq_attr_ok(ipc_ns, attr)) {
-                       ret = -EINVAL;
+               ret = mq_attr_ok(ipc_ns, attr);
+               if (ret)
                        goto out;
-               }
                /* store for use during create */
                dentry->d_fsdata = attr;
+       } else {
+               struct mq_attr def_attr;
+
+               def_attr.mq_maxmsg = min(ipc_ns->mq_msg_max,
+                                        ipc_ns->mq_msg_default);
+               def_attr.mq_msgsize = min(ipc_ns->mq_msgsize_max,
+                                         ipc_ns->mq_msgsize_default);
+               ret = mq_attr_ok(ipc_ns, &def_attr);
+               if (ret)
+                       goto out;
        }
 
        mode &= ~current_umask();
@@ -837,7 +959,8 @@ static inline void pipelined_receive(struct mqueue_inode_info *info)
                wake_up_interruptible(&info->wait_q);
                return;
        }
-       msg_insert(sender->msg, info);
+       if (msg_insert(sender->msg, info))
+               return;
        list_del(&sender->list);
        sender->state = STATE_PENDING;
        wake_up_process(sender->task);
@@ -857,7 +980,8 @@ SYSCALL_DEFINE5(mq_timedsend, mqd_t, mqdes, const char __user *, u_msg_ptr,
        struct mqueue_inode_info *info;
        ktime_t expires, *timeout = NULL;
        struct timespec ts;
-       int ret;
+       struct posix_msg_tree_node *new_leaf = NULL;
+       int ret = 0;
 
        if (u_abs_timeout) {
                int res = prepare_timeout(u_abs_timeout, &expires, &ts);
@@ -905,34 +1029,60 @@ SYSCALL_DEFINE5(mq_timedsend, mqd_t, mqdes, const char __user *, u_msg_ptr,
        msg_ptr->m_ts = msg_len;
        msg_ptr->m_type = msg_prio;
 
+       /*
+        * msg_insert really wants us to have a valid, spare node struct so
+        * it doesn't have to kmalloc a GFP_ATOMIC allocation, but it will
+        * fall back to that if necessary.
+        */
+       if (!info->node_cache)
+               new_leaf = kmalloc(sizeof(*new_leaf), GFP_KERNEL);
+
        spin_lock(&info->lock);
 
+       if (!info->node_cache && new_leaf) {
+               /* Save our speculative allocation into the cache */
+               rb_init_node(&new_leaf->rb_node);
+               INIT_LIST_HEAD(&new_leaf->msg_list);
+               info->node_cache = new_leaf;
+               info->qsize += sizeof(*new_leaf);
+               new_leaf = NULL;
+       } else {
+               kfree(new_leaf);
+       }
+
        if (info->attr.mq_curmsgs == info->attr.mq_maxmsg) {
                if (filp->f_flags & O_NONBLOCK) {
-                       spin_unlock(&info->lock);
                        ret = -EAGAIN;
                } else {
                        wait.task = current;
                        wait.msg = (void *) msg_ptr;
                        wait.state = STATE_NONE;
                        ret = wq_sleep(info, SEND, timeout, &wait);
+                       /*
+                        * wq_sleep must be called with info->lock held, and
+                        * returns with the lock released
+                        */
+                       goto out_free;
                }
-               if (ret < 0)
-                       free_msg(msg_ptr);
        } else {
                receiver = wq_get_first_waiter(info, RECV);
                if (receiver) {
                        pipelined_send(info, msg_ptr, receiver);
                } else {
                        /* adds message to the queue */
-                       msg_insert(msg_ptr, info);
+                       ret = msg_insert(msg_ptr, info);
+                       if (ret)
+                               goto out_unlock;
                        __do_notify(info);
                }
                inode->i_atime = inode->i_mtime = inode->i_ctime =
                                CURRENT_TIME;
-               spin_unlock(&info->lock);
-               ret = 0;
        }
+out_unlock:
+       spin_unlock(&info->lock);
+out_free:
+       if (ret)
+               free_msg(msg_ptr);
 out_fput:
        fput(filp);
 out:
@@ -951,6 +1101,7 @@ SYSCALL_DEFINE5(mq_timedreceive, mqd_t, mqdes, char __user *, u_msg_ptr,
        struct ext_wait_queue wait;
        ktime_t expires, *timeout = NULL;
        struct timespec ts;
+       struct posix_msg_tree_node *new_leaf = NULL;
 
        if (u_abs_timeout) {
                int res = prepare_timeout(u_abs_timeout, &expires, &ts);
@@ -986,7 +1137,26 @@ SYSCALL_DEFINE5(mq_timedreceive, mqd_t, mqdes, char __user *, u_msg_ptr,
                goto out_fput;
        }
 
+       /*
+        * msg_insert really wants us to have a valid, spare node struct so
+        * it doesn't have to kmalloc a GFP_ATOMIC allocation, but it will
+        * fall back to that if necessary.
+        */
+       if (!info->node_cache)
+               new_leaf = kmalloc(sizeof(*new_leaf), GFP_KERNEL);
+
        spin_lock(&info->lock);
+
+       if (!info->node_cache && new_leaf) {
+               /* Save our speculative allocation into the cache */
+               rb_init_node(&new_leaf->rb_node);
+               INIT_LIST_HEAD(&new_leaf->msg_list);
+               info->node_cache = new_leaf;
+               info->qsize += sizeof(*new_leaf);
+       } else {
+               kfree(new_leaf);
+       }
+
        if (info->attr.mq_curmsgs == 0) {
                if (filp->f_flags & O_NONBLOCK) {
                        spin_unlock(&info->lock);
@@ -1251,6 +1421,8 @@ int mq_init_ns(struct ipc_namespace *ns)
        ns->mq_queues_max    = DFLT_QUEUESMAX;
        ns->mq_msg_max       = DFLT_MSGMAX;
        ns->mq_msgsize_max   = DFLT_MSGSIZEMAX;
+       ns->mq_msg_default   = DFLT_MSG;
+       ns->mq_msgsize_default  = DFLT_MSGSIZE;
 
        ns->mq_mnt = kern_mount_data(&mqueue_fs_type, ns);
        if (IS_ERR(ns->mq_mnt)) {
index 406c5b208193373b979ce82bffe6617250ea64ed..5e2cbfdab6fc0d6b96a19c321a9208dda8cd130d 100644 (file)
--- a/ipc/shm.c
+++ b/ipc/shm.c
@@ -1036,6 +1036,10 @@ long do_shmat(int shmid, char __user *shmaddr, int shmflg, ulong *raddr)
        sfd->file = shp->shm_file;
        sfd->vm_ops = NULL;
 
+       err = security_mmap_file(file, prot, flags);
+       if (err)
+               goto out_fput;
+
        down_write(&current->mm->mmap_sem);
        if (addr && !(shmflg & SHM_REMAP)) {
                err = -EINVAL;
@@ -1050,7 +1054,7 @@ long do_shmat(int shmid, char __user *shmaddr, int shmflg, ulong *raddr)
                        goto invalid;
        }
                
-       user_addr = do_mmap (file, addr, size, prot, flags, 0);
+       user_addr = do_mmap_pgoff(file, addr, size, prot, flags, 0);
        *raddr = user_addr;
        err = 0;
        if (IS_ERR_VALUE(user_addr))
@@ -1058,6 +1062,7 @@ long do_shmat(int shmid, char __user *shmaddr, int shmflg, ulong *raddr)
 invalid:
        up_write(&current->mm->mmap_sem);
 
+out_fput:
        fput(file);
 
 out_nattch:
index 6c07f30fa9b7e678e23b2038474a5b508b7fa661..c0cc67ad764ceddbe9f226ee1bfb90c4055f19ff 100644 (file)
@@ -5,12 +5,12 @@
 obj-y     = fork.o exec_domain.o panic.o printk.o \
            cpu.o exit.o itimer.o time.o softirq.o resource.o \
            sysctl.o sysctl_binary.o capability.o ptrace.o timer.o user.o \
-           signal.o sys.o kmod.o workqueue.o pid.o \
+           signal.o sys.o kmod.o workqueue.o pid.o task_work.o \
            rcupdate.o extable.o params.o posix-timers.o \
            kthread.o wait.o kfifo.o sys_ni.o posix-cpu-timers.o mutex.o \
            hrtimer.o rwsem.o nsproxy.o srcu.o semaphore.o \
            notifier.o ksysfs.o cred.o \
-           async.o range.o groups.o
+           async.o range.o groups.o lglock.o
 
 ifdef CONFIG_FUNCTION_TRACER
 # Do not trace debug files and internal ftrace files
@@ -25,6 +25,9 @@ endif
 obj-y += sched/
 obj-y += power/
 
+ifeq ($(CONFIG_CHECKPOINT_RESTORE),y)
+obj-$(CONFIG_X86) += kcmp.o
+endif
 obj-$(CONFIG_FREEZER) += freezer.o
 obj-$(CONFIG_PROFILING) += profile.o
 obj-$(CONFIG_STACKTRACE) += stacktrace.o
index a0c6af34d50063b31f6bd1fd1fc1de8b299d10ce..0f3527d6184a1597fb81f00a878df15eeab0ecf8 100644 (file)
@@ -5132,7 +5132,7 @@ EXPORT_SYMBOL_GPL(css_depth);
  * @root: the css supporsed to be an ancestor of the child.
  *
  * Returns true if "root" is an ancestor of "child" in its hierarchy. Because
- * this function reads css->id, this use rcu_dereference() and rcu_read_lock().
+ * this function reads css->id, the caller must hold rcu_read_lock().
  * But, considering usual usage, the csses should be valid objects after test.
  * Assuming that the caller will do some action to the child if this returns
  * returns true, the caller must take "child";s reference count.
@@ -5144,18 +5144,18 @@ bool css_is_ancestor(struct cgroup_subsys_state *child,
 {
        struct css_id *child_id;
        struct css_id *root_id;
-       bool ret = true;
 
-       rcu_read_lock();
        child_id  = rcu_dereference(child->id);
+       if (!child_id)
+               return false;
        root_id = rcu_dereference(root->id);
-       if (!child_id
-           || !root_id
-           || (child_id->depth < root_id->depth)
-           || (child_id->stack[root_id->depth] != root_id->id))
-               ret = false;
-       rcu_read_unlock();
-       return ret;
+       if (!root_id)
+               return false;
+       if (child_id->depth < root_id->depth)
+               return false;
+       if (child_id->stack[root_id->depth] != root_id->id)
+               return false;
+       return true;
 }
 
 void free_css_id(struct cgroup_subsys *ss, struct cgroup_subsys_state *css)
index 0e6353cf147abf51c4f760054971915063ece928..a4eb5227a19e482eaf2b821c94de845228381bdd 100644 (file)
 #include <linux/sched.h>
 #include <linux/unistd.h>
 #include <linux/cpu.h>
+#include <linux/oom.h>
+#include <linux/rcupdate.h>
 #include <linux/export.h>
+#include <linux/bug.h>
 #include <linux/kthread.h>
 #include <linux/stop_machine.h>
 #include <linux/mutex.h>
@@ -173,6 +176,47 @@ void __ref unregister_cpu_notifier(struct notifier_block *nb)
 }
 EXPORT_SYMBOL(unregister_cpu_notifier);
 
+/**
+ * clear_tasks_mm_cpumask - Safely clear tasks' mm_cpumask for a CPU
+ * @cpu: a CPU id
+ *
+ * This function walks all processes, finds a valid mm struct for each one and
+ * then clears a corresponding bit in mm's cpumask.  While this all sounds
+ * trivial, there are various non-obvious corner cases, which this function
+ * tries to solve in a safe manner.
+ *
+ * Also note that the function uses a somewhat relaxed locking scheme, so it may
+ * be called only for an already offlined CPU.
+ */
+void clear_tasks_mm_cpumask(int cpu)
+{
+       struct task_struct *p;
+
+       /*
+        * This function is called after the cpu is taken down and marked
+        * offline, so its not like new tasks will ever get this cpu set in
+        * their mm mask. -- Peter Zijlstra
+        * Thus, we may use rcu_read_lock() here, instead of grabbing
+        * full-fledged tasklist_lock.
+        */
+       WARN_ON(cpu_online(cpu));
+       rcu_read_lock();
+       for_each_process(p) {
+               struct task_struct *t;
+
+               /*
+                * Main thread might exit, but other threads may still have
+                * a valid mm. Find one.
+                */
+               t = find_lock_task_mm(p);
+               if (!t)
+                       continue;
+               cpumask_clear_cpu(cpu, mm_cpumask(t->mm));
+               task_unlock(t);
+       }
+       rcu_read_unlock();
+}
+
 static inline void check_for_tasks(int cpu)
 {
        struct task_struct *p;
index 249152e15308c9d3a36922c2d10989a961e221d9..9656a3c36503dee343813149bbf1153bb6aea05a 100644 (file)
@@ -81,7 +81,7 @@ int cpu_pm_unregister_notifier(struct notifier_block *nb)
 EXPORT_SYMBOL_GPL(cpu_pm_unregister_notifier);
 
 /**
- * cpm_pm_enter - CPU low power entry notifier
+ * cpu_pm_enter - CPU low power entry notifier
  *
  * Notifies listeners that a single CPU is entering a low power state that may
  * cause some blocks in the same power domain as the cpu to reset.
@@ -89,7 +89,7 @@ EXPORT_SYMBOL_GPL(cpu_pm_unregister_notifier);
  * Must be called on the affected CPU with interrupts disabled.  Platform is
  * responsible for ensuring that cpu_pm_enter is not called twice on the same
  * CPU before cpu_pm_exit is called. Notified drivers can include VFP
- * co-processor, interrupt controller and it's PM extensions, local CPU
+ * co-processor, interrupt controller and its PM extensions, local CPU
  * timers context save/restore which shouldn't be interrupted. Hence it
  * must be called with interrupts disabled.
  *
@@ -115,13 +115,13 @@ int cpu_pm_enter(void)
 EXPORT_SYMBOL_GPL(cpu_pm_enter);
 
 /**
- * cpm_pm_exit - CPU low power exit notifier
+ * cpu_pm_exit - CPU low power exit notifier
  *
  * Notifies listeners that a single CPU is exiting a low power state that may
  * have caused some blocks in the same power domain as the cpu to reset.
  *
  * Notified drivers can include VFP co-processor, interrupt controller
- * and it's PM extensions, local CPU timers context save/restore which
+ * and its PM extensions, local CPU timers context save/restore which
  * shouldn't be interrupted. Hence it must be called with interrupts disabled.
  *
  * Return conditions are same as __raw_notifier_call_chain.
@@ -139,7 +139,7 @@ int cpu_pm_exit(void)
 EXPORT_SYMBOL_GPL(cpu_pm_exit);
 
 /**
- * cpm_cluster_pm_enter - CPU cluster low power entry notifier
+ * cpu_cluster_pm_enter - CPU cluster low power entry notifier
  *
  * Notifies listeners that all cpus in a power domain are entering a low power
  * state that may cause some blocks in the same power domain to reset.
@@ -147,7 +147,7 @@ EXPORT_SYMBOL_GPL(cpu_pm_exit);
  * Must be called after cpu_pm_enter has been called on all cpus in the power
  * domain, and before cpu_pm_exit has been called on any cpu in the power
  * domain. Notified drivers can include VFP co-processor, interrupt controller
- * and it's PM extensions, local CPU timers context save/restore which
+ * and its PM extensions, local CPU timers context save/restore which
  * shouldn't be interrupted. Hence it must be called with interrupts disabled.
  *
  * Must be called with interrupts disabled.
@@ -174,7 +174,7 @@ int cpu_cluster_pm_enter(void)
 EXPORT_SYMBOL_GPL(cpu_cluster_pm_enter);
 
 /**
- * cpm_cluster_pm_exit - CPU cluster low power exit notifier
+ * cpu_cluster_pm_exit - CPU cluster low power exit notifier
  *
  * Notifies listeners that all cpus in a power domain are exiting form a
  * low power state that may have caused some blocks in the same power domain
@@ -183,7 +183,7 @@ EXPORT_SYMBOL_GPL(cpu_cluster_pm_enter);
  * Must be called after cpu_pm_exit has been called on all cpus in the power
  * domain, and before cpu_pm_exit has been called on any cpu in the power
  * domain. Notified drivers can include VFP co-processor, interrupt controller
- * and it's PM extensions, local CPU timers context save/restore which
+ * and its PM extensions, local CPU timers context save/restore which
  * shouldn't be interrupted. Hence it must be called with interrupts disabled.
  *
  * Return conditions are same as __raw_notifier_call_chain.
index 430557ea488f3625243455afcdd6b2f9f481ac19..de728ac50d821b9f38340534a4ba6202137d55a2 100644 (file)
@@ -207,13 +207,6 @@ void exit_creds(struct task_struct *tsk)
        validate_creds(cred);
        alter_cred_subscribers(cred, -1);
        put_cred(cred);
-
-       cred = (struct cred *) tsk->replacement_session_keyring;
-       if (cred) {
-               tsk->replacement_session_keyring = NULL;
-               validate_creds(cred);
-               put_cred(cred);
-       }
 }
 
 /**
@@ -396,8 +389,6 @@ int copy_creds(struct task_struct *p, unsigned long clone_flags)
        struct cred *new;
        int ret;
 
-       p->replacement_session_keyring = NULL;
-
        if (
 #ifdef CONFIG_KEYS
                !p->cred->thread_keyring &&
index 910a0716e17ab4124ddd07f22d502e19ed9f30de..34867cc5b42a77f325c204bb2fd09e1fabe38955 100644 (file)
@@ -884,9 +884,9 @@ static void check_stack_usage(void)
 
        spin_lock(&low_water_lock);
        if (free < lowest_to_date) {
-               printk(KERN_WARNING "%s used greatest stack depth: %lu bytes "
-                               "left\n",
-                               current->comm, free);
+               printk(KERN_WARNING "%s (%d) used greatest stack depth: "
+                               "%lu bytes left\n",
+                               current->comm, task_pid_nr(current), free);
                lowest_to_date = free;
        }
        spin_unlock(&low_water_lock);
@@ -946,12 +946,13 @@ void do_exit(long code)
        exit_signals(tsk);  /* sets PF_EXITING */
        /*
         * tsk->flags are checked in the futex code to protect against
-        * an exiting task cleaning up the robust pi futexes.
+        * an exiting task cleaning up the robust pi futexes, and in
+        * task_work_add() to avoid the race with exit_task_work().
         */
        smp_mb();
        raw_spin_unlock_wait(&tsk->pi_lock);
 
-       exit_irq_thread();
+       exit_task_work(tsk);
 
        if (unlikely(in_atomic()))
                printk(KERN_INFO "note: %s[%d] exited with preempt_count %d\n",
@@ -1214,7 +1215,7 @@ static int wait_task_zombie(struct wait_opts *wo, struct task_struct *p)
        unsigned long state;
        int retval, status, traced;
        pid_t pid = task_pid_vnr(p);
-       uid_t uid = from_kuid_munged(current_user_ns(), __task_cred(p)->uid);
+       uid_t uid = from_kuid_munged(current_user_ns(), task_uid(p));
        struct siginfo __user *infop;
 
        if (!likely(wo->wo_flags & WEXITED))
index 47b4e4f379f94c2b726aa9babdcbbd26508e8dc1..ab5211b9e622cf94d07b7bfb4ccfd9bac85e7b79 100644 (file)
@@ -386,7 +386,8 @@ static int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm)
                }
                charge = 0;
                if (mpnt->vm_flags & VM_ACCOUNT) {
-                       unsigned int len = (mpnt->vm_end - mpnt->vm_start) >> PAGE_SHIFT;
+                       unsigned long len;
+                       len = (mpnt->vm_end - mpnt->vm_start) >> PAGE_SHIFT;
                        if (security_vm_enough_memory_mm(oldmm, len)) /* sic */
                                goto fail_nomem;
                        charge = len;
@@ -614,7 +615,6 @@ void mmput(struct mm_struct *mm)
                        list_del(&mm->mmlist);
                        spin_unlock(&mmlist_lock);
                }
-               put_swap_token(mm);
                if (mm->binfmt)
                        module_put(mm->binfmt->module);
                mmdrop(mm);
@@ -787,9 +787,6 @@ void mm_release(struct task_struct *tsk, struct mm_struct *mm)
        /* Get rid of any cached register state */
        deactivate_mm(tsk, mm);
 
-       if (tsk->vfork_done)
-               complete_vfork_done(tsk);
-
        /*
         * If we're exiting normally, clear a user-space tid field if
         * requested.  We leave this alone when dying by signal, to leave
@@ -810,6 +807,13 @@ void mm_release(struct task_struct *tsk, struct mm_struct *mm)
                }
                tsk->clear_child_tid = NULL;
        }
+
+       /*
+        * All done, finally we can wake up parent and return this mm to him.
+        * Also kthread_stop() uses this completion for synchronization.
+        */
+       if (tsk->vfork_done)
+               complete_vfork_done(tsk);
 }
 
 /*
@@ -831,10 +835,6 @@ struct mm_struct *dup_mm(struct task_struct *tsk)
        memcpy(mm, oldmm, sizeof(*mm));
        mm_init_cpumask(mm);
 
-       /* Initializing for Swap token stuff */
-       mm->token_priority = 0;
-       mm->last_interval = 0;
-
 #ifdef CONFIG_TRANSPARENT_HUGEPAGE
        mm->pmd_huge_pte = NULL;
 #endif
@@ -913,10 +913,6 @@ static int copy_mm(unsigned long clone_flags, struct task_struct *tsk)
                goto fail_nomem;
 
 good_mm:
-       /* Initializing for Swap token stuff */
-       mm->token_priority = 0;
-       mm->last_interval = 0;
-
        tsk->mm = mm;
        tsk->active_mm = mm;
        return 0;
@@ -984,9 +980,8 @@ static int copy_io(unsigned long clone_flags, struct task_struct *tsk)
         * Share io context with parent, if CLONE_IO is set
         */
        if (clone_flags & CLONE_IO) {
-               tsk->io_context = ioc_task_link(ioc);
-               if (unlikely(!tsk->io_context))
-                       return -ENOMEM;
+               ioc_task_link(ioc);
+               tsk->io_context = ioc;
        } else if (ioprio_valid(ioc->ioprio)) {
                new_ioc = get_task_io_context(tsk, GFP_KERNEL, NUMA_NO_NODE);
                if (unlikely(!new_ioc))
@@ -1420,6 +1415,7 @@ static struct task_struct *copy_process(unsigned long clone_flags,
         */
        p->group_leader = p;
        INIT_LIST_HEAD(&p->thread_group);
+       INIT_HLIST_HEAD(&p->task_works);
 
        /* Now that the task is set up, run cgroup callbacks if
         * necessary. We need to run them before the task is visible
index bb32326afe8796be6ebbf240fbd3ef4bb1fc8cbe..ea0c6c2ae6f747d0bd8dff198e5e1f6b6934f050 100644 (file)
@@ -7,6 +7,8 @@
  * This file contains driver APIs to the irq subsystem.
  */
 
+#define pr_fmt(fmt) "genirq: " fmt
+
 #include <linux/irq.h>
 #include <linux/kthread.h>
 #include <linux/module.h>
@@ -14,6 +16,7 @@
 #include <linux/interrupt.h>
 #include <linux/slab.h>
 #include <linux/sched.h>
+#include <linux/task_work.h>
 
 #include "internals.h"
 
@@ -565,7 +568,7 @@ int __irq_set_trigger(struct irq_desc *desc, unsigned int irq,
                 * IRQF_TRIGGER_* but the PIC does not support multiple
                 * flow-types?
                 */
-               pr_debug("genirq: No set_type function for IRQ %d (%s)\n", irq,
+               pr_debug("No set_type function for IRQ %d (%s)\n", irq,
                         chip ? (chip->name ? : "unknown") : "unknown");
                return 0;
        }
@@ -600,7 +603,7 @@ int __irq_set_trigger(struct irq_desc *desc, unsigned int irq,
                ret = 0;
                break;
        default:
-               pr_err("genirq: Setting trigger mode %lu for irq %u failed (%pF)\n",
+               pr_err("Setting trigger mode %lu for irq %u failed (%pF)\n",
                       flags, irq, chip->irq_set_type);
        }
        if (unmask)
@@ -773,11 +776,39 @@ static void wake_threads_waitq(struct irq_desc *desc)
                wake_up(&desc->wait_for_threads);
 }
 
+static void irq_thread_dtor(struct task_work *unused)
+{
+       struct task_struct *tsk = current;
+       struct irq_desc *desc;
+       struct irqaction *action;
+
+       if (WARN_ON_ONCE(!(current->flags & PF_EXITING)))
+               return;
+
+       action = kthread_data(tsk);
+
+       pr_err("exiting task \"%s\" (%d) is an active IRQ thread (irq %d)\n",
+              tsk->comm ? tsk->comm : "", tsk->pid, action->irq);
+
+
+       desc = irq_to_desc(action->irq);
+       /*
+        * If IRQTF_RUNTHREAD is set, we need to decrement
+        * desc->threads_active and wake possible waiters.
+        */
+       if (test_and_clear_bit(IRQTF_RUNTHREAD, &action->thread_flags))
+               wake_threads_waitq(desc);
+
+       /* Prevent a stale desc->threads_oneshot */
+       irq_finalize_oneshot(desc, action);
+}
+
 /*
  * Interrupt handler thread
  */
 static int irq_thread(void *data)
 {
+       struct task_work on_exit_work;
        static const struct sched_param param = {
                .sched_priority = MAX_USER_RT_PRIO/2,
        };
@@ -793,7 +824,9 @@ static int irq_thread(void *data)
                handler_fn = irq_thread_fn;
 
        sched_setscheduler(current, SCHED_FIFO, &param);
-       current->irq_thread = 1;
+
+       init_task_work(&on_exit_work, irq_thread_dtor, NULL);
+       task_work_add(current, &on_exit_work, false);
 
        while (!irq_wait_for_interrupt(action)) {
                irqreturn_t action_ret;
@@ -815,44 +848,11 @@ static int irq_thread(void *data)
         * cannot touch the oneshot mask at this point anymore as
         * __setup_irq() might have given out currents thread_mask
         * again.
-        *
-        * Clear irq_thread. Otherwise exit_irq_thread() would make
-        * fuzz about an active irq thread going into nirvana.
         */
-       current->irq_thread = 0;
+       task_work_cancel(current, irq_thread_dtor);
        return 0;
 }
 
-/*
- * Called from do_exit()
- */
-void exit_irq_thread(void)
-{
-       struct task_struct *tsk = current;
-       struct irq_desc *desc;
-       struct irqaction *action;
-
-       if (!tsk->irq_thread)
-               return;
-
-       action = kthread_data(tsk);
-
-       pr_err("genirq: exiting task \"%s\" (%d) is an active IRQ thread (irq %d)\n",
-              tsk->comm ? tsk->comm : "", tsk->pid, action->irq);
-
-       desc = irq_to_desc(action->irq);
-
-       /*
-        * If IRQTF_RUNTHREAD is set, we need to decrement
-        * desc->threads_active and wake possible waiters.
-        */
-       if (test_and_clear_bit(IRQTF_RUNTHREAD, &action->thread_flags))
-               wake_threads_waitq(desc);
-
-       /* Prevent a stale desc->threads_oneshot */
-       irq_finalize_oneshot(desc, action);
-}
-
 static void irq_setup_forced_threading(struct irqaction *new)
 {
        if (!force_irqthreads)
@@ -1044,7 +1044,7 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
                 * has. The type flags are unreliable as the
                 * underlying chip implementation can override them.
                 */
-               pr_err("genirq: Threaded irq requested with handler=NULL and !ONESHOT for irq %d\n",
+               pr_err("Threaded irq requested with handler=NULL and !ONESHOT for irq %d\n",
                       irq);
                ret = -EINVAL;
                goto out_mask;
@@ -1095,7 +1095,7 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
 
                if (nmsk != omsk)
                        /* hope the handler works with current  trigger mode */
-                       pr_warning("genirq: irq %d uses trigger mode %u; requested %u\n",
+                       pr_warning("irq %d uses trigger mode %u; requested %u\n",
                                   irq, nmsk, omsk);
        }
 
@@ -1133,7 +1133,7 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
 
 mismatch:
        if (!(new->flags & IRQF_PROBE_SHARED)) {
-               pr_err("genirq: Flags mismatch irq %d. %08x (%s) vs. %08x (%s)\n",
+               pr_err("Flags mismatch irq %d. %08x (%s) vs. %08x (%s)\n",
                       irq, new->flags, new->name, old->flags, old->name);
 #ifdef CONFIG_DEBUG_SHIRQ
                dump_stack();
index 079f1d39a8b84a9105852864cbc8ceaf8875d5ac..2169feeba529be9843ea844e7281e1cdb31377a2 100644 (file)
@@ -343,7 +343,7 @@ int lookup_symbol_attrs(unsigned long addr, unsigned long *size,
 
 /* Look up a kernel symbol and return it in a text buffer. */
 static int __sprint_symbol(char *buffer, unsigned long address,
-                          int symbol_offset)
+                          int symbol_offset, int add_offset)
 {
        char *modname;
        const char *name;
@@ -358,13 +358,13 @@ static int __sprint_symbol(char *buffer, unsigned long address,
        if (name != buffer)
                strcpy(buffer, name);
        len = strlen(buffer);
-       buffer += len;
        offset -= symbol_offset;
 
+       if (add_offset)
+               len += sprintf(buffer + len, "+%#lx/%#lx", offset, size);
+
        if (modname)
-               len += sprintf(buffer, "+%#lx/%#lx [%s]", offset, size, modname);
-       else
-               len += sprintf(buffer, "+%#lx/%#lx", offset, size);
+               len += sprintf(buffer + len, " [%s]", modname);
 
        return len;
 }
@@ -382,11 +382,27 @@ static int __sprint_symbol(char *buffer, unsigned long address,
  */
 int sprint_symbol(char *buffer, unsigned long address)
 {
-       return __sprint_symbol(buffer, address, 0);
+       return __sprint_symbol(buffer, address, 0, 1);
 }
-
 EXPORT_SYMBOL_GPL(sprint_symbol);
 
+/**
+ * sprint_symbol_no_offset - Look up a kernel symbol and return it in a text buffer
+ * @buffer: buffer to be stored
+ * @address: address to lookup
+ *
+ * This function looks up a kernel symbol with @address and stores its name
+ * and module name to @buffer if possible. If no symbol was found, just saves
+ * its @address as is.
+ *
+ * This function returns the number of bytes stored in @buffer.
+ */
+int sprint_symbol_no_offset(char *buffer, unsigned long address)
+{
+       return __sprint_symbol(buffer, address, 0, 0);
+}
+EXPORT_SYMBOL_GPL(sprint_symbol_no_offset);
+
 /**
  * sprint_backtrace - Look up a backtrace symbol and return it in a text buffer
  * @buffer: buffer to be stored
@@ -403,7 +419,7 @@ EXPORT_SYMBOL_GPL(sprint_symbol);
  */
 int sprint_backtrace(char *buffer, unsigned long address)
 {
-       return __sprint_symbol(buffer, address, -1);
+       return __sprint_symbol(buffer, address, -1, 1);
 }
 
 /* Look up a kernel symbol and print it to the kernel messages. */
diff --git a/kernel/kcmp.c b/kernel/kcmp.c
new file mode 100644 (file)
index 0000000..30b7b22
--- /dev/null
@@ -0,0 +1,196 @@
+#include <linux/kernel.h>
+#include <linux/syscalls.h>
+#include <linux/fdtable.h>
+#include <linux/string.h>
+#include <linux/random.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/cache.h>
+#include <linux/bug.h>
+#include <linux/err.h>
+#include <linux/kcmp.h>
+
+#include <asm/unistd.h>
+
+/*
+ * We don't expose the real in-memory order of objects for security reasons.
+ * But still the comparison results should be suitable for sorting. So we
+ * obfuscate kernel pointers values and compare the production instead.
+ *
+ * The obfuscation is done in two steps. First we xor the kernel pointer with
+ * a random value, which puts pointer into a new position in a reordered space.
+ * Secondly we multiply the xor production with a large odd random number to
+ * permute its bits even more (the odd multiplier guarantees that the product
+ * is unique ever after the high bits are truncated, since any odd number is
+ * relative prime to 2^n).
+ *
+ * Note also that the obfuscation itself is invisible to userspace and if needed
+ * it can be changed to an alternate scheme.
+ */
+static unsigned long cookies[KCMP_TYPES][2] __read_mostly;
+
+static long kptr_obfuscate(long v, int type)
+{
+       return (v ^ cookies[type][0]) * cookies[type][1];
+}
+
+/*
+ * 0 - equal, i.e. v1 = v2
+ * 1 - less than, i.e. v1 < v2
+ * 2 - greater than, i.e. v1 > v2
+ * 3 - not equal but ordering unavailable (reserved for future)
+ */
+static int kcmp_ptr(void *v1, void *v2, enum kcmp_type type)
+{
+       long ret;
+
+       ret = kptr_obfuscate((long)v1, type) - kptr_obfuscate((long)v2, type);
+
+       return (ret < 0) | ((ret > 0) << 1);
+}
+
+/* The caller must have pinned the task */
+static struct file *
+get_file_raw_ptr(struct task_struct *task, unsigned int idx)
+{
+       struct file *file = NULL;
+
+       task_lock(task);
+       rcu_read_lock();
+
+       if (task->files)
+               file = fcheck_files(task->files, idx);
+
+       rcu_read_unlock();
+       task_unlock(task);
+
+       return file;
+}
+
+static void kcmp_unlock(struct mutex *m1, struct mutex *m2)
+{
+       if (likely(m2 != m1))
+               mutex_unlock(m2);
+       mutex_unlock(m1);
+}
+
+static int kcmp_lock(struct mutex *m1, struct mutex *m2)
+{
+       int err;
+
+       if (m2 > m1)
+               swap(m1, m2);
+
+       err = mutex_lock_killable(m1);
+       if (!err && likely(m1 != m2)) {
+               err = mutex_lock_killable_nested(m2, SINGLE_DEPTH_NESTING);
+               if (err)
+                       mutex_unlock(m1);
+       }
+
+       return err;
+}
+
+SYSCALL_DEFINE5(kcmp, pid_t, pid1, pid_t, pid2, int, type,
+               unsigned long, idx1, unsigned long, idx2)
+{
+       struct task_struct *task1, *task2;
+       int ret;
+
+       rcu_read_lock();
+
+       /*
+        * Tasks are looked up in caller's PID namespace only.
+        */
+       task1 = find_task_by_vpid(pid1);
+       task2 = find_task_by_vpid(pid2);
+       if (!task1 || !task2)
+               goto err_no_task;
+
+       get_task_struct(task1);
+       get_task_struct(task2);
+
+       rcu_read_unlock();
+
+       /*
+        * One should have enough rights to inspect task details.
+        */
+       ret = kcmp_lock(&task1->signal->cred_guard_mutex,
+                       &task2->signal->cred_guard_mutex);
+       if (ret)
+               goto err;
+       if (!ptrace_may_access(task1, PTRACE_MODE_READ) ||
+           !ptrace_may_access(task2, PTRACE_MODE_READ)) {
+               ret = -EPERM;
+               goto err_unlock;
+       }
+
+       switch (type) {
+       case KCMP_FILE: {
+               struct file *filp1, *filp2;
+
+               filp1 = get_file_raw_ptr(task1, idx1);
+               filp2 = get_file_raw_ptr(task2, idx2);
+
+               if (filp1 && filp2)
+                       ret = kcmp_ptr(filp1, filp2, KCMP_FILE);
+               else
+                       ret = -EBADF;
+               break;
+       }
+       case KCMP_VM:
+               ret = kcmp_ptr(task1->mm, task2->mm, KCMP_VM);
+               break;
+       case KCMP_FILES:
+               ret = kcmp_ptr(task1->files, task2->files, KCMP_FILES);
+               break;
+       case KCMP_FS:
+               ret = kcmp_ptr(task1->fs, task2->fs, KCMP_FS);
+               break;
+       case KCMP_SIGHAND:
+               ret = kcmp_ptr(task1->sighand, task2->sighand, KCMP_SIGHAND);
+               break;
+       case KCMP_IO:
+               ret = kcmp_ptr(task1->io_context, task2->io_context, KCMP_IO);
+               break;
+       case KCMP_SYSVSEM:
+#ifdef CONFIG_SYSVIPC
+               ret = kcmp_ptr(task1->sysvsem.undo_list,
+                              task2->sysvsem.undo_list,
+                              KCMP_SYSVSEM);
+#else
+               ret = -EOPNOTSUPP;
+#endif
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+
+err_unlock:
+       kcmp_unlock(&task1->signal->cred_guard_mutex,
+                   &task2->signal->cred_guard_mutex);
+err:
+       put_task_struct(task1);
+       put_task_struct(task2);
+
+       return ret;
+
+err_no_task:
+       rcu_read_unlock();
+       return -ESRCH;
+}
+
+static __init int kcmp_cookies_init(void)
+{
+       int i;
+
+       get_random_bytes(cookies, sizeof(cookies));
+
+       for (i = 0; i < KCMP_TYPES; i++)
+               cookies[i][1] |= (~(~0UL >>  1) | 1);
+
+       return 0;
+}
+arch_initcall(kcmp_cookies_init);
index 05698a7415fea66ea604b87959bde93f5b2673a3..ff2c7cb86d770aaf51712e330dc0f1e8a72a26e6 100644 (file)
@@ -221,13 +221,12 @@ fail:
        return 0;
 }
 
-void call_usermodehelper_freeinfo(struct subprocess_info *info)
+static void call_usermodehelper_freeinfo(struct subprocess_info *info)
 {
        if (info->cleanup)
                (*info->cleanup)(info);
        kfree(info);
 }
-EXPORT_SYMBOL(call_usermodehelper_freeinfo);
 
 static void umh_complete(struct subprocess_info *sub_info)
 {
@@ -410,7 +409,7 @@ EXPORT_SYMBOL_GPL(usermodehelper_read_unlock);
 
 /**
  * __usermodehelper_set_disable_depth - Modify usermodehelper_disabled.
- * depth: New value to assign to usermodehelper_disabled.
+ * @depth: New value to assign to usermodehelper_disabled.
  *
  * Change the value of usermodehelper_disabled (under umhelper_sem locked for
  * writing) and wakeup tasks waiting for it to change.
@@ -479,6 +478,7 @@ static void helper_unlock(void)
  * structure.  This should be passed to call_usermodehelper_exec to
  * exec the process and free the structure.
  */
+static
 struct subprocess_info *call_usermodehelper_setup(char *path, char **argv,
                                                  char **envp, gfp_t gfp_mask)
 {
@@ -494,7 +494,6 @@ struct subprocess_info *call_usermodehelper_setup(char *path, char **argv,
   out:
        return sub_info;
 }
-EXPORT_SYMBOL(call_usermodehelper_setup);
 
 /**
  * call_usermodehelper_setfns - set a cleanup/init function
@@ -512,6 +511,7 @@ EXPORT_SYMBOL(call_usermodehelper_setup);
  * Function must be runnable in either a process context or the
  * context in which call_usermodehelper_exec is called.
  */
+static
 void call_usermodehelper_setfns(struct subprocess_info *info,
                    int (*init)(struct subprocess_info *info, struct cred *new),
                    void (*cleanup)(struct subprocess_info *info),
@@ -521,7 +521,6 @@ void call_usermodehelper_setfns(struct subprocess_info *info,
        info->init = init;
        info->data = data;
 }
-EXPORT_SYMBOL(call_usermodehelper_setfns);
 
 /**
  * call_usermodehelper_exec - start a usermode application
@@ -535,6 +534,7 @@ EXPORT_SYMBOL(call_usermodehelper_setfns);
  * asynchronously if wait is not set, and runs as a child of keventd.
  * (ie. it runs with full root capabilities).
  */
+static
 int call_usermodehelper_exec(struct subprocess_info *sub_info, int wait)
 {
        DECLARE_COMPLETION_ONSTACK(done);
@@ -576,7 +576,25 @@ unlock:
        helper_unlock();
        return retval;
 }
-EXPORT_SYMBOL(call_usermodehelper_exec);
+
+int call_usermodehelper_fns(
+       char *path, char **argv, char **envp, int wait,
+       int (*init)(struct subprocess_info *info, struct cred *new),
+       void (*cleanup)(struct subprocess_info *), void *data)
+{
+       struct subprocess_info *info;
+       gfp_t gfp_mask = (wait == UMH_NO_WAIT) ? GFP_ATOMIC : GFP_KERNEL;
+
+       info = call_usermodehelper_setup(path, argv, envp, gfp_mask);
+
+       if (info == NULL)
+               return -ENOMEM;
+
+       call_usermodehelper_setfns(info, init, cleanup, data);
+
+       return call_usermodehelper_exec(info, wait);
+}
+EXPORT_SYMBOL(call_usermodehelper_fns);
 
 static int proc_cap_handler(struct ctl_table *table, int write,
                         void __user *buffer, size_t *lenp, loff_t *ppos)
diff --git a/kernel/lglock.c b/kernel/lglock.c
new file mode 100644 (file)
index 0000000..6535a66
--- /dev/null
@@ -0,0 +1,89 @@
+/* See include/linux/lglock.h for description */
+#include <linux/module.h>
+#include <linux/lglock.h>
+#include <linux/cpu.h>
+#include <linux/string.h>
+
+/*
+ * Note there is no uninit, so lglocks cannot be defined in
+ * modules (but it's fine to use them from there)
+ * Could be added though, just undo lg_lock_init
+ */
+
+void lg_lock_init(struct lglock *lg, char *name)
+{
+       LOCKDEP_INIT_MAP(&lg->lock_dep_map, name, &lg->lock_key, 0);
+}
+EXPORT_SYMBOL(lg_lock_init);
+
+void lg_local_lock(struct lglock *lg)
+{
+       arch_spinlock_t *lock;
+
+       preempt_disable();
+       rwlock_acquire_read(&lg->lock_dep_map, 0, 0, _RET_IP_);
+       lock = this_cpu_ptr(lg->lock);
+       arch_spin_lock(lock);
+}
+EXPORT_SYMBOL(lg_local_lock);
+
+void lg_local_unlock(struct lglock *lg)
+{
+       arch_spinlock_t *lock;
+
+       rwlock_release(&lg->lock_dep_map, 1, _RET_IP_);
+       lock = this_cpu_ptr(lg->lock);
+       arch_spin_unlock(lock);
+       preempt_enable();
+}
+EXPORT_SYMBOL(lg_local_unlock);
+
+void lg_local_lock_cpu(struct lglock *lg, int cpu)
+{
+       arch_spinlock_t *lock;
+
+       preempt_disable();
+       rwlock_acquire_read(&lg->lock_dep_map, 0, 0, _RET_IP_);
+       lock = per_cpu_ptr(lg->lock, cpu);
+       arch_spin_lock(lock);
+}
+EXPORT_SYMBOL(lg_local_lock_cpu);
+
+void lg_local_unlock_cpu(struct lglock *lg, int cpu)
+{
+       arch_spinlock_t *lock;
+
+       rwlock_release(&lg->lock_dep_map, 1, _RET_IP_);
+       lock = per_cpu_ptr(lg->lock, cpu);
+       arch_spin_unlock(lock);
+       preempt_enable();
+}
+EXPORT_SYMBOL(lg_local_unlock_cpu);
+
+void lg_global_lock(struct lglock *lg)
+{
+       int i;
+
+       preempt_disable();
+       rwlock_acquire(&lg->lock_dep_map, 0, 0, _RET_IP_);
+       for_each_possible_cpu(i) {
+               arch_spinlock_t *lock;
+               lock = per_cpu_ptr(lg->lock, i);
+               arch_spin_lock(lock);
+       }
+}
+EXPORT_SYMBOL(lg_global_lock);
+
+void lg_global_unlock(struct lglock *lg)
+{
+       int i;
+
+       rwlock_release(&lg->lock_dep_map, 1, _RET_IP_);
+       for_each_possible_cpu(i) {
+               arch_spinlock_t *lock;
+               lock = per_cpu_ptr(lg->lock, i);
+               arch_spin_unlock(lock);
+       }
+       preempt_enable();
+}
+EXPORT_SYMBOL(lg_global_unlock);
index 57bc1fd35b3cbe6bffdbfe71af5f13fc00648b81..16b20e38c4a1e26e64db477f76bbbfcf1787b75b 100644 (file)
@@ -149,7 +149,12 @@ void zap_pid_ns_processes(struct pid_namespace *pid_ns)
 {
        int nr;
        int rc;
-       struct task_struct *task;
+       struct task_struct *task, *me = current;
+
+       /* Ignore SIGCHLD causing any terminated children to autoreap */
+       spin_lock_irq(&me->sighand->siglock);
+       me->sighand->action[SIGCHLD - 1].sa.sa_handler = SIG_IGN;
+       spin_unlock_irq(&me->sighand->siglock);
 
        /*
         * The last thread in the cgroup-init thread group is terminating.
@@ -191,6 +196,7 @@ void zap_pid_ns_processes(struct pid_namespace *pid_ns)
        return;
 }
 
+#ifdef CONFIG_CHECKPOINT_RESTORE
 static int pid_ns_ctl_handler(struct ctl_table *table, int write,
                void __user *buffer, size_t *lenp, loff_t *ppos)
 {
@@ -218,8 +224,8 @@ static struct ctl_table pid_ns_ctl_table[] = {
        },
        { }
 };
-
 static struct ctl_path kern_path[] = { { .procname = "kernel", }, { } };
+#endif /* CONFIG_CHECKPOINT_RESTORE */
 
 int reboot_pid_ns(struct pid_namespace *pid_ns, int cmd)
 {
@@ -253,7 +259,10 @@ int reboot_pid_ns(struct pid_namespace *pid_ns, int cmd)
 static __init int pid_namespaces_init(void)
 {
        pid_ns_cachep = KMEM_CACHE(pid_namespace, SLAB_PANIC);
+
+#ifdef CONFIG_CHECKPOINT_RESTORE
        register_sysctl_paths(kern_path, pid_ns_ctl_table);
+#endif
        return 0;
 }
 
index bebe2b170d49ffd4c5b96590114f40d4d9fb69f9..ad581aa2369a2ed8f925c395b2b4eadd9d8640f2 100644 (file)
@@ -94,13 +94,15 @@ void res_counter_uncharge_locked(struct res_counter *counter, unsigned long val)
        counter->usage -= val;
 }
 
-void res_counter_uncharge(struct res_counter *counter, unsigned long val)
+void res_counter_uncharge_until(struct res_counter *counter,
+                               struct res_counter *top,
+                               unsigned long val)
 {
        unsigned long flags;
        struct res_counter *c;
 
        local_irq_save(flags);
-       for (c = counter; c != NULL; c = c->parent) {
+       for (c = counter; c != top; c = c->parent) {
                spin_lock(&c->lock);
                res_counter_uncharge_locked(c, val);
                spin_unlock(&c->lock);
@@ -108,6 +110,10 @@ void res_counter_uncharge(struct res_counter *counter, unsigned long val)
        local_irq_restore(flags);
 }
 
+void res_counter_uncharge(struct res_counter *counter, unsigned long val)
+{
+       res_counter_uncharge_until(counter, NULL, val);
+}
 
 static inline unsigned long long *
 res_counter_member(struct res_counter *counter, int member)
index 7e8ea66a8c016ffc934997256835f3fdc735f2f8..e1d2b8ee76d5bcd22d552c21cdf2102eaf6c7627 100644 (file)
@@ -515,8 +515,8 @@ out:
  * @root: root resource descriptor
  * @new: resource descriptor desired by caller
  * @size: requested resource region size
- * @min: minimum size to allocate
- * @max: maximum size to allocate
+ * @min: minimum boundary to allocate
+ * @max: maximum boundary to allocate
  * @align: alignment requested, in bytes
  * @alignf: alignment function, optional, called if not NULL
  * @alignf_data: arbitrary data to pass to the @alignf function
index f7b4182176331c2f3c667117fa60d48040d44a9e..677102789cf22d4847936782f6c6f67085421927 100644 (file)
@@ -1656,19 +1656,18 @@ bool do_notify_parent(struct task_struct *tsk, int sig)
        info.si_signo = sig;
        info.si_errno = 0;
        /*
-        * we are under tasklist_lock here so our parent is tied to
-        * us and cannot exit and release its namespace.
+        * We are under tasklist_lock here so our parent is tied to
+        * us and cannot change.
         *
-        * the only it can is to switch its nsproxy with sys_unshare,
-        * bu uncharing pid namespaces is not allowed, so we'll always
-        * see relevant namespace
+        * task_active_pid_ns will always return the same pid namespace
+        * until a task passes through release_task.
         *
         * write_lock() currently calls preempt_disable() which is the
         * same as rcu_read_lock(), but according to Oleg, this is not
         * correct to rely on this
         */
        rcu_read_lock();
-       info.si_pid = task_pid_nr_ns(tsk, tsk->parent->nsproxy->pid_ns);
+       info.si_pid = task_pid_nr_ns(tsk, task_active_pid_ns(tsk->parent));
        info.si_uid = from_kuid_munged(task_cred_xxx(tsk->parent, user_ns),
                                       task_uid(tsk));
        rcu_read_unlock();
@@ -2369,24 +2368,34 @@ relock:
 }
 
 /**
- * block_sigmask - add @ka's signal mask to current->blocked
- * @ka: action for @signr
- * @signr: signal that has been successfully delivered
+ * signal_delivered - 
+ * @sig:               number of signal being delivered
+ * @info:              siginfo_t of signal being delivered
+ * @ka:                        sigaction setting that chose the handler
+ * @regs:              user register state
+ * @stepping:          nonzero if debugger single-step or block-step in use
  *
  * This function should be called when a signal has succesfully been
- * delivered. It adds the mask of signals for @ka to current->blocked
- * so that they are blocked during the execution of the signal
- * handler. In addition, @signr will be blocked unless %SA_NODEFER is
- * set in @ka->sa.sa_flags.
+ * delivered. It updates the blocked signals accordingly (@ka->sa.sa_mask
+ * is always blocked, and the signal itself is blocked unless %SA_NODEFER
+ * is set in @ka->sa.sa_flags.  Tracing is notified.
  */
-void block_sigmask(struct k_sigaction *ka, int signr)
+void signal_delivered(int sig, siginfo_t *info, struct k_sigaction *ka,
+                       struct pt_regs *regs, int stepping)
 {
        sigset_t blocked;
 
+       /* A signal was successfully delivered, and the
+          saved sigmask was stored on the signal frame,
+          and will be restored by sigreturn.  So we can
+          simply clear the restore sigmask flag.  */
+       clear_restore_sigmask();
+
        sigorsets(&blocked, &current->blocked, &ka->sa.sa_mask);
        if (!(ka->sa.sa_flags & SA_NODEFER))
-               sigaddset(&blocked, signr);
+               sigaddset(&blocked, sig);
        set_current_blocked(&blocked);
+       tracehook_signal_handler(sig, info, ka, regs, stepping);
 }
 
 /*
@@ -2519,7 +2528,16 @@ static void __set_task_blocked(struct task_struct *tsk, const sigset_t *newset)
  * It is wrong to change ->blocked directly, this helper should be used
  * to ensure the process can't miss a shared signal we are going to block.
  */
-void set_current_blocked(const sigset_t *newset)
+void set_current_blocked(sigset_t *newset)
+{
+       struct task_struct *tsk = current;
+       sigdelsetmask(newset, sigmask(SIGKILL) | sigmask(SIGSTOP));
+       spin_lock_irq(&tsk->sighand->siglock);
+       __set_task_blocked(tsk, newset);
+       spin_unlock_irq(&tsk->sighand->siglock);
+}
+
+void __set_current_blocked(const sigset_t *newset)
 {
        struct task_struct *tsk = current;
 
@@ -2559,7 +2577,7 @@ int sigprocmask(int how, sigset_t *set, sigset_t *oldset)
                return -EINVAL;
        }
 
-       set_current_blocked(&newset);
+       __set_current_blocked(&newset);
        return 0;
 }
 
@@ -3133,7 +3151,7 @@ SYSCALL_DEFINE3(sigprocmask, int, how, old_sigset_t __user *, nset,
                        return -EINVAL;
                }
 
-               set_current_blocked(&new_blocked);
+               __set_current_blocked(&new_blocked);
        }
 
        if (oset) {
@@ -3197,7 +3215,6 @@ SYSCALL_DEFINE1(ssetmask, int, newmask)
        int old = current->blocked.sig[0];
        sigset_t newset;
 
-       siginitset(&newset, newmask & ~(sigmask(SIGKILL) | sigmask(SIGSTOP)));
        set_current_blocked(&newset);
 
        return old;
@@ -3236,11 +3253,8 @@ SYSCALL_DEFINE0(pause)
 
 #endif
 
-#ifdef HAVE_SET_RESTORE_SIGMASK
 int sigsuspend(sigset_t *set)
 {
-       sigdelsetmask(set, sigmask(SIGKILL)|sigmask(SIGSTOP));
-
        current->saved_sigmask = current->blocked;
        set_current_blocked(set);
 
@@ -3249,7 +3263,6 @@ int sigsuspend(sigset_t *set)
        set_restore_sigmask();
        return -ERESTARTNOHAND;
 }
-#endif
 
 #ifdef __ARCH_WANT_SYS_RT_SIGSUSPEND
 /**
index 6df42624e454aeb236ab1c9413d6fdf5f676365f..9ff89cb9657a681d15714447d7d3c64939fbbe50 100644 (file)
@@ -36,6 +36,8 @@
 #include <linux/personality.h>
 #include <linux/ptrace.h>
 #include <linux/fs_struct.h>
+#include <linux/file.h>
+#include <linux/mount.h>
 #include <linux/gfp.h>
 #include <linux/syscore_ops.h>
 #include <linux/version.h>
@@ -1378,8 +1380,8 @@ SYSCALL_DEFINE2(sethostname, char __user *, name, int, len)
                memcpy(u->nodename, tmp, len);
                memset(u->nodename + len, 0, sizeof(u->nodename) - len);
                errno = 0;
+               uts_proc_notify(UTS_PROC_HOSTNAME);
        }
-       uts_proc_notify(UTS_PROC_HOSTNAME);
        up_write(&uts_sem);
        return errno;
 }
@@ -1429,8 +1431,8 @@ SYSCALL_DEFINE2(setdomainname, char __user *, name, int, len)
                memcpy(u->domainname, tmp, len);
                memset(u->domainname + len, 0, sizeof(u->domainname) - len);
                errno = 0;
+               uts_proc_notify(UTS_PROC_DOMAINNAME);
        }
-       uts_proc_notify(UTS_PROC_DOMAINNAME);
        up_write(&uts_sem);
        return errno;
 }
@@ -1784,77 +1786,102 @@ SYSCALL_DEFINE1(umask, int, mask)
 }
 
 #ifdef CONFIG_CHECKPOINT_RESTORE
+static bool vma_flags_mismatch(struct vm_area_struct *vma,
+                              unsigned long required,
+                              unsigned long banned)
+{
+       return (vma->vm_flags & required) != required ||
+               (vma->vm_flags & banned);
+}
+
+static int prctl_set_mm_exe_file(struct mm_struct *mm, unsigned int fd)
+{
+       struct file *exe_file;
+       struct dentry *dentry;
+       int err;
+
+       /*
+        * Setting new mm::exe_file is only allowed when no VM_EXECUTABLE vma's
+        * remain. So perform a quick test first.
+        */
+       if (mm->num_exe_file_vmas)
+               return -EBUSY;
+
+       exe_file = fget(fd);
+       if (!exe_file)
+               return -EBADF;
+
+       dentry = exe_file->f_path.dentry;
+
+       /*
+        * Because the original mm->exe_file points to executable file, make
+        * sure that this one is executable as well, to avoid breaking an
+        * overall picture.
+        */
+       err = -EACCES;
+       if (!S_ISREG(dentry->d_inode->i_mode)   ||
+           exe_file->f_path.mnt->mnt_flags & MNT_NOEXEC)
+               goto exit;
+
+       err = inode_permission(dentry->d_inode, MAY_EXEC);
+       if (err)
+               goto exit;
+
+       /*
+        * The symlink can be changed only once, just to disallow arbitrary
+        * transitions malicious software might bring in. This means one
+        * could make a snapshot over all processes running and monitor
+        * /proc/pid/exe changes to notice unusual activity if needed.
+        */
+       down_write(&mm->mmap_sem);
+       if (likely(!mm->exe_file))
+               set_mm_exe_file(mm, exe_file);
+       else
+               err = -EBUSY;
+       up_write(&mm->mmap_sem);
+
+exit:
+       fput(exe_file);
+       return err;
+}
+
 static int prctl_set_mm(int opt, unsigned long addr,
                        unsigned long arg4, unsigned long arg5)
 {
        unsigned long rlim = rlimit(RLIMIT_DATA);
-       unsigned long vm_req_flags;
-       unsigned long vm_bad_flags;
-       struct vm_area_struct *vma;
-       int error = 0;
        struct mm_struct *mm = current->mm;
+       struct vm_area_struct *vma;
+       int error;
 
-       if (arg4 | arg5)
+       if (arg5 || (arg4 && opt != PR_SET_MM_AUXV))
                return -EINVAL;
 
        if (!capable(CAP_SYS_RESOURCE))
                return -EPERM;
 
+       if (opt == PR_SET_MM_EXE_FILE)
+               return prctl_set_mm_exe_file(mm, (unsigned int)addr);
+
        if (addr >= TASK_SIZE)
                return -EINVAL;
 
+       error = -EINVAL;
+
        down_read(&mm->mmap_sem);
        vma = find_vma(mm, addr);
 
-       if (opt != PR_SET_MM_START_BRK && opt != PR_SET_MM_BRK) {
-               /* It must be existing VMA */
-               if (!vma || vma->vm_start > addr)
-                       goto out;
-       }
-
-       error = -EINVAL;
        switch (opt) {
        case PR_SET_MM_START_CODE:
+               mm->start_code = addr;
+               break;
        case PR_SET_MM_END_CODE:
-               vm_req_flags = VM_READ | VM_EXEC;
-               vm_bad_flags = VM_WRITE | VM_MAYSHARE;
-
-               if ((vma->vm_flags & vm_req_flags) != vm_req_flags ||
-                   (vma->vm_flags & vm_bad_flags))
-                       goto out;
-
-               if (opt == PR_SET_MM_START_CODE)
-                       mm->start_code = addr;
-               else
-                       mm->end_code = addr;
+               mm->end_code = addr;
                break;
-
        case PR_SET_MM_START_DATA:
-       case PR_SET_MM_END_DATA:
-               vm_req_flags = VM_READ | VM_WRITE;
-               vm_bad_flags = VM_EXEC | VM_MAYSHARE;
-
-               if ((vma->vm_flags & vm_req_flags) != vm_req_flags ||
-                   (vma->vm_flags & vm_bad_flags))
-                       goto out;
-
-               if (opt == PR_SET_MM_START_DATA)
-                       mm->start_data = addr;
-               else
-                       mm->end_data = addr;
+               mm->start_data = addr;
                break;
-
-       case PR_SET_MM_START_STACK:
-
-#ifdef CONFIG_STACK_GROWSUP
-               vm_req_flags = VM_READ | VM_WRITE | VM_GROWSUP;
-#else
-               vm_req_flags = VM_READ | VM_WRITE | VM_GROWSDOWN;
-#endif
-               if ((vma->vm_flags & vm_req_flags) != vm_req_flags)
-                       goto out;
-
-               mm->start_stack = addr;
+       case PR_SET_MM_END_DATA:
+               mm->end_data = addr;
                break;
 
        case PR_SET_MM_START_BRK:
@@ -1881,16 +1908,77 @@ static int prctl_set_mm(int opt, unsigned long addr,
                mm->brk = addr;
                break;
 
+       /*
+        * If command line arguments and environment
+        * are placed somewhere else on stack, we can
+        * set them up here, ARG_START/END to setup
+        * command line argumets and ENV_START/END
+        * for environment.
+        */
+       case PR_SET_MM_START_STACK:
+       case PR_SET_MM_ARG_START:
+       case PR_SET_MM_ARG_END:
+       case PR_SET_MM_ENV_START:
+       case PR_SET_MM_ENV_END:
+               if (!vma) {
+                       error = -EFAULT;
+                       goto out;
+               }
+#ifdef CONFIG_STACK_GROWSUP
+               if (vma_flags_mismatch(vma, VM_READ | VM_WRITE | VM_GROWSUP, 0))
+#else
+               if (vma_flags_mismatch(vma, VM_READ | VM_WRITE | VM_GROWSDOWN, 0))
+#endif
+                       goto out;
+               if (opt == PR_SET_MM_START_STACK)
+                       mm->start_stack = addr;
+               else if (opt == PR_SET_MM_ARG_START)
+                       mm->arg_start = addr;
+               else if (opt == PR_SET_MM_ARG_END)
+                       mm->arg_end = addr;
+               else if (opt == PR_SET_MM_ENV_START)
+                       mm->env_start = addr;
+               else if (opt == PR_SET_MM_ENV_END)
+                       mm->env_end = addr;
+               break;
+
+       /*
+        * This doesn't move auxiliary vector itself
+        * since it's pinned to mm_struct, but allow
+        * to fill vector with new values. It's up
+        * to a caller to provide sane values here
+        * otherwise user space tools which use this
+        * vector might be unhappy.
+        */
+       case PR_SET_MM_AUXV: {
+               unsigned long user_auxv[AT_VECTOR_SIZE];
+
+               if (arg4 > sizeof(user_auxv))
+                       goto out;
+               up_read(&mm->mmap_sem);
+
+               if (copy_from_user(user_auxv, (const void __user *)addr, arg4))
+                       return -EFAULT;
+
+               /* Make sure the last entry is always AT_NULL */
+               user_auxv[AT_VECTOR_SIZE - 2] = 0;
+               user_auxv[AT_VECTOR_SIZE - 1] = 0;
+
+               BUILD_BUG_ON(sizeof(user_auxv) != sizeof(mm->saved_auxv));
+
+               task_lock(current);
+               memcpy(mm->saved_auxv, user_auxv, arg4);
+               task_unlock(current);
+
+               return 0;
+       }
        default:
-               error = -EINVAL;
                goto out;
        }
 
        error = 0;
-
 out:
        up_read(&mm->mmap_sem);
-
        return error;
 }
 #else /* CONFIG_CHECKPOINT_RESTORE */
@@ -2114,7 +2202,6 @@ int orderly_poweroff(bool force)
                NULL
        };
        int ret = -ENOMEM;
-       struct subprocess_info *info;
 
        if (argv == NULL) {
                printk(KERN_WARNING "%s failed to allocate memory for \"%s\"\n",
@@ -2122,18 +2209,16 @@ int orderly_poweroff(bool force)
                goto out;
        }
 
-       info = call_usermodehelper_setup(argv[0], argv, envp, GFP_ATOMIC);
-       if (info == NULL) {
-               argv_free(argv);
-               goto out;
-       }
-
-       call_usermodehelper_setfns(info, NULL, argv_cleanup, NULL);
+       ret = call_usermodehelper_fns(argv[0], argv, envp, UMH_NO_WAIT,
+                                     NULL, argv_cleanup, NULL);
+out:
+       if (likely(!ret))
+               return 0;
 
-       ret = call_usermodehelper_exec(info, UMH_NO_WAIT);
+       if (ret == -ENOMEM)
+               argv_free(argv);
 
-  out:
-       if (ret && force) {
+       if (force) {
                printk(KERN_WARNING "Failed to start orderly shutdown: "
                       "forcing the issue\n");
 
index 47bfa16430d7dc764c17a06f4c40dd142ef6a88a..dbff751e408647badd0d7e92b935962bfc3ef8e2 100644 (file)
@@ -203,3 +203,6 @@ cond_syscall(sys_fanotify_mark);
 cond_syscall(sys_name_to_handle_at);
 cond_syscall(sys_open_by_handle_at);
 cond_syscall(compat_sys_open_by_handle_at);
+
+/* compare kernel pointers */
+cond_syscall(sys_kcmp);
diff --git a/kernel/task_work.c b/kernel/task_work.c
new file mode 100644 (file)
index 0000000..82d1c79
--- /dev/null
@@ -0,0 +1,84 @@
+#include <linux/spinlock.h>
+#include <linux/task_work.h>
+#include <linux/tracehook.h>
+
+int
+task_work_add(struct task_struct *task, struct task_work *twork, bool notify)
+{
+       unsigned long flags;
+       int err = -ESRCH;
+
+#ifndef TIF_NOTIFY_RESUME
+       if (notify)
+               return -ENOTSUPP;
+#endif
+       /*
+        * We must not insert the new work if the task has already passed
+        * exit_task_work(). We rely on do_exit()->raw_spin_unlock_wait()
+        * and check PF_EXITING under pi_lock.
+        */
+       raw_spin_lock_irqsave(&task->pi_lock, flags);
+       if (likely(!(task->flags & PF_EXITING))) {
+               hlist_add_head(&twork->hlist, &task->task_works);
+               err = 0;
+       }
+       raw_spin_unlock_irqrestore(&task->pi_lock, flags);
+
+       /* test_and_set_bit() implies mb(), see tracehook_notify_resume(). */
+       if (likely(!err) && notify)
+               set_notify_resume(task);
+       return err;
+}
+
+struct task_work *
+task_work_cancel(struct task_struct *task, task_work_func_t func)
+{
+       unsigned long flags;
+       struct task_work *twork;
+       struct hlist_node *pos;
+
+       raw_spin_lock_irqsave(&task->pi_lock, flags);
+       hlist_for_each_entry(twork, pos, &task->task_works, hlist) {
+               if (twork->func == func) {
+                       hlist_del(&twork->hlist);
+                       goto found;
+               }
+       }
+       twork = NULL;
+ found:
+       raw_spin_unlock_irqrestore(&task->pi_lock, flags);
+
+       return twork;
+}
+
+void task_work_run(void)
+{
+       struct task_struct *task = current;
+       struct hlist_head task_works;
+       struct hlist_node *pos;
+
+       raw_spin_lock_irq(&task->pi_lock);
+       hlist_move_list(&task->task_works, &task_works);
+       raw_spin_unlock_irq(&task->pi_lock);
+
+       if (unlikely(hlist_empty(&task_works)))
+               return;
+       /*
+        * We use hlist to save the space in task_struct, but we want fifo.
+        * Find the last entry, the list should be short, then process them
+        * in reverse order.
+        */
+       for (pos = task_works.first; pos->next; pos = pos->next)
+               ;
+
+       for (;;) {
+               struct hlist_node **pprev = pos->pprev;
+               struct task_work *twork = container_of(pos, struct task_work,
+                                                       hlist);
+               twork->func(twork);
+
+               if (pprev == &task_works.first)
+                       break;
+               pos = container_of(pprev, struct hlist_node, next);
+       }
+}
index 6420cda62336c1194d02c86203a91e18766a037b..1d0f6a8a0e5e83680c0df3b28836a5f6a2103a39 100644 (file)
@@ -1486,6 +1486,11 @@ int ring_buffer_resize(struct ring_buffer *buffer, unsigned long size,
        if (!buffer)
                return size;
 
+       /* Make sure the requested buffer exists */
+       if (cpu_id != RING_BUFFER_ALL_CPUS &&
+           !cpumask_test_cpu(cpu_id, buffer->cpumask))
+               return size;
+
        size = DIV_ROUND_UP(size, BUF_PAGE_SIZE);
        size *= BUF_PAGE_SIZE;
 
index b5a8b6ad2454e32ec2444509fdb7aff4c80ec2d4..06fdfa1aeba712283c1e1d706424c1279abaf0ca 100644 (file)
@@ -369,7 +369,8 @@ EXPORT_SYMBOL(bitmap_find_next_zero_area);
  * @nmaskbits: size of bitmap, in bits
  *
  * Exactly @nmaskbits bits are displayed.  Hex digits are grouped into
- * comma-separated sets of eight digits per set.
+ * comma-separated sets of eight digits per set.  Returns the number of
+ * characters which were written to *buf, excluding the trailing \0.
  */
 int bitmap_scnprintf(char *buf, unsigned int buflen,
        const unsigned long *maskp, int nmaskbits)
@@ -517,8 +518,8 @@ EXPORT_SYMBOL(bitmap_parse_user);
  *
  * Helper routine for bitmap_scnlistprintf().  Write decimal number
  * or range to buf, suppressing output past buf+buflen, with optional
- * comma-prefix.  Return len of what would be written to buf, if it
- * all fit.
+ * comma-prefix.  Return len of what was written to *buf, excluding the
+ * trailing \0.
  */
 static inline int bscnl_emit(char *buf, int buflen, int rbot, int rtop, int len)
 {
@@ -544,9 +545,8 @@ static inline int bscnl_emit(char *buf, int buflen, int rbot, int rtop, int len)
  * the range.  Output format is compatible with the format
  * accepted as input by bitmap_parselist().
  *
- * The return value is the number of characters which would be
- * generated for the given input, excluding the trailing '\0', as
- * per ISO C99.
+ * The return value is the number of characters which were written to *buf
+ * excluding the trailing '\0', as per ISO C99's scnprintf.
  */
 int bitmap_scnlistprintf(char *buf, unsigned int buflen,
        const unsigned long *maskp, int nmaskbits)
index 13ef2338be4150d1345a80a510d67f410a7c2c73..518aea714d21d9dea1c7d52b0cf6878482ca5d82 100644 (file)
@@ -430,7 +430,7 @@ static struct dma_debug_entry *__dma_entry_alloc(void)
  */
 static struct dma_debug_entry *dma_entry_alloc(void)
 {
-       struct dma_debug_entry *entry = NULL;
+       struct dma_debug_entry *entry;
        unsigned long flags;
 
        spin_lock_irqsave(&free_entries_lock, flags);
@@ -438,11 +438,14 @@ static struct dma_debug_entry *dma_entry_alloc(void)
        if (list_empty(&free_entries)) {
                pr_err("DMA-API: debugging out of memory - disabling\n");
                global_disable = true;
-               goto out;
+               spin_unlock_irqrestore(&free_entries_lock, flags);
+               return NULL;
        }
 
        entry = __dma_entry_alloc();
 
+       spin_unlock_irqrestore(&free_entries_lock, flags);
+
 #ifdef CONFIG_STACKTRACE
        entry->stacktrace.max_entries = DMA_DEBUG_STACKTRACE_ENTRIES;
        entry->stacktrace.entries = entry->st_entries;
@@ -450,9 +453,6 @@ static struct dma_debug_entry *dma_entry_alloc(void)
        save_stack_trace(&entry->stacktrace);
 #endif
 
-out:
-       spin_unlock_irqrestore(&free_entries_lock, flags);
-
        return entry;
 }
 
index 3810b481f940bac6450f3e7342ec5dbfdb385318..23a5e031cd8bc43605655da59bce57eadb3304b8 100644 (file)
@@ -31,6 +31,9 @@ void __list_add(struct list_head *new,
                "list_add corruption. prev->next should be "
                "next (%p), but was %p. (prev=%p).\n",
                next, prev->next, prev);
+       WARN(new == prev || new == next,
+            "list_add double add: new=%p, prev=%p, next=%p.\n",
+            new, prev, next);
        next->prev = new;
        new->next = next;
        new->prev = prev;
index 86516f5588e31782676087fd49fe45fd87538c03..d7c878cc006cf62ac2b0b0abb1a3a69fa62c64e2 100644 (file)
@@ -72,12 +72,25 @@ static unsigned long height_to_maxindex[RADIX_TREE_MAX_PATH + 1] __read_mostly;
  */
 static struct kmem_cache *radix_tree_node_cachep;
 
+/*
+ * The radix tree is variable-height, so an insert operation not only has
+ * to build the branch to its corresponding item, it also has to build the
+ * branch to existing items if the size has to be increased (by
+ * radix_tree_extend).
+ *
+ * The worst case is a zero height tree with just a single item at index 0,
+ * and then inserting an item at index ULONG_MAX. This requires 2 new branches
+ * of RADIX_TREE_MAX_PATH size to be created, with only the root node shared.
+ * Hence:
+ */
+#define RADIX_TREE_PRELOAD_SIZE (RADIX_TREE_MAX_PATH * 2 - 1)
+
 /*
  * Per-cpu pool of preloaded nodes
  */
 struct radix_tree_preload {
        int nr;
-       struct radix_tree_node *nodes[RADIX_TREE_MAX_PATH];
+       struct radix_tree_node *nodes[RADIX_TREE_PRELOAD_SIZE];
 };
 static DEFINE_PER_CPU(struct radix_tree_preload, radix_tree_preloads) = { 0, };
 
index 525d160d44f05c112acca5b2866b566ded334e19..d0ec4f3d1593031b5498dcc120822b41c423a3c5 100644 (file)
@@ -58,7 +58,7 @@ static void spin_dump(raw_spinlock_t *lock, const char *msg)
        printk(KERN_EMERG "BUG: spinlock %s on CPU#%d, %s/%d\n",
                msg, raw_smp_processor_id(),
                current->comm, task_pid_nr(current));
-       printk(KERN_EMERG " lock: %p, .magic: %08x, .owner: %s/%d, "
+       printk(KERN_EMERG " lock: %ps, .magic: %08x, .owner: %s/%d, "
                        ".owner_cpu: %d\n",
                lock, lock->magic,
                owner ? owner->comm : "<none>",
index dd4ece372699e5b717626ff0a558d919a118d60e..1cffc223bff52b7e153dff043ae9149073050934 100644 (file)
 int string_get_size(u64 size, const enum string_size_units units,
                    char *buf, int len)
 {
-       const char *units_10[] = { "B", "kB", "MB", "GB", "TB", "PB",
+       static const char *units_10[] = { "B", "kB", "MB", "GB", "TB", "PB",
                                   "EB", "ZB", "YB", NULL};
-       const char *units_2[] = {"B", "KiB", "MiB", "GiB", "TiB", "PiB",
+       static const char *units_2[] = {"B", "KiB", "MiB", "GiB", "TiB", "PiB",
                                 "EiB", "ZiB", "YiB", NULL };
-       const char **units_str[] = {
+       static const char **units_str[] = {
                [STRING_UNITS_10] =  units_10,
                [STRING_UNITS_2] = units_2,
        };
-       const unsigned int divisor[] = {
+       static const unsigned int divisor[] = {
                [STRING_UNITS_10] = 1000,
                [STRING_UNITS_2] = 1024,
        };
index 414f46ed1dcda9ae63be846cc6cf6e1b9bb7bb2e..45bc1f83a5ada665297bc0b9dcd3a6ad72e2ec72 100644 (file)
@@ -130,11 +130,9 @@ void swiotlb_print_info(void)
        pstart = virt_to_phys(io_tlb_start);
        pend = virt_to_phys(io_tlb_end);
 
-       printk(KERN_INFO "Placing %luMB software IO TLB between %p - %p\n",
-              bytes >> 20, io_tlb_start, io_tlb_end);
-       printk(KERN_INFO "software IO TLB at phys %#llx - %#llx\n",
-              (unsigned long long)pstart,
-              (unsigned long long)pend);
+       printk(KERN_INFO "software IO TLB [mem %#010llx-%#010llx] (%luMB) mapped at [%p-%p]\n",
+              (unsigned long long)pstart, (unsigned long long)pend - 1,
+              bytes >> 20, io_tlb_start, io_tlb_end - 1);
 }
 
 void __init swiotlb_init_with_tbl(char *tlb, unsigned long nslabs, int verbose)
index d55769d63cb8b3efb8182a0f58a73ed496768b21..bea3f3fa3f02a920fb92b9293c0adf06d72a52a6 100644 (file)
@@ -11,7 +11,7 @@ struct test_fail {
 };
 
 #define DEFINE_TEST_FAIL(test) \
-       const struct test_fail test[] __initdata
+       const struct test_fail test[] __initconst
 
 #define DECLARE_TEST_OK(type, test_type)       \
        test_type {                             \
@@ -21,7 +21,7 @@ struct test_fail {
        }
 
 #define DEFINE_TEST_OK(type, test)     \
-       const type test[] __initdata
+       const type test[] __initconst
 
 #define TEST_FAIL(fn, type, fmt, test)                                 \
 {                                                                      \
index abbabec9720a1947ffa5364ae9aab9ec9696ecfe..c3f36d415bdf43034415c801b8e3f922f5e5b928 100644 (file)
@@ -112,106 +112,199 @@ int skip_atoi(const char **s)
 /* Decimal conversion is by far the most typical, and is used
  * for /proc and /sys data. This directly impacts e.g. top performance
  * with many processes running. We optimize it for speed
- * using code from
- * http://www.cs.uiowa.edu/~jones/bcd/decimal.html
- * (with permission from the author, Douglas W. Jones). */
+ * using ideas described at <http://www.cs.uiowa.edu/~jones/bcd/divide.html>
+ * (with permission from the author, Douglas W. Jones).
+ */
 
-/* Formats correctly any integer in [0,99999].
- * Outputs from one to five digits depending on input.
- * On i386 gcc 4.1.2 -O2: ~250 bytes of code. */
+#if BITS_PER_LONG != 32 || BITS_PER_LONG_LONG != 64
+/* Formats correctly any integer in [0, 999999999] */
 static noinline_for_stack
-char *put_dec_trunc(char *buf, unsigned q)
+char *put_dec_full9(char *buf, unsigned q)
 {
-       unsigned d3, d2, d1, d0;
-       d1 = (q>>4) & 0xf;
-       d2 = (q>>8) & 0xf;
-       d3 = (q>>12);
-
-       d0 = 6*(d3 + d2 + d1) + (q & 0xf);
-       q = (d0 * 0xcd) >> 11;
-       d0 = d0 - 10*q;
-       *buf++ = d0 + '0'; /* least significant digit */
-       d1 = q + 9*d3 + 5*d2 + d1;
-       if (d1 != 0) {
-               q = (d1 * 0xcd) >> 11;
-               d1 = d1 - 10*q;
-               *buf++ = d1 + '0'; /* next digit */
-
-               d2 = q + 2*d2;
-               if ((d2 != 0) || (d3 != 0)) {
-                       q = (d2 * 0xd) >> 7;
-                       d2 = d2 - 10*q;
-                       *buf++ = d2 + '0'; /* next digit */
-
-                       d3 = q + 4*d3;
-                       if (d3 != 0) {
-                               q = (d3 * 0xcd) >> 11;
-                               d3 = d3 - 10*q;
-                               *buf++ = d3 + '0';  /* next digit */
-                               if (q != 0)
-                                       *buf++ = q + '0'; /* most sign. digit */
-                       }
-               }
-       }
+       unsigned r;
 
+       /*
+        * Possible ways to approx. divide by 10
+        * (x * 0x1999999a) >> 32 x < 1073741829 (multiply must be 64-bit)
+        * (x * 0xcccd) >> 19     x <      81920 (x < 262149 when 64-bit mul)
+        * (x * 0x6667) >> 18     x <      43699
+        * (x * 0x3334) >> 17     x <      16389
+        * (x * 0x199a) >> 16     x <      16389
+        * (x * 0x0ccd) >> 15     x <      16389
+        * (x * 0x0667) >> 14     x <       2739
+        * (x * 0x0334) >> 13     x <       1029
+        * (x * 0x019a) >> 12     x <       1029
+        * (x * 0x00cd) >> 11     x <       1029 shorter code than * 0x67 (on i386)
+        * (x * 0x0067) >> 10     x <        179
+        * (x * 0x0034) >>  9     x <         69 same
+        * (x * 0x001a) >>  8     x <         69 same
+        * (x * 0x000d) >>  7     x <         69 same, shortest code (on i386)
+        * (x * 0x0007) >>  6     x <         19
+        * See <http://www.cs.uiowa.edu/~jones/bcd/divide.html>
+        */
+       r      = (q * (uint64_t)0x1999999a) >> 32;
+       *buf++ = (q - 10 * r) + '0'; /* 1 */
+       q      = (r * (uint64_t)0x1999999a) >> 32;
+       *buf++ = (r - 10 * q) + '0'; /* 2 */
+       r      = (q * (uint64_t)0x1999999a) >> 32;
+       *buf++ = (q - 10 * r) + '0'; /* 3 */
+       q      = (r * (uint64_t)0x1999999a) >> 32;
+       *buf++ = (r - 10 * q) + '0'; /* 4 */
+       r      = (q * (uint64_t)0x1999999a) >> 32;
+       *buf++ = (q - 10 * r) + '0'; /* 5 */
+       /* Now value is under 10000, can avoid 64-bit multiply */
+       q      = (r * 0x199a) >> 16;
+       *buf++ = (r - 10 * q)  + '0'; /* 6 */
+       r      = (q * 0xcd) >> 11;
+       *buf++ = (q - 10 * r)  + '0'; /* 7 */
+       q      = (r * 0xcd) >> 11;
+       *buf++ = (r - 10 * q) + '0'; /* 8 */
+       *buf++ = q + '0'; /* 9 */
        return buf;
 }
-/* Same with if's removed. Always emits five digits */
+#endif
+
+/* Similar to above but do not pad with zeros.
+ * Code can be easily arranged to print 9 digits too, but our callers
+ * always call put_dec_full9() instead when the number has 9 decimal digits.
+ */
 static noinline_for_stack
-char *put_dec_full(char *buf, unsigned q)
+char *put_dec_trunc8(char *buf, unsigned r)
 {
-       /* BTW, if q is in [0,9999], 8-bit ints will be enough, */
-       /* but anyway, gcc produces better code with full-sized ints */
-       unsigned d3, d2, d1, d0;
-       d1 = (q>>4) & 0xf;
-       d2 = (q>>8) & 0xf;
-       d3 = (q>>12);
+       unsigned q;
+
+       /* Copy of previous function's body with added early returns */
+       q      = (r * (uint64_t)0x1999999a) >> 32;
+       *buf++ = (r - 10 * q) + '0'; /* 2 */
+       if (q == 0)
+               return buf;
+       r      = (q * (uint64_t)0x1999999a) >> 32;
+       *buf++ = (q - 10 * r) + '0'; /* 3 */
+       if (r == 0)
+               return buf;
+       q      = (r * (uint64_t)0x1999999a) >> 32;
+       *buf++ = (r - 10 * q) + '0'; /* 4 */
+       if (q == 0)
+               return buf;
+       r      = (q * (uint64_t)0x1999999a) >> 32;
+       *buf++ = (q - 10 * r) + '0'; /* 5 */
+       if (r == 0)
+               return buf;
+       q      = (r * 0x199a) >> 16;
+       *buf++ = (r - 10 * q)  + '0'; /* 6 */
+       if (q == 0)
+               return buf;
+       r      = (q * 0xcd) >> 11;
+       *buf++ = (q - 10 * r)  + '0'; /* 7 */
+       if (r == 0)
+               return buf;
+       q      = (r * 0xcd) >> 11;
+       *buf++ = (r - 10 * q) + '0'; /* 8 */
+       if (q == 0)
+               return buf;
+       *buf++ = q + '0'; /* 9 */
+       return buf;
+}
 
-       /*
-        * Possible ways to approx. divide by 10
-        * gcc -O2 replaces multiply with shifts and adds
-        * (x * 0xcd) >> 11: 11001101 - shorter code than * 0x67 (on i386)
-        * (x * 0x67) >> 10:  1100111
-        * (x * 0x34) >> 9:    110100 - same
-        * (x * 0x1a) >> 8:     11010 - same
-        * (x * 0x0d) >> 7:      1101 - same, shortest code (on i386)
-        */
-       d0 = 6*(d3 + d2 + d1) + (q & 0xf);
-       q = (d0 * 0xcd) >> 11;
-       d0 = d0 - 10*q;
-       *buf++ = d0 + '0';
-       d1 = q + 9*d3 + 5*d2 + d1;
-               q = (d1 * 0xcd) >> 11;
-               d1 = d1 - 10*q;
-               *buf++ = d1 + '0';
-
-               d2 = q + 2*d2;
-                       q = (d2 * 0xd) >> 7;
-                       d2 = d2 - 10*q;
-                       *buf++ = d2 + '0';
-
-                       d3 = q + 4*d3;
-                               q = (d3 * 0xcd) >> 11; /* - shorter code */
-                               /* q = (d3 * 0x67) >> 10; - would also work */
-                               d3 = d3 - 10*q;
-                               *buf++ = d3 + '0';
-                                       *buf++ = q + '0';
+/* There are two algorithms to print larger numbers.
+ * One is generic: divide by 1000000000 and repeatedly print
+ * groups of (up to) 9 digits. It's conceptually simple,
+ * but requires a (unsigned long long) / 1000000000 division.
+ *
+ * Second algorithm splits 64-bit unsigned long long into 16-bit chunks,
+ * manipulates them cleverly and generates groups of 4 decimal digits.
+ * It so happens that it does NOT require long long division.
+ *
+ * If long is > 32 bits, division of 64-bit values is relatively easy,
+ * and we will use the first algorithm.
+ * If long long is > 64 bits (strange architecture with VERY large long long),
+ * second algorithm can't be used, and we again use the first one.
+ *
+ * Else (if long is 32 bits and long long is 64 bits) we use second one.
+ */
 
-       return buf;
+#if BITS_PER_LONG != 32 || BITS_PER_LONG_LONG != 64
+
+/* First algorithm: generic */
+
+static
+char *put_dec(char *buf, unsigned long long n)
+{
+       if (n >= 100*1000*1000) {
+               while (n >= 1000*1000*1000)
+                       buf = put_dec_full9(buf, do_div(n, 1000*1000*1000));
+               if (n >= 100*1000*1000)
+                       return put_dec_full9(buf, n);
+       }
+       return put_dec_trunc8(buf, n);
 }
-/* No inlining helps gcc to use registers better */
+
+#else
+
+/* Second algorithm: valid only for 64-bit long longs */
+
 static noinline_for_stack
-char *put_dec(char *buf, unsigned long long num)
+char *put_dec_full4(char *buf, unsigned q)
 {
-       while (1) {
-               unsigned rem;
-               if (num < 100000)
-                       return put_dec_trunc(buf, num);
-               rem = do_div(num, 100000);
-               buf = put_dec_full(buf, rem);
-       }
+       unsigned r;
+       r      = (q * 0xcccd) >> 19;
+       *buf++ = (q - 10 * r) + '0';
+       q      = (r * 0x199a) >> 16;
+       *buf++ = (r - 10 * q)  + '0';
+       r      = (q * 0xcd) >> 11;
+       *buf++ = (q - 10 * r)  + '0';
+       *buf++ = r + '0';
+       return buf;
+}
+
+/* Based on code by Douglas W. Jones found at
+ * <http://www.cs.uiowa.edu/~jones/bcd/decimal.html#sixtyfour>
+ * (with permission from the author).
+ * Performs no 64-bit division and hence should be fast on 32-bit machines.
+ */
+static
+char *put_dec(char *buf, unsigned long long n)
+{
+       uint32_t d3, d2, d1, q, h;
+
+       if (n < 100*1000*1000)
+               return put_dec_trunc8(buf, n);
+
+       d1  = ((uint32_t)n >> 16); /* implicit "& 0xffff" */
+       h   = (n >> 32);
+       d2  = (h      ) & 0xffff;
+       d3  = (h >> 16); /* implicit "& 0xffff" */
+
+       q   = 656 * d3 + 7296 * d2 + 5536 * d1 + ((uint32_t)n & 0xffff);
+
+       buf = put_dec_full4(buf, q % 10000);
+       q   = q / 10000;
+
+       d1  = q + 7671 * d3 + 9496 * d2 + 6 * d1;
+       buf = put_dec_full4(buf, d1 % 10000);
+       q   = d1 / 10000;
+
+       d2  = q + 4749 * d3 + 42 * d2;
+       buf = put_dec_full4(buf, d2 % 10000);
+       q   = d2 / 10000;
+
+       d3  = q + 281 * d3;
+       if (!d3)
+               goto done;
+       buf = put_dec_full4(buf, d3 % 10000);
+       q   = d3 / 10000;
+       if (!q)
+               goto done;
+       buf = put_dec_full4(buf, q);
+ done:
+       while (buf[-1] == '0')
+               --buf;
+
+       return buf;
 }
 
+#endif
+
 /*
  * Convert passed number to decimal string.
  * Returns the length of string.  On buffer overflow, returns 0.
@@ -220,16 +313,22 @@ char *put_dec(char *buf, unsigned long long num)
  */
 int num_to_str(char *buf, int size, unsigned long long num)
 {
-       char tmp[21];           /* Enough for 2^64 in decimal */
+       char tmp[sizeof(num) * 3];
        int idx, len;
 
-       len = put_dec(tmp, num) - tmp;
+       /* put_dec() may work incorrectly for num = 0 (generate "", not "0") */
+       if (num <= 9) {
+               tmp[0] = '0' + num;
+               len = 1;
+       } else {
+               len = put_dec(tmp, num) - tmp;
+       }
 
        if (len > size)
                return 0;
        for (idx = 0; idx < len; ++idx)
                buf[idx] = tmp[len - idx - 1];
-       return  len;
+       return len;
 }
 
 #define ZEROPAD        1               /* pad with zero */
@@ -284,6 +383,7 @@ char *number(char *buf, char *end, unsigned long long num,
        char locase;
        int need_pfx = ((spec.flags & SPECIAL) && spec.base != 10);
        int i;
+       bool is_zero = num == 0LL;
 
        /* locase = 0 or 0x20. ORing digits or letters with 'locase'
         * produces same digits or (maybe lowercased) letters */
@@ -305,15 +405,16 @@ char *number(char *buf, char *end, unsigned long long num,
                }
        }
        if (need_pfx) {
-               spec.field_width--;
                if (spec.base == 16)
+                       spec.field_width -= 2;
+               else if (!is_zero)
                        spec.field_width--;
        }
 
        /* generate full string in tmp[], in reverse order */
        i = 0;
-       if (num == 0)
-               tmp[i++] = '0';
+       if (num < spec.base)
+               tmp[i++] = digits[num] | locase;
        /* Generic code, for any base:
        else do {
                tmp[i++] = (digits[do_div(num,base)] | locase);
@@ -353,9 +454,11 @@ char *number(char *buf, char *end, unsigned long long num,
        }
        /* "0x" / "0" prefix */
        if (need_pfx) {
-               if (buf < end)
-                       *buf = '0';
-               ++buf;
+               if (spec.base == 16 || !is_zero) {
+                       if (buf < end)
+                               *buf = '0';
+                       ++buf;
+               }
                if (spec.base == 16) {
                        if (buf < end)
                                *buf = ('X' | locase);
@@ -436,7 +539,7 @@ char *symbol_string(char *buf, char *end, void *ptr,
        else if (ext != 'f' && ext != 's')
                sprint_symbol(sym, value);
        else
-               kallsyms_lookup(value, NULL, NULL, NULL, sym);
+               sprint_symbol_no_offset(sym, value);
 
        return string(buf, end, sym, spec);
 #else
@@ -607,7 +710,7 @@ char *ip4_string(char *p, const u8 *addr, const char *fmt)
        }
        for (i = 0; i < 4; i++) {
                char temp[3];   /* hold each IP quad in reverse order */
-               int digits = put_dec_trunc(temp, addr[index]) - temp;
+               int digits = put_dec_trunc8(temp, addr[index]) - temp;
                if (leading_zeros) {
                        if (digits < 3)
                                *p++ = '0';
@@ -866,13 +969,15 @@ static noinline_for_stack
 char *pointer(const char *fmt, char *buf, char *end, void *ptr,
              struct printf_spec spec)
 {
+       int default_width = 2 * sizeof(void *) + (spec.flags & SPECIAL ? 2 : 0);
+
        if (!ptr && *fmt != 'K') {
                /*
                 * Print (null) with the same width as a pointer so it makes
                 * tabular output look nice.
                 */
                if (spec.field_width == -1)
-                       spec.field_width = 2 * sizeof(void *);
+                       spec.field_width = default_width;
                return string(buf, end, "(null)", spec);
        }
 
@@ -927,7 +1032,7 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr,
                 */
                if (in_irq() || in_serving_softirq() || in_nmi()) {
                        if (spec.field_width == -1)
-                               spec.field_width = 2 * sizeof(void *);
+                               spec.field_width = default_width;
                        return string(buf, end, "pK-error", spec);
                }
                if (!((kptr_restrict == 0) ||
@@ -944,7 +1049,7 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr,
        }
        spec.flags |= SMALL;
        if (spec.field_width == -1) {
-               spec.field_width = 2 * sizeof(void *);
+               spec.field_width = default_width;
                spec.flags |= ZEROPAD;
        }
        spec.base = 16;
index 39220026c797c57f2cf8c0cf6b2e4de028b2cbf2..b2176374b98e5e678ec93acda28cc0891d1c3717 100644 (file)
@@ -349,6 +349,16 @@ choice
          benefit.
 endchoice
 
+config CROSS_MEMORY_ATTACH
+       bool "Cross Memory Support"
+       depends on MMU
+       default y
+       help
+         Enabling this option adds the system calls process_vm_readv and
+         process_vm_writev which allow a process with the correct privileges
+         to directly read from or write to to another process's address space.
+         See the man page for more details.
+
 #
 # UP and nommu archs use km based percpu allocator
 #
index 8aada89efbbb636959df638967f754f367b87ba0..a156285ce88d9a19e529b54b8836efac559b7af7 100644 (file)
@@ -5,8 +5,11 @@
 mmu-y                  := nommu.o
 mmu-$(CONFIG_MMU)      := fremap.o highmem.o madvise.o memory.o mincore.o \
                           mlock.o mmap.o mprotect.o mremap.o msync.o rmap.o \
-                          vmalloc.o pagewalk.o pgtable-generic.o \
-                          process_vm_access.o
+                          vmalloc.o pagewalk.o pgtable-generic.o
+
+ifdef CONFIG_CROSS_MEMORY_ATTACH
+mmu-$(CONFIG_MMU)      += process_vm_access.o
+endif
 
 obj-y                  := filemap.o mempool.o oom_kill.o fadvise.o \
                           maccess.o page_alloc.o page-writeback.o \
@@ -25,7 +28,7 @@ endif
 obj-$(CONFIG_HAVE_MEMBLOCK) += memblock.o
 
 obj-$(CONFIG_BOUNCE)   += bounce.o
-obj-$(CONFIG_SWAP)     += page_io.o swap_state.o swapfile.o thrash.o
+obj-$(CONFIG_SWAP)     += page_io.o swap_state.o swapfile.o
 obj-$(CONFIG_HAS_DMA)  += dmapool.o
 obj-$(CONFIG_HUGETLBFS)        += hugetlb.o
 obj-$(CONFIG_NUMA)     += mempolicy.o
index 0131170c9d540a572c7b2ba3108ca5c2d9db30b7..ec4fcb7a56c8975492d656940906af6153136e51 100644 (file)
@@ -77,16 +77,16 @@ unsigned long __init bootmem_bootmap_pages(unsigned long pages)
  */
 static void __init link_bootmem(bootmem_data_t *bdata)
 {
-       struct list_head *iter;
+       bootmem_data_t *ent;
 
-       list_for_each(iter, &bdata_list) {
-               bootmem_data_t *ent;
-
-               ent = list_entry(iter, bootmem_data_t, list);
-               if (bdata->node_min_pfn < ent->node_min_pfn)
-                       break;
+       list_for_each_entry(ent, &bdata_list, list) {
+               if (bdata->node_min_pfn < ent->node_min_pfn) {
+                       list_add_tail(&bdata->list, &ent->list);
+                       return;
+               }
        }
-       list_add_tail(&bdata->list, iter);
+
+       list_add_tail(&bdata->list, &bdata_list);
 }
 
 /*
@@ -203,7 +203,8 @@ static unsigned long __init free_all_bootmem_core(bootmem_data_t *bdata)
                } else {
                        unsigned long off = 0;
 
-                       while (vec && off < BITS_PER_LONG) {
+                       vec >>= start & (BITS_PER_LONG - 1);
+                       while (vec) {
                                if (vec & 1) {
                                        page = pfn_to_page(start + off);
                                        __free_pages_bootmem(page, 0);
@@ -467,7 +468,7 @@ static unsigned long __init align_off(struct bootmem_data *bdata,
        return ALIGN(base + off, align) - base;
 }
 
-static void * __init alloc_bootmem_core(struct bootmem_data *bdata,
+static void * __init alloc_bootmem_bdata(struct bootmem_data *bdata,
                                        unsigned long size, unsigned long align,
                                        unsigned long goal, unsigned long limit)
 {
@@ -588,14 +589,14 @@ static void * __init alloc_arch_preferred_bootmem(bootmem_data_t *bdata,
                p_bdata = bootmem_arch_preferred_node(bdata, size, align,
                                                        goal, limit);
                if (p_bdata)
-                       return alloc_bootmem_core(p_bdata, size, align,
+                       return alloc_bootmem_bdata(p_bdata, size, align,
                                                        goal, limit);
        }
 #endif
        return NULL;
 }
 
-static void * __init ___alloc_bootmem_nopanic(unsigned long size,
+static void * __init alloc_bootmem_core(unsigned long size,
                                        unsigned long align,
                                        unsigned long goal,
                                        unsigned long limit)
@@ -603,7 +604,6 @@ static void * __init ___alloc_bootmem_nopanic(unsigned long size,
        bootmem_data_t *bdata;
        void *region;
 
-restart:
        region = alloc_arch_preferred_bootmem(NULL, size, align, goal, limit);
        if (region)
                return region;
@@ -614,11 +614,25 @@ restart:
                if (limit && bdata->node_min_pfn >= PFN_DOWN(limit))
                        break;
 
-               region = alloc_bootmem_core(bdata, size, align, goal, limit);
+               region = alloc_bootmem_bdata(bdata, size, align, goal, limit);
                if (region)
                        return region;
        }
 
+       return NULL;
+}
+
+static void * __init ___alloc_bootmem_nopanic(unsigned long size,
+                                             unsigned long align,
+                                             unsigned long goal,
+                                             unsigned long limit)
+{
+       void *ptr;
+
+restart:
+       ptr = alloc_bootmem_core(size, align, goal, limit);
+       if (ptr)
+               return ptr;
        if (goal) {
                goal = 0;
                goto restart;
@@ -684,21 +698,56 @@ void * __init __alloc_bootmem(unsigned long size, unsigned long align,
        return ___alloc_bootmem(size, align, goal, limit);
 }
 
-static void * __init ___alloc_bootmem_node(bootmem_data_t *bdata,
+static void * __init ___alloc_bootmem_node_nopanic(pg_data_t *pgdat,
                                unsigned long size, unsigned long align,
                                unsigned long goal, unsigned long limit)
 {
        void *ptr;
 
-       ptr = alloc_arch_preferred_bootmem(bdata, size, align, goal, limit);
+again:
+       ptr = alloc_arch_preferred_bootmem(pgdat->bdata, size,
+                                          align, goal, limit);
        if (ptr)
                return ptr;
 
-       ptr = alloc_bootmem_core(bdata, size, align, goal, limit);
+       ptr = alloc_bootmem_bdata(pgdat->bdata, size, align, goal, limit);
        if (ptr)
                return ptr;
 
-       return ___alloc_bootmem(size, align, goal, limit);
+       ptr = alloc_bootmem_core(size, align, goal, limit);
+       if (ptr)
+               return ptr;
+
+       if (goal) {
+               goal = 0;
+               goto again;
+       }
+
+       return NULL;
+}
+
+void * __init __alloc_bootmem_node_nopanic(pg_data_t *pgdat, unsigned long size,
+                                  unsigned long align, unsigned long goal)
+{
+       if (WARN_ON_ONCE(slab_is_available()))
+               return kzalloc_node(size, GFP_NOWAIT, pgdat->node_id);
+
+       return ___alloc_bootmem_node_nopanic(pgdat, size, align, goal, 0);
+}
+
+void * __init ___alloc_bootmem_node(pg_data_t *pgdat, unsigned long size,
+                                   unsigned long align, unsigned long goal,
+                                   unsigned long limit)
+{
+       void *ptr;
+
+       ptr = ___alloc_bootmem_node_nopanic(pgdat, size, align, goal, 0);
+       if (ptr)
+               return ptr;
+
+       printk(KERN_ALERT "bootmem alloc of %lu bytes failed!\n", size);
+       panic("Out of memory");
+       return NULL;
 }
 
 /**
@@ -722,7 +771,7 @@ void * __init __alloc_bootmem_node(pg_data_t *pgdat, unsigned long size,
        if (WARN_ON_ONCE(slab_is_available()))
                return kzalloc_node(size, GFP_NOWAIT, pgdat->node_id);
 
-       return  ___alloc_bootmem_node(pgdat->bdata, size, align, goal, 0);
+       return  ___alloc_bootmem_node(pgdat, size, align, goal, 0);
 }
 
 void * __init __alloc_bootmem_node_high(pg_data_t *pgdat, unsigned long size,
@@ -743,7 +792,7 @@ void * __init __alloc_bootmem_node_high(pg_data_t *pgdat, unsigned long size,
                unsigned long new_goal;
 
                new_goal = MAX_DMA32_PFN << PAGE_SHIFT;
-               ptr = alloc_bootmem_core(pgdat->bdata, size, align,
+               ptr = alloc_bootmem_bdata(pgdat->bdata, size, align,
                                                 new_goal, 0);
                if (ptr)
                        return ptr;
@@ -754,47 +803,6 @@ void * __init __alloc_bootmem_node_high(pg_data_t *pgdat, unsigned long size,
 
 }
 
-#ifdef CONFIG_SPARSEMEM
-/**
- * alloc_bootmem_section - allocate boot memory from a specific section
- * @size: size of the request in bytes
- * @section_nr: sparse map section to allocate from
- *
- * Return NULL on failure.
- */
-void * __init alloc_bootmem_section(unsigned long size,
-                                   unsigned long section_nr)
-{
-       bootmem_data_t *bdata;
-       unsigned long pfn, goal;
-
-       pfn = section_nr_to_pfn(section_nr);
-       goal = pfn << PAGE_SHIFT;
-       bdata = &bootmem_node_data[early_pfn_to_nid(pfn)];
-
-       return alloc_bootmem_core(bdata, size, SMP_CACHE_BYTES, goal, 0);
-}
-#endif
-
-void * __init __alloc_bootmem_node_nopanic(pg_data_t *pgdat, unsigned long size,
-                                  unsigned long align, unsigned long goal)
-{
-       void *ptr;
-
-       if (WARN_ON_ONCE(slab_is_available()))
-               return kzalloc_node(size, GFP_NOWAIT, pgdat->node_id);
-
-       ptr = alloc_arch_preferred_bootmem(pgdat->bdata, size, align, goal, 0);
-       if (ptr)
-               return ptr;
-
-       ptr = alloc_bootmem_core(pgdat->bdata, size, align, goal, 0);
-       if (ptr)
-               return ptr;
-
-       return __alloc_bootmem_nopanic(size, align, goal);
-}
-
 #ifndef ARCH_LOW_ADDRESS_LIMIT
 #define ARCH_LOW_ADDRESS_LIMIT 0xffffffffUL
 #endif
@@ -839,6 +847,6 @@ void * __init __alloc_bootmem_low_node(pg_data_t *pgdat, unsigned long size,
        if (WARN_ON_ONCE(slab_is_available()))
                return kzalloc_node(size, GFP_NOWAIT, pgdat->node_id);
 
-       return ___alloc_bootmem_node(pgdat->bdata, size, align,
-                               goal, ARCH_LOW_ADDRESS_LIMIT);
+       return ___alloc_bootmem_node(pgdat, size, align,
+                                    goal, ARCH_LOW_ADDRESS_LIMIT);
 }
index 5646c740f613ed1ec8b34a094f76d7934eed1aac..32e6f4136fa2297e13a6ac51444d50c18b78e9a3 100644 (file)
@@ -80,7 +80,7 @@ EXPORT_SYMBOL(__cleancache_init_shared_fs);
 static int cleancache_get_key(struct inode *inode,
                              struct cleancache_filekey *key)
 {
-       int (*fhfn)(struct dentry *, __u32 *fh, int *, int);
+       int (*fhfn)(struct inode *, __u32 *fh, int *, struct inode *);
        int len = 0, maxlen = CLEANCACHE_KEY_MAX;
        struct super_block *sb = inode->i_sb;
 
@@ -88,9 +88,7 @@ static int cleancache_get_key(struct inode *inode,
        if (sb->s_export_op != NULL) {
                fhfn = sb->s_export_op->encode_fh;
                if  (fhfn) {
-                       struct dentry d;
-                       d.d_inode = inode;
-                       len = (*fhfn)(&d, &key->u.fh[0], &maxlen, 0);
+                       len = (*fhfn)(inode, &key->u.fh[0], &maxlen, NULL);
                        if (len <= 0 || len == 255)
                                return -1;
                        if (maxlen > CLEANCACHE_KEY_MAX)
index da7d35ea51034796e27fc7274a382c338c5a03e8..4ac338af512099aae2dbb35c64bc96e822e8c162 100644 (file)
@@ -226,7 +226,8 @@ isolate_migratepages_range(struct zone *zone, struct compact_control *cc,
        unsigned long last_pageblock_nr = 0, pageblock_nr;
        unsigned long nr_scanned = 0, nr_isolated = 0;
        struct list_head *migratelist = &cc->migratepages;
-       isolate_mode_t mode = ISOLATE_ACTIVE|ISOLATE_INACTIVE;
+       isolate_mode_t mode = 0;
+       struct lruvec *lruvec;
 
        /*
         * Ensure that there are not too many pages isolated from the LRU
@@ -235,7 +236,7 @@ isolate_migratepages_range(struct zone *zone, struct compact_control *cc,
         */
        while (unlikely(too_many_isolated(zone))) {
                /* async migration should just abort */
-               if (!cc->sync)
+               if (cc->mode != COMPACT_SYNC)
                        return 0;
 
                congestion_wait(BLK_RW_ASYNC, HZ/10);
@@ -303,7 +304,8 @@ isolate_migratepages_range(struct zone *zone, struct compact_control *cc,
                 * satisfies the allocation
                 */
                pageblock_nr = low_pfn >> pageblock_order;
-               if (!cc->sync && last_pageblock_nr != pageblock_nr &&
+               if (cc->mode != COMPACT_SYNC &&
+                   last_pageblock_nr != pageblock_nr &&
                    !migrate_async_suitable(get_pageblock_migratetype(page))) {
                        low_pfn += pageblock_nr_pages;
                        low_pfn = ALIGN(low_pfn, pageblock_nr_pages) - 1;
@@ -324,17 +326,19 @@ isolate_migratepages_range(struct zone *zone, struct compact_control *cc,
                        continue;
                }
 
-               if (!cc->sync)
+               if (cc->mode != COMPACT_SYNC)
                        mode |= ISOLATE_ASYNC_MIGRATE;
 
+               lruvec = mem_cgroup_page_lruvec(page, zone);
+
                /* Try isolate the page */
-               if (__isolate_lru_page(page, mode, 0) != 0)
+               if (__isolate_lru_page(page, mode) != 0)
                        continue;
 
                VM_BUG_ON(PageTransCompound(page));
 
                /* Successfully isolated */
-               del_page_from_lru_list(zone, page, page_lru(page));
+               del_page_from_lru_list(page, lruvec, page_lru(page));
                list_add(&page->lru, migratelist);
                cc->nr_migratepages++;
                nr_isolated++;
@@ -357,27 +361,90 @@ isolate_migratepages_range(struct zone *zone, struct compact_control *cc,
 
 #endif /* CONFIG_COMPACTION || CONFIG_CMA */
 #ifdef CONFIG_COMPACTION
+/*
+ * Returns true if MIGRATE_UNMOVABLE pageblock was successfully
+ * converted to MIGRATE_MOVABLE type, false otherwise.
+ */
+static bool rescue_unmovable_pageblock(struct page *page)
+{
+       unsigned long pfn, start_pfn, end_pfn;
+       struct page *start_page, *end_page;
+
+       pfn = page_to_pfn(page);
+       start_pfn = pfn & ~(pageblock_nr_pages - 1);
+       end_pfn = start_pfn + pageblock_nr_pages;
+
+       start_page = pfn_to_page(start_pfn);
+       end_page = pfn_to_page(end_pfn);
+
+       /* Do not deal with pageblocks that overlap zones */
+       if (page_zone(start_page) != page_zone(end_page))
+               return false;
+
+       for (page = start_page, pfn = start_pfn; page < end_page; pfn++,
+                                                                 page++) {
+               if (!pfn_valid_within(pfn))
+                       continue;
+
+               if (PageBuddy(page)) {
+                       int order = page_order(page);
+
+                       pfn += (1 << order) - 1;
+                       page += (1 << order) - 1;
+
+                       continue;
+               } else if (page_count(page) == 0 || PageLRU(page))
+                       continue;
+
+               return false;
+       }
+
+       set_pageblock_migratetype(page, MIGRATE_MOVABLE);
+       move_freepages_block(page_zone(page), page, MIGRATE_MOVABLE);
+       return true;
+}
+
+enum smt_result {
+       GOOD_AS_MIGRATION_TARGET,
+       FAIL_UNMOVABLE_TARGET,
+       FAIL_BAD_TARGET,
+};
 
-/* Returns true if the page is within a block suitable for migration to */
-static bool suitable_migration_target(struct page *page)
+/*
+ * Returns GOOD_AS_MIGRATION_TARGET if the page is within a block
+ * suitable for migration to, FAIL_UNMOVABLE_TARGET if the page
+ * is within a MIGRATE_UNMOVABLE block, FAIL_BAD_TARGET otherwise.
+ */
+static enum smt_result suitable_migration_target(struct page *page,
+                                     struct compact_control *cc)
 {
 
        int migratetype = get_pageblock_migratetype(page);
 
        /* Don't interfere with memory hot-remove or the min_free_kbytes blocks */
        if (migratetype == MIGRATE_ISOLATE || migratetype == MIGRATE_RESERVE)
-               return false;
+               return FAIL_BAD_TARGET;
 
        /* If the page is a large free page, then allow migration */
        if (PageBuddy(page) && page_order(page) >= pageblock_order)
-               return true;
+               return GOOD_AS_MIGRATION_TARGET;
 
        /* If the block is MIGRATE_MOVABLE or MIGRATE_CMA, allow migration */
-       if (migrate_async_suitable(migratetype))
-               return true;
+       if (cc->mode != COMPACT_ASYNC_UNMOVABLE &&
+           migrate_async_suitable(migratetype))
+               return GOOD_AS_MIGRATION_TARGET;
+
+       if (cc->mode == COMPACT_ASYNC_MOVABLE &&
+           migratetype == MIGRATE_UNMOVABLE)
+               return FAIL_UNMOVABLE_TARGET;
+
+       if (cc->mode != COMPACT_ASYNC_MOVABLE &&
+           migratetype == MIGRATE_UNMOVABLE &&
+           rescue_unmovable_pageblock(page))
+               return GOOD_AS_MIGRATION_TARGET;
 
        /* Otherwise skip the block */
-       return false;
+       return FAIL_BAD_TARGET;
 }
 
 /*
@@ -410,6 +477,13 @@ static void isolate_freepages(struct zone *zone,
 
        zone_end_pfn = zone->zone_start_pfn + zone->spanned_pages;
 
+       /*
+        * isolate_freepages() may be called more than once during
+        * compact_zone_order() run and we want only the most recent
+        * count.
+        */
+       cc->nr_pageblocks_skipped = 0;
+
        /*
         * Isolate free pages until enough are available to migrate the
         * pages on cc->migratepages. We stop searching if the migrate
@@ -418,6 +492,7 @@ static void isolate_freepages(struct zone *zone,
        for (; pfn > low_pfn && cc->nr_migratepages > nr_freepages;
                                        pfn -= pageblock_nr_pages) {
                unsigned long isolated;
+               enum smt_result ret;
 
                if (!pfn_valid(pfn))
                        continue;
@@ -434,9 +509,12 @@ static void isolate_freepages(struct zone *zone,
                        continue;
 
                /* Check the block is suitable for migration */
-               if (!suitable_migration_target(page))
+               ret = suitable_migration_target(page, cc);
+               if (ret != GOOD_AS_MIGRATION_TARGET) {
+                       if (ret == FAIL_UNMOVABLE_TARGET)
+                               cc->nr_pageblocks_skipped++;
                        continue;
-
+               }
                /*
                 * Found a block suitable for isolating free pages from. Now
                 * we disabled interrupts, double check things are ok and
@@ -445,12 +523,14 @@ static void isolate_freepages(struct zone *zone,
                 */
                isolated = 0;
                spin_lock_irqsave(&zone->lock, flags);
-               if (suitable_migration_target(page)) {
+               ret = suitable_migration_target(page, cc);
+               if (ret == GOOD_AS_MIGRATION_TARGET) {
                        end_pfn = min(pfn + pageblock_nr_pages, zone_end_pfn);
                        isolated = isolate_freepages_block(pfn, end_pfn,
                                                           freelist, false);
                        nr_freepages += isolated;
-               }
+               } else if (ret == FAIL_UNMOVABLE_TARGET)
+                       cc->nr_pageblocks_skipped++;
                spin_unlock_irqrestore(&zone->lock, flags);
 
                /*
@@ -682,8 +762,9 @@ static int compact_zone(struct zone *zone, struct compact_control *cc)
 
                nr_migrate = cc->nr_migratepages;
                err = migrate_pages(&cc->migratepages, compaction_alloc,
-                               (unsigned long)cc, false,
-                               cc->sync ? MIGRATE_SYNC_LIGHT : MIGRATE_ASYNC);
+                       (unsigned long)&cc->freepages, false,
+                       (cc->mode == COMPACT_SYNC) ? MIGRATE_SYNC_LIGHT
+                                                     : MIGRATE_ASYNC);
                update_nr_listpages(cc);
                nr_remaining = cc->nr_migratepages;
 
@@ -712,7 +793,8 @@ out:
 
 static unsigned long compact_zone_order(struct zone *zone,
                                 int order, gfp_t gfp_mask,
-                                bool sync)
+                                enum compact_mode mode,
+                                unsigned long *nr_pageblocks_skipped)
 {
        struct compact_control cc = {
                .nr_freepages = 0,
@@ -720,12 +802,17 @@ static unsigned long compact_zone_order(struct zone *zone,
                .order = order,
                .migratetype = allocflags_to_migratetype(gfp_mask),
                .zone = zone,
-               .sync = sync,
+               .mode = mode,
        };
+       unsigned long rc;
+
        INIT_LIST_HEAD(&cc.freepages);
        INIT_LIST_HEAD(&cc.migratepages);
 
-       return compact_zone(zone, &cc);
+       rc = compact_zone(zone, &cc);
+       *nr_pageblocks_skipped = cc.nr_pageblocks_skipped;
+
+       return rc;
 }
 
 int sysctl_extfrag_threshold = 500;
@@ -750,6 +837,8 @@ unsigned long try_to_compact_pages(struct zonelist *zonelist,
        struct zoneref *z;
        struct zone *zone;
        int rc = COMPACT_SKIPPED;
+       unsigned long nr_pageblocks_skipped;
+       enum compact_mode mode;
 
        /*
         * Check whether it is worth even starting compaction. The order check is
@@ -766,12 +855,22 @@ unsigned long try_to_compact_pages(struct zonelist *zonelist,
                                                                nodemask) {
                int status;
 
-               status = compact_zone_order(zone, order, gfp_mask, sync);
+               mode = sync ? COMPACT_SYNC : COMPACT_ASYNC_MOVABLE;
+retry:
+               status = compact_zone_order(zone, order, gfp_mask, mode,
+                                               &nr_pageblocks_skipped);
                rc = max(status, rc);
 
                /* If a normal allocation would succeed, stop compacting */
                if (zone_watermark_ok(zone, order, low_wmark_pages(zone), 0, 0))
                        break;
+
+               if (rc == COMPACT_COMPLETE && mode == COMPACT_ASYNC_MOVABLE) {
+                       if (nr_pageblocks_skipped) {
+                               mode = COMPACT_ASYNC_UNMOVABLE;
+                               goto retry;
+                       }
+               }
        }
 
        return rc;
@@ -805,7 +904,7 @@ static int __compact_pgdat(pg_data_t *pgdat, struct compact_control *cc)
                        if (ok && cc->order > zone->compact_order_failed)
                                zone->compact_order_failed = cc->order + 1;
                        /* Currently async compaction is never deferred. */
-                       else if (!ok && cc->sync)
+                       else if (!ok && cc->mode == COMPACT_SYNC)
                                defer_compaction(zone, cc->order);
                }
 
@@ -820,7 +919,7 @@ int compact_pgdat(pg_data_t *pgdat, int order)
 {
        struct compact_control cc = {
                .order = order,
-               .sync = false,
+               .mode = COMPACT_ASYNC_MOVABLE,
        };
 
        return __compact_pgdat(pgdat, &cc);
@@ -830,7 +929,7 @@ static int compact_node(int nid)
 {
        struct compact_control cc = {
                .order = -1,
-               .sync = true,
+               .mode = COMPACT_SYNC,
        };
 
        return __compact_pgdat(NODE_DATA(nid), &cc);
index 79c4b2b0b14eec1d05c93e3493dd02e0fd182829..a4a5260b0279b77b37738540b1e8c24fb446a3e5 100644 (file)
@@ -29,7 +29,6 @@
 #include <linux/pagevec.h>
 #include <linux/blkdev.h>
 #include <linux/security.h>
-#include <linux/syscalls.h>
 #include <linux/cpuset.h>
 #include <linux/hardirq.h> /* for BUG_ON(!in_atomic()) only */
 #include <linux/memcontrol.h>
@@ -1478,44 +1477,6 @@ out:
 }
 EXPORT_SYMBOL(generic_file_aio_read);
 
-static ssize_t
-do_readahead(struct address_space *mapping, struct file *filp,
-            pgoff_t index, unsigned long nr)
-{
-       if (!mapping || !mapping->a_ops || !mapping->a_ops->readpage)
-               return -EINVAL;
-
-       force_page_cache_readahead(mapping, filp, index, nr);
-       return 0;
-}
-
-SYSCALL_DEFINE(readahead)(int fd, loff_t offset, size_t count)
-{
-       ssize_t ret;
-       struct file *file;
-
-       ret = -EBADF;
-       file = fget(fd);
-       if (file) {
-               if (file->f_mode & FMODE_READ) {
-                       struct address_space *mapping = file->f_mapping;
-                       pgoff_t start = offset >> PAGE_CACHE_SHIFT;
-                       pgoff_t end = (offset + count - 1) >> PAGE_CACHE_SHIFT;
-                       unsigned long len = end - start + 1;
-                       ret = do_readahead(mapping, file, start, len);
-               }
-               fput(file);
-       }
-       return ret;
-}
-#ifdef CONFIG_HAVE_SYSCALL_WRAPPERS
-asmlinkage long SyS_readahead(long fd, loff_t offset, long count)
-{
-       return SYSC_readahead((int) fd, offset, (size_t) count);
-}
-SYSCALL_ALIAS(sys_readahead, SyS_readahead);
-#endif
-
 #ifdef CONFIG_MMU
 /**
  * page_cache_read - adds requested page to the page cache if not already there
@@ -1938,71 +1899,6 @@ struct page *read_cache_page(struct address_space *mapping,
 }
 EXPORT_SYMBOL(read_cache_page);
 
-/*
- * The logic we want is
- *
- *     if suid or (sgid and xgrp)
- *             remove privs
- */
-int should_remove_suid(struct dentry *dentry)
-{
-       umode_t mode = dentry->d_inode->i_mode;
-       int kill = 0;
-
-       /* suid always must be killed */
-       if (unlikely(mode & S_ISUID))
-               kill = ATTR_KILL_SUID;
-
-       /*
-        * sgid without any exec bits is just a mandatory locking mark; leave
-        * it alone.  If some exec bits are set, it's a real sgid; kill it.
-        */
-       if (unlikely((mode & S_ISGID) && (mode & S_IXGRP)))
-               kill |= ATTR_KILL_SGID;
-
-       if (unlikely(kill && !capable(CAP_FSETID) && S_ISREG(mode)))
-               return kill;
-
-       return 0;
-}
-EXPORT_SYMBOL(should_remove_suid);
-
-static int __remove_suid(struct dentry *dentry, int kill)
-{
-       struct iattr newattrs;
-
-       newattrs.ia_valid = ATTR_FORCE | kill;
-       return notify_change(dentry, &newattrs);
-}
-
-int file_remove_suid(struct file *file)
-{
-       struct dentry *dentry = file->f_path.dentry;
-       struct inode *inode = dentry->d_inode;
-       int killsuid;
-       int killpriv;
-       int error = 0;
-
-       /* Fast path for nothing security related */
-       if (IS_NOSEC(inode))
-               return 0;
-
-       killsuid = should_remove_suid(dentry);
-       killpriv = security_inode_need_killpriv(dentry);
-
-       if (killpriv < 0)
-               return killpriv;
-       if (killpriv)
-               error = security_inode_killpriv(dentry);
-       if (!error && killsuid)
-               error = __remove_suid(dentry, killsuid);
-       if (!error && (inode->i_sb->s_flags & MS_NOSEC))
-               inode->i_flags |= S_NOSEC;
-
-       return error;
-}
-EXPORT_SYMBOL(file_remove_suid);
-
 static size_t __iovec_copy_from_user_inatomic(char *vaddr,
                        const struct iovec *iov, size_t base, size_t bytes)
 {
@@ -2528,7 +2424,9 @@ ssize_t __generic_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
        if (err)
                goto out;
 
-       file_update_time(file);
+       err = file_update_time(file);
+       if (err)
+               goto out;
 
        /* coalesce the iovecs and go direct-to-BIO for O_DIRECT */
        if (unlikely(file->f_flags & O_DIRECT)) {
index a4eb3113222912c9aada14bd92c6b68d01577b73..213ca1f5340980e1ce6fad8d4f12e50858d61397 100644 (file)
@@ -426,7 +426,9 @@ xip_file_write(struct file *filp, const char __user *buf, size_t len,
        if (ret)
                goto out_backing;
 
-       file_update_time(filp);
+       ret = file_update_time(filp);
+       if (ret)
+               goto out_backing;
 
        ret = __xip_file_write (filp, buf, count, pos, ppos);
 
index f0e5306eeb55e8e179da3abbe6c033045b6ad073..57c4b93090151f2acbc1271b7b214fe5bc96478c 100644 (file)
@@ -636,16 +636,12 @@ static int __do_huge_pmd_anonymous_page(struct mm_struct *mm,
                                        unsigned long haddr, pmd_t *pmd,
                                        struct page *page)
 {
-       int ret = 0;
        pgtable_t pgtable;
 
        VM_BUG_ON(!PageCompound(page));
        pgtable = pte_alloc_one(mm, haddr);
-       if (unlikely(!pgtable)) {
-               mem_cgroup_uncharge_page(page);
-               put_page(page);
+       if (unlikely(!pgtable))
                return VM_FAULT_OOM;
-       }
 
        clear_huge_page(page, haddr, HPAGE_PMD_NR);
        __SetPageUptodate(page);
@@ -675,7 +671,7 @@ static int __do_huge_pmd_anonymous_page(struct mm_struct *mm,
                spin_unlock(&mm->page_table_lock);
        }
 
-       return ret;
+       return 0;
 }
 
 static inline gfp_t alloc_hugepage_gfpmask(int defrag, gfp_t extra_gfp)
@@ -724,8 +720,14 @@ int do_huge_pmd_anonymous_page(struct mm_struct *mm, struct vm_area_struct *vma,
                        put_page(page);
                        goto out;
                }
+               if (unlikely(__do_huge_pmd_anonymous_page(mm, vma, haddr, pmd,
+                                                         page))) {
+                       mem_cgroup_uncharge_page(page);
+                       put_page(page);
+                       goto out;
+               }
 
-               return __do_huge_pmd_anonymous_page(mm, vma, haddr, pmd, page);
+               return 0;
        }
 out:
        /*
@@ -950,6 +952,8 @@ int do_huge_pmd_wp_page(struct mm_struct *mm, struct vm_area_struct *vma,
                count_vm_event(THP_FAULT_FALLBACK);
                ret = do_huge_pmd_wp_page_fallback(mm, vma, address,
                                                   pmd, orig_pmd, page, haddr);
+               if (ret & VM_FAULT_OOM)
+                       split_huge_page(page);
                put_page(page);
                goto out;
        }
@@ -957,6 +961,7 @@ int do_huge_pmd_wp_page(struct mm_struct *mm, struct vm_area_struct *vma,
 
        if (unlikely(mem_cgroup_newpage_charge(new_page, mm, GFP_KERNEL))) {
                put_page(new_page);
+               split_huge_page(page);
                put_page(page);
                ret |= VM_FAULT_OOM;
                goto out;
@@ -968,8 +973,10 @@ int do_huge_pmd_wp_page(struct mm_struct *mm, struct vm_area_struct *vma,
        spin_lock(&mm->page_table_lock);
        put_page(page);
        if (unlikely(!pmd_same(*pmd, orig_pmd))) {
+               spin_unlock(&mm->page_table_lock);
                mem_cgroup_uncharge_page(new_page);
                put_page(new_page);
+               goto out;
        } else {
                pmd_t entry;
                VM_BUG_ON(!PageHead(page));
@@ -1224,10 +1231,13 @@ static void __split_huge_page_refcount(struct page *page)
 {
        int i;
        struct zone *zone = page_zone(page);
+       struct lruvec *lruvec;
        int tail_count = 0;
 
        /* prevent PageLRU to go away from under us, and freeze lru stats */
        spin_lock_irq(&zone->lru_lock);
+       lruvec = mem_cgroup_page_lruvec(page, zone);
+
        compound_lock(page);
        /* complete memcg works before add pages to LRU */
        mem_cgroup_split_huge_fixup(page);
@@ -1302,13 +1312,12 @@ static void __split_huge_page_refcount(struct page *page)
                BUG_ON(!PageDirty(page_tail));
                BUG_ON(!PageSwapBacked(page_tail));
 
-
-               lru_add_page_tail(zone, page, page_tail);
+               lru_add_page_tail(page, page_tail, lruvec);
        }
        atomic_sub(tail_count, &page->_count);
        BUG_ON(atomic_read(&page->_count) <= 0);
 
-       __dec_zone_page_state(page, NR_ANON_TRANSPARENT_HUGEPAGES);
+       __mod_zone_page_state(zone, NR_ANON_TRANSPARENT_HUGEPAGES, -1);
        __mod_zone_page_state(zone, NR_ANON_PAGES, HPAGE_PMD_NR);
 
        ClearPageCompound(page);
index 4e28416c47fb5a299ff2bab6ae94afd05e802730..e198831276a3eab77b4a89fc0e1457a5a45d025d 100644 (file)
@@ -273,8 +273,8 @@ static long region_count(struct list_head *head, long f, long t)
 
        /* Locate each segment we overlap with, and count that overlap. */
        list_for_each_entry(rg, head, link) {
-               int seg_from;
-               int seg_to;
+               long seg_from;
+               long seg_to;
 
                if (rg->to <= f)
                        continue;
@@ -2157,6 +2157,15 @@ static void hugetlb_vm_op_open(struct vm_area_struct *vma)
                kref_get(&reservations->refs);
 }
 
+static void resv_map_put(struct vm_area_struct *vma)
+{
+       struct resv_map *reservations = vma_resv_map(vma);
+
+       if (!reservations)
+               return;
+       kref_put(&reservations->refs, resv_map_release);
+}
+
 static void hugetlb_vm_op_close(struct vm_area_struct *vma)
 {
        struct hstate *h = hstate_vma(vma);
@@ -2173,7 +2182,7 @@ static void hugetlb_vm_op_close(struct vm_area_struct *vma)
                reserve = (end - start) -
                        region_count(&reservations->regions, start, end);
 
-               kref_put(&reservations->refs, resv_map_release);
+               resv_map_put(vma);
 
                if (reserve) {
                        hugetlb_acct_memory(h, -reserve);
@@ -2991,12 +3000,16 @@ int hugetlb_reserve_pages(struct inode *inode,
                set_vma_resv_flags(vma, HPAGE_RESV_OWNER);
        }
 
-       if (chg < 0)
-               return chg;
+       if (chg < 0) {
+               ret = chg;
+               goto out_err;
+       }
 
        /* There must be enough pages in the subpool for the mapping */
-       if (hugepage_subpool_get_pages(spool, chg))
-               return -ENOSPC;
+       if (hugepage_subpool_get_pages(spool, chg)) {
+               ret = -ENOSPC;
+               goto out_err;
+       }
 
        /*
         * Check enough hugepages are available for the reservation.
@@ -3005,7 +3018,7 @@ int hugetlb_reserve_pages(struct inode *inode,
        ret = hugetlb_acct_memory(h, chg);
        if (ret < 0) {
                hugepage_subpool_put_pages(spool, chg);
-               return ret;
+               goto out_err;
        }
 
        /*
@@ -3022,6 +3035,10 @@ int hugetlb_reserve_pages(struct inode *inode,
        if (!vma || vma->vm_flags & VM_MAYSHARE)
                region_add(&inode->i_mapping->private_list, from, to);
        return 0;
+out_err:
+       if (vma)
+               resv_map_put(vma);
+       return ret;
 }
 
 void hugetlb_unreserve_pages(struct inode *inode, long offset, long freed)
index aee4761cf9a92b8b88fe16a20794c765e74c2cac..5cbb78190041573ee4e92e77a12a8f129514862f 100644 (file)
@@ -94,6 +94,9 @@ extern void putback_lru_page(struct page *page);
 /*
  * in mm/page_alloc.c
  */
+extern void set_pageblock_migratetype(struct page *page, int migratetype);
+extern int move_freepages_block(struct zone *zone, struct page *page,
+                               int migratetype);
 extern void __free_pages_bootmem(struct page *page, unsigned int order);
 extern void prep_compound_page(struct page *page, unsigned long order);
 #ifdef CONFIG_MEMORY_FAILURE
@@ -101,6 +104,7 @@ extern bool is_free_buddy_page(struct page *page);
 #endif
 
 #if defined CONFIG_COMPACTION || defined CONFIG_CMA
+#include <linux/compaction.h>
 
 /*
  * in mm/compaction.c
@@ -119,11 +123,14 @@ struct compact_control {
        unsigned long nr_migratepages;  /* Number of pages to migrate */
        unsigned long free_pfn;         /* isolate_freepages search base */
        unsigned long migrate_pfn;      /* isolate_migratepages search base */
-       bool sync;                      /* Synchronous migration */
+       enum compact_mode mode;         /* Compaction mode */
 
        int order;                      /* order a direct compactor needs */
        int migratetype;                /* MOVABLE, RECLAIMABLE etc */
        struct zone *zone;
+
+       /* Number of UNMOVABLE destination pageblocks skipped during scan */
+       unsigned long nr_pageblocks_skipped;
 };
 
 unsigned long
@@ -164,7 +171,8 @@ static inline void munlock_vma_pages_all(struct vm_area_struct *vma)
  * to determine if it's being mapped into a LOCKED vma.
  * If so, mark page as mlocked.
  */
-static inline int is_mlocked_vma(struct vm_area_struct *vma, struct page *page)
+static inline int mlocked_vma_newpage(struct vm_area_struct *vma,
+                                   struct page *page)
 {
        VM_BUG_ON(PageLRU(page));
 
@@ -222,7 +230,7 @@ extern unsigned long vma_address(struct page *page,
                                 struct vm_area_struct *vma);
 #endif
 #else /* !CONFIG_MMU */
-static inline int is_mlocked_vma(struct vm_area_struct *v, struct page *p)
+static inline int mlocked_vma_newpage(struct vm_area_struct *v, struct page *p)
 {
        return 0;
 }
@@ -342,3 +350,7 @@ extern u64 hwpoison_filter_flags_mask;
 extern u64 hwpoison_filter_flags_value;
 extern u64 hwpoison_filter_memcg;
 extern u32 hwpoison_filter_enable;
+
+extern unsigned long vm_mmap_pgoff(struct file *, unsigned long,
+        unsigned long, unsigned long,
+        unsigned long, unsigned long);
index 1ccbba5b667414e717db987b85da9e0f1691268f..deff1b64a08c36ef4857590e9913717488953014 100644 (file)
 #include <linux/mempolicy.h>
 #include <linux/page-isolation.h>
 #include <linux/hugetlb.h>
+#include <linux/falloc.h>
 #include <linux/sched.h>
 #include <linux/ksm.h>
+#include <linux/fs.h>
 
 /*
  * Any behaviour which results in changes to the vma->vm_flags needs to
@@ -200,8 +202,7 @@ static long madvise_remove(struct vm_area_struct *vma,
                                struct vm_area_struct **prev,
                                unsigned long start, unsigned long end)
 {
-       struct address_space *mapping;
-       loff_t offset, endoff;
+       loff_t offset;
        int error;
 
        *prev = NULL;   /* tell sys_madvise we drop mmap_sem */
@@ -217,16 +218,14 @@ static long madvise_remove(struct vm_area_struct *vma,
        if ((vma->vm_flags & (VM_SHARED|VM_WRITE)) != (VM_SHARED|VM_WRITE))
                return -EACCES;
 
-       mapping = vma->vm_file->f_mapping;
-
        offset = (loff_t)(start - vma->vm_start)
                        + ((loff_t)vma->vm_pgoff << PAGE_SHIFT);
-       endoff = (loff_t)(end - vma->vm_start - 1)
-                       + ((loff_t)vma->vm_pgoff << PAGE_SHIFT);
 
-       /* vmtruncate_range needs to take i_mutex */
+       /* filesystem's fallocate may need to take i_mutex */
        up_read(&current->mm->mmap_sem);
-       error = vmtruncate_range(mapping->host, offset, endoff);
+       error = do_fallocate(vma->vm_file,
+                               FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
+                               offset, end - start);
        down_read(&current->mm->mmap_sem);
        return error;
 }
index a44eab3157f8dc4b25e686b643ccacbc8449e041..952123eba43371a5e6d26ff8a5b7ad934747c027 100644 (file)
@@ -37,6 +37,8 @@ struct memblock memblock __initdata_memblock = {
 
 int memblock_debug __initdata_memblock;
 static int memblock_can_resize __initdata_memblock;
+static int memblock_memory_in_slab __initdata_memblock = 0;
+static int memblock_reserved_in_slab __initdata_memblock = 0;
 
 /* inline so we don't get a warning when pr_debug is compiled out */
 static inline const char *memblock_type_name(struct memblock_type *type)
@@ -187,6 +189,7 @@ static int __init_memblock memblock_double_array(struct memblock_type *type)
        struct memblock_region *new_array, *old_array;
        phys_addr_t old_size, new_size, addr;
        int use_slab = slab_is_available();
+       int *in_slab;
 
        /* We don't allow resizing until we know about the reserved regions
         * of memory that aren't suitable for allocation
@@ -198,6 +201,12 @@ static int __init_memblock memblock_double_array(struct memblock_type *type)
        old_size = type->max * sizeof(struct memblock_region);
        new_size = old_size << 1;
 
+       /* Retrieve the slab flag */
+       if (type == &memblock.memory)
+               in_slab = &memblock_memory_in_slab;
+       else
+               in_slab = &memblock_reserved_in_slab;
+
        /* Try to find some space for it.
         *
         * WARNING: We assume that either slab_is_available() and we use it or
@@ -212,14 +221,15 @@ static int __init_memblock memblock_double_array(struct memblock_type *type)
        if (use_slab) {
                new_array = kmalloc(new_size, GFP_KERNEL);
                addr = new_array ? __pa(new_array) : 0;
-       } else
+       } else {
                addr = memblock_find_in_range(0, MEMBLOCK_ALLOC_ACCESSIBLE, new_size, sizeof(phys_addr_t));
+               new_array = addr ? __va(addr) : 0;
+       }
        if (!addr) {
                pr_err("memblock: Failed to double %s array from %ld to %ld entries !\n",
                       memblock_type_name(type), type->max, type->max * 2);
                return -1;
        }
-       new_array = __va(addr);
 
        memblock_dbg("memblock: %s array is doubled to %ld at [%#010llx-%#010llx]",
                 memblock_type_name(type), type->max * 2, (u64)addr, (u64)addr + new_size - 1);
@@ -234,22 +244,24 @@ static int __init_memblock memblock_double_array(struct memblock_type *type)
        type->regions = new_array;
        type->max <<= 1;
 
-       /* If we use SLAB that's it, we are done */
-       if (use_slab)
-               return 0;
-
-       /* Add the new reserved region now. Should not fail ! */
-       BUG_ON(memblock_reserve(addr, new_size));
-
-       /* If the array wasn't our static init one, then free it. We only do
-        * that before SLAB is available as later on, we don't know whether
-        * to use kfree or free_bootmem_pages(). Shouldn't be a big deal
-        * anyways
+       /* Free old array. We needn't free it if the array is the
+        * static one
         */
-       if (old_array != memblock_memory_init_regions &&
-           old_array != memblock_reserved_init_regions)
+       if (*in_slab)
+               kfree(old_array);
+       else if (old_array != memblock_memory_init_regions &&
+                old_array != memblock_reserved_init_regions)
                memblock_free(__pa(old_array), old_size);
 
+       /* Reserve the new array if that comes from the memblock.
+        * Otherwise, we needn't do it
+        */
+       if (!use_slab)
+               BUG_ON(memblock_reserve(addr, new_size));
+
+       /* Update slab flag */
+       *in_slab = use_slab;
+
        return 0;
 }
 
index f342778a0c0a2649b00a3a284036ea17f05023a1..ac35bccadb7b9f53606d445a961e442e891aa94a 100644 (file)
@@ -59,7 +59,7 @@
 
 struct cgroup_subsys mem_cgroup_subsys __read_mostly;
 #define MEM_CGROUP_RECLAIM_RETRIES     5
-struct mem_cgroup *root_mem_cgroup __read_mostly;
+static struct mem_cgroup *root_mem_cgroup __read_mostly;
 
 #ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP
 /* Turned on only when memory cgroup is enabled && really_do_swap_account = 1 */
@@ -73,7 +73,7 @@ static int really_do_swap_account __initdata = 0;
 #endif
 
 #else
-#define do_swap_account                (0)
+#define do_swap_account                0
 #endif
 
 
@@ -88,18 +88,31 @@ enum mem_cgroup_stat_index {
        MEM_CGROUP_STAT_RSS,       /* # of pages charged as anon rss */
        MEM_CGROUP_STAT_FILE_MAPPED,  /* # of pages charged as file rss */
        MEM_CGROUP_STAT_SWAPOUT, /* # of pages, swapped out */
-       MEM_CGROUP_STAT_DATA, /* end of data requires synchronization */
        MEM_CGROUP_STAT_NSTATS,
 };
 
+static const char * const mem_cgroup_stat_names[] = {
+       "cache",
+       "rss",
+       "mapped_file",
+       "swap",
+};
+
 enum mem_cgroup_events_index {
        MEM_CGROUP_EVENTS_PGPGIN,       /* # of pages paged in */
        MEM_CGROUP_EVENTS_PGPGOUT,      /* # of pages paged out */
-       MEM_CGROUP_EVENTS_COUNT,        /* # of pages paged in/out */
        MEM_CGROUP_EVENTS_PGFAULT,      /* # of page-faults */
        MEM_CGROUP_EVENTS_PGMAJFAULT,   /* # of major page-faults */
        MEM_CGROUP_EVENTS_NSTATS,
 };
+
+static const char * const mem_cgroup_events_names[] = {
+       "pgpgin",
+       "pgpgout",
+       "pgfault",
+       "pgmajfault",
+};
+
 /*
  * Per memcg event counter is incremented at every pagein/pageout. With THP,
  * it will be incremated by the number of pages. This counter is used for
@@ -112,13 +125,14 @@ enum mem_cgroup_events_target {
        MEM_CGROUP_TARGET_NUMAINFO,
        MEM_CGROUP_NTARGETS,
 };
-#define THRESHOLDS_EVENTS_TARGET (128)
-#define SOFTLIMIT_EVENTS_TARGET (1024)
-#define NUMAINFO_EVENTS_TARGET (1024)
+#define THRESHOLDS_EVENTS_TARGET 128
+#define SOFTLIMIT_EVENTS_TARGET 1024
+#define NUMAINFO_EVENTS_TARGET 1024
 
 struct mem_cgroup_stat_cpu {
        long count[MEM_CGROUP_STAT_NSTATS];
        unsigned long events[MEM_CGROUP_EVENTS_NSTATS];
+       unsigned long nr_page_events;
        unsigned long targets[MEM_CGROUP_NTARGETS];
 };
 
@@ -138,7 +152,6 @@ struct mem_cgroup_per_zone {
 
        struct mem_cgroup_reclaim_iter reclaim_iter[DEF_PRIORITY + 1];
 
-       struct zone_reclaim_stat reclaim_stat;
        struct rb_node          tree_node;      /* RB tree node */
        unsigned long long      usage_in_excess;/* Set to the value by which */
                                                /* the soft limit is exceeded*/
@@ -182,7 +195,7 @@ struct mem_cgroup_threshold {
 
 /* For threshold */
 struct mem_cgroup_threshold_ary {
-       /* An array index points to threshold just below usage. */
+       /* An array index points to threshold just below or equal to usage. */
        int current_threshold;
        /* Size of entries[] */
        unsigned int size;
@@ -245,8 +258,8 @@ struct mem_cgroup {
                 */
                struct rcu_head rcu_freeing;
                /*
-                * But when using vfree(), that cannot be done at
-                * interrupt time, so we must then queue the work.
+                * We also need some space for a worker in deferred freeing.
+                * By the time we call it, rcu_freeing is no longer in use.
                 */
                struct work_struct work_freeing;
        };
@@ -305,7 +318,7 @@ struct mem_cgroup {
        /*
         * percpu counter.
         */
-       struct mem_cgroup_stat_cpu *stat;
+       struct mem_cgroup_stat_cpu __percpu *stat;
        /*
         * used when a cpu is offlined or other synchronizations
         * See mem_cgroup_read_stat().
@@ -360,8 +373,8 @@ static bool move_file(void)
  * Maximum loops in mem_cgroup_hierarchical_reclaim(), used for soft
  * limit reclaim to prevent infinite loops, if they ever occur.
  */
-#define        MEM_CGROUP_MAX_RECLAIM_LOOPS            (100)
-#define        MEM_CGROUP_MAX_SOFT_LIMIT_RECLAIM_LOOPS (2)
+#define        MEM_CGROUP_MAX_RECLAIM_LOOPS            100
+#define        MEM_CGROUP_MAX_SOFT_LIMIT_RECLAIM_LOOPS 2
 
 enum charge_type {
        MEM_CGROUP_CHARGE_TYPE_CACHE = 0,
@@ -377,8 +390,8 @@ enum charge_type {
 #define _MEM                   (0)
 #define _MEMSWAP               (1)
 #define _OOM_TYPE              (2)
-#define MEMFILE_PRIVATE(x, val)        (((x) << 16) | (val))
-#define MEMFILE_TYPE(val)      (((val) >> 16) & 0xffff)
+#define MEMFILE_PRIVATE(x, val)        ((x) << 16 | (val))
+#define MEMFILE_TYPE(val)      ((val) >> 16 & 0xffff)
 #define MEMFILE_ATTR(val)      ((val) & 0xffff)
 /* Used for OOM nofiier */
 #define OOM_CONTROL            (0)
@@ -404,6 +417,7 @@ void sock_update_memcg(struct sock *sk)
 {
        if (mem_cgroup_sockets_enabled) {
                struct mem_cgroup *memcg;
+               struct cg_proto *cg_proto;
 
                BUG_ON(!sk->sk_prot->proto_cgroup);
 
@@ -423,9 +437,10 @@ void sock_update_memcg(struct sock *sk)
 
                rcu_read_lock();
                memcg = mem_cgroup_from_task(current);
-               if (!mem_cgroup_is_root(memcg)) {
+               cg_proto = sk->sk_prot->proto_cgroup(memcg);
+               if (!mem_cgroup_is_root(memcg) && memcg_proto_active(cg_proto)) {
                        mem_cgroup_get(memcg);
-                       sk->sk_cgrp = sk->sk_prot->proto_cgroup(memcg);
+                       sk->sk_cgrp = cg_proto;
                }
                rcu_read_unlock();
        }
@@ -454,6 +469,19 @@ EXPORT_SYMBOL(tcp_proto_cgroup);
 #endif /* CONFIG_INET */
 #endif /* CONFIG_CGROUP_MEM_RES_CTLR_KMEM */
 
+#if defined(CONFIG_INET) && defined(CONFIG_CGROUP_MEM_RES_CTLR_KMEM)
+static void disarm_sock_keys(struct mem_cgroup *memcg)
+{
+       if (!memcg_proto_activated(&memcg->tcp_mem.cg_proto))
+               return;
+       static_key_slow_dec(&memcg_socket_limit_enabled);
+}
+#else
+static void disarm_sock_keys(struct mem_cgroup *memcg)
+{
+}
+#endif
+
 static void drain_all_stock_async(struct mem_cgroup *memcg);
 
 static struct mem_cgroup_per_zone *
@@ -718,12 +746,21 @@ static void mem_cgroup_charge_statistics(struct mem_cgroup *memcg,
                nr_pages = -nr_pages; /* for event */
        }
 
-       __this_cpu_add(memcg->stat->events[MEM_CGROUP_EVENTS_COUNT], nr_pages);
+       __this_cpu_add(memcg->stat->nr_page_events, nr_pages);
 
        preempt_enable();
 }
 
 unsigned long
+mem_cgroup_get_lru_size(struct lruvec *lruvec, enum lru_list lru)
+{
+       struct mem_cgroup_per_zone *mz;
+
+       mz = container_of(lruvec, struct mem_cgroup_per_zone, lruvec);
+       return mz->lru_size[lru];
+}
+
+static unsigned long
 mem_cgroup_zone_nr_lru_pages(struct mem_cgroup *memcg, int nid, int zid,
                        unsigned int lru_mask)
 {
@@ -770,7 +807,7 @@ static bool mem_cgroup_event_ratelimit(struct mem_cgroup *memcg,
 {
        unsigned long val, next;
 
-       val = __this_cpu_read(memcg->stat->events[MEM_CGROUP_EVENTS_COUNT]);
+       val = __this_cpu_read(memcg->stat->nr_page_events);
        next = __this_cpu_read(memcg->stat->targets[target]);
        /* from time_after() in jiffies.h */
        if ((long)next - (long)val < 0) {
@@ -1013,7 +1050,7 @@ EXPORT_SYMBOL(mem_cgroup_count_vm_event);
 /**
  * mem_cgroup_zone_lruvec - get the lru list vector for a zone and memcg
  * @zone: zone of the wanted lruvec
- * @mem: memcg of the wanted lruvec
+ * @memcg: memcg of the wanted lruvec
  *
  * Returns the lru list vector holding pages for the given @zone and
  * @mem.  This can be the global zone lruvec, if the memory controller
@@ -1046,19 +1083,11 @@ struct lruvec *mem_cgroup_zone_lruvec(struct zone *zone,
  */
 
 /**
- * mem_cgroup_lru_add_list - account for adding an lru page and return lruvec
- * @zone: zone of the page
+ * mem_cgroup_page_lruvec - return lruvec for adding an lru page
  * @page: the page
- * @lru: current lru
- *
- * This function accounts for @page being added to @lru, and returns
- * the lruvec for the given @zone and the memcg @page is charged to.
- *
- * The callsite is then responsible for physically linking the page to
- * the returned lruvec->lists[@lru].
+ * @zone: zone of the page
  */
-struct lruvec *mem_cgroup_lru_add_list(struct zone *zone, struct page *page,
-                                      enum lru_list lru)
+struct lruvec *mem_cgroup_page_lruvec(struct page *page, struct zone *zone)
 {
        struct mem_cgroup_per_zone *mz;
        struct mem_cgroup *memcg;
@@ -1071,7 +1100,7 @@ struct lruvec *mem_cgroup_lru_add_list(struct zone *zone, struct page *page,
        memcg = pc->mem_cgroup;
 
        /*
-        * Surreptitiously switch any uncharged page to root:
+        * Surreptitiously switch any uncharged offlist page to root:
         * an uncharged page off lru does nothing to secure
         * its former mem_cgroup from sudden removal.
         *
@@ -1079,85 +1108,60 @@ struct lruvec *mem_cgroup_lru_add_list(struct zone *zone, struct page *page,
         * under page_cgroup lock: between them, they make all uses
         * of pc->mem_cgroup safe.
         */
-       if (!PageCgroupUsed(pc) && memcg != root_mem_cgroup)
+       if (!PageLRU(page) && !PageCgroupUsed(pc) && memcg != root_mem_cgroup)
                pc->mem_cgroup = memcg = root_mem_cgroup;
 
        mz = page_cgroup_zoneinfo(memcg, page);
-       /* compound_order() is stabilized through lru_lock */
-       mz->lru_size[lru] += 1 << compound_order(page);
        return &mz->lruvec;
 }
 
 /**
- * mem_cgroup_lru_del_list - account for removing an lru page
- * @page: the page
- * @lru: target lru
- *
- * This function accounts for @page being removed from @lru.
+ * mem_cgroup_update_lru_size - account for adding or removing an lru page
+ * @lruvec: mem_cgroup per zone lru vector
+ * @lru: index of lru list the page is sitting on
+ * @nr_pages: positive when adding or negative when removing
  *
- * The callsite is then responsible for physically unlinking
- * @page->lru.
+ * This function must be called when a page is added to or removed from an
+ * lru list.
  */
-void mem_cgroup_lru_del_list(struct page *page, enum lru_list lru)
+void mem_cgroup_update_lru_size(struct lruvec *lruvec, enum lru_list lru,
+                               int nr_pages)
 {
        struct mem_cgroup_per_zone *mz;
-       struct mem_cgroup *memcg;
-       struct page_cgroup *pc;
+       unsigned long *lru_size;
 
        if (mem_cgroup_disabled())
                return;
 
-       pc = lookup_page_cgroup(page);
-       memcg = pc->mem_cgroup;
-       VM_BUG_ON(!memcg);
-       mz = page_cgroup_zoneinfo(memcg, page);
-       /* huge page split is done under lru_lock. so, we have no races. */
-       VM_BUG_ON(mz->lru_size[lru] < (1 << compound_order(page)));
-       mz->lru_size[lru] -= 1 << compound_order(page);
-}
-
-void mem_cgroup_lru_del(struct page *page)
-{
-       mem_cgroup_lru_del_list(page, page_lru(page));
-}
-
-/**
- * mem_cgroup_lru_move_lists - account for moving a page between lrus
- * @zone: zone of the page
- * @page: the page
- * @from: current lru
- * @to: target lru
- *
- * This function accounts for @page being moved between the lrus @from
- * and @to, and returns the lruvec for the given @zone and the memcg
- * @page is charged to.
- *
- * The callsite is then responsible for physically relinking
- * @page->lru to the returned lruvec->lists[@to].
- */
-struct lruvec *mem_cgroup_lru_move_lists(struct zone *zone,
-                                        struct page *page,
-                                        enum lru_list from,
-                                        enum lru_list to)
-{
-       /* XXX: Optimize this, especially for @from == @to */
-       mem_cgroup_lru_del_list(page, from);
-       return mem_cgroup_lru_add_list(zone, page, to);
+       mz = container_of(lruvec, struct mem_cgroup_per_zone, lruvec);
+       lru_size = mz->lru_size + lru;
+       *lru_size += nr_pages;
+       VM_BUG_ON((long)(*lru_size) < 0);
 }
 
 /*
  * Checks whether given mem is same or in the root_mem_cgroup's
  * hierarchy subtree
  */
+bool __mem_cgroup_same_or_subtree(const struct mem_cgroup *root_memcg,
+                                 struct mem_cgroup *memcg)
+{
+       if (root_memcg == memcg)
+               return true;
+       if (!root_memcg->use_hierarchy)
+               return false;
+       return css_is_ancestor(&memcg->css, &root_memcg->css);
+}
+
 static bool mem_cgroup_same_or_subtree(const struct mem_cgroup *root_memcg,
-               struct mem_cgroup *memcg)
+                                      struct mem_cgroup *memcg)
 {
-       if (root_memcg != memcg) {
-               return (root_memcg->use_hierarchy &&
-                       css_is_ancestor(&memcg->css, &root_memcg->css));
-       }
+       bool ret;
 
-       return true;
+       rcu_read_lock();
+       ret = __mem_cgroup_same_or_subtree(root_memcg, memcg);
+       rcu_read_unlock();
+       return ret;
 }
 
 int task_in_mem_cgroup(struct task_struct *task, const struct mem_cgroup *memcg)
@@ -1195,19 +1199,15 @@ int task_in_mem_cgroup(struct task_struct *task, const struct mem_cgroup *memcg)
        return ret;
 }
 
-int mem_cgroup_inactive_anon_is_low(struct mem_cgroup *memcg, struct zone *zone)
+int mem_cgroup_inactive_anon_is_low(struct lruvec *lruvec)
 {
        unsigned long inactive_ratio;
-       int nid = zone_to_nid(zone);
-       int zid = zone_idx(zone);
        unsigned long inactive;
        unsigned long active;
        unsigned long gb;
 
-       inactive = mem_cgroup_zone_nr_lru_pages(memcg, nid, zid,
-                                               BIT(LRU_INACTIVE_ANON));
-       active = mem_cgroup_zone_nr_lru_pages(memcg, nid, zid,
-                                             BIT(LRU_ACTIVE_ANON));
+       inactive = mem_cgroup_get_lru_size(lruvec, LRU_INACTIVE_ANON);
+       active = mem_cgroup_get_lru_size(lruvec, LRU_ACTIVE_ANON);
 
        gb = (inactive + active) >> (30 - PAGE_SHIFT);
        if (gb)
@@ -1218,49 +1218,17 @@ int mem_cgroup_inactive_anon_is_low(struct mem_cgroup *memcg, struct zone *zone)
        return inactive * inactive_ratio < active;
 }
 
-int mem_cgroup_inactive_file_is_low(struct mem_cgroup *memcg, struct zone *zone)
+int mem_cgroup_inactive_file_is_low(struct lruvec *lruvec)
 {
        unsigned long active;
        unsigned long inactive;
-       int zid = zone_idx(zone);
-       int nid = zone_to_nid(zone);
 
-       inactive = mem_cgroup_zone_nr_lru_pages(memcg, nid, zid,
-                                               BIT(LRU_INACTIVE_FILE));
-       active = mem_cgroup_zone_nr_lru_pages(memcg, nid, zid,
-                                             BIT(LRU_ACTIVE_FILE));
+       inactive = mem_cgroup_get_lru_size(lruvec, LRU_INACTIVE_FILE);
+       active = mem_cgroup_get_lru_size(lruvec, LRU_ACTIVE_FILE);
 
        return (active > inactive);
 }
 
-struct zone_reclaim_stat *mem_cgroup_get_reclaim_stat(struct mem_cgroup *memcg,
-                                                     struct zone *zone)
-{
-       int nid = zone_to_nid(zone);
-       int zid = zone_idx(zone);
-       struct mem_cgroup_per_zone *mz = mem_cgroup_zoneinfo(memcg, nid, zid);
-
-       return &mz->reclaim_stat;
-}
-
-struct zone_reclaim_stat *
-mem_cgroup_get_reclaim_stat_from_page(struct page *page)
-{
-       struct page_cgroup *pc;
-       struct mem_cgroup_per_zone *mz;
-
-       if (mem_cgroup_disabled())
-               return NULL;
-
-       pc = lookup_page_cgroup(page);
-       if (!PageCgroupUsed(pc))
-               return NULL;
-       /* Ensure pc->mem_cgroup is visible after reading PCG_USED. */
-       smp_rmb();
-       mz = page_cgroup_zoneinfo(pc->mem_cgroup, page);
-       return &mz->reclaim_stat;
-}
-
 #define mem_cgroup_from_res_counter(counter, member)   \
        container_of(counter, struct mem_cgroup, member)
 
@@ -1634,7 +1602,7 @@ int mem_cgroup_select_victim_node(struct mem_cgroup *memcg)
  * unused nodes. But scan_nodes is lazily updated and may not cotain
  * enough new information. We need to do double check.
  */
-bool mem_cgroup_reclaimable(struct mem_cgroup *memcg, bool noswap)
+static bool mem_cgroup_reclaimable(struct mem_cgroup *memcg, bool noswap)
 {
        int nid;
 
@@ -1669,7 +1637,7 @@ int mem_cgroup_select_victim_node(struct mem_cgroup *memcg)
        return 0;
 }
 
-bool mem_cgroup_reclaimable(struct mem_cgroup *memcg, bool noswap)
+static bool mem_cgroup_reclaimable(struct mem_cgroup *memcg, bool noswap)
 {
        return test_mem_cgroup_node_reclaimable(memcg, 0, noswap);
 }
@@ -1843,7 +1811,8 @@ static void memcg_oom_recover(struct mem_cgroup *memcg)
 /*
  * try to call OOM killer. returns false if we should exit memory-reclaim loop.
  */
-bool mem_cgroup_handle_oom(struct mem_cgroup *memcg, gfp_t mask, int order)
+static bool mem_cgroup_handle_oom(struct mem_cgroup *memcg, gfp_t mask,
+                                 int order)
 {
        struct oom_wait_info owait;
        bool locked, need_to_kill;
@@ -1992,7 +1961,7 @@ struct memcg_stock_pcp {
        unsigned int nr_pages;
        struct work_struct work;
        unsigned long flags;
-#define FLUSHING_CACHED_CHARGE (0)
+#define FLUSHING_CACHED_CHARGE 0
 };
 static DEFINE_PER_CPU(struct memcg_stock_pcp, memcg_stock);
 static DEFINE_MUTEX(percpu_charge_mutex);
@@ -2139,7 +2108,7 @@ static void mem_cgroup_drain_pcp_counter(struct mem_cgroup *memcg, int cpu)
        int i;
 
        spin_lock(&memcg->pcp_counter_lock);
-       for (i = 0; i < MEM_CGROUP_STAT_DATA; i++) {
+       for (i = 0; i < MEM_CGROUP_STAT_NSTATS; i++) {
                long x = per_cpu(memcg->stat->count[i], cpu);
 
                per_cpu(memcg->stat->count[i], cpu) = 0;
@@ -2426,6 +2395,24 @@ static void __mem_cgroup_cancel_charge(struct mem_cgroup *memcg,
        }
 }
 
+/*
+ * Cancel chrages in this cgroup....doesn't propagate to parent cgroup.
+ * This is useful when moving usage to parent cgroup.
+ */
+static void __mem_cgroup_cancel_local_charge(struct mem_cgroup *memcg,
+                                       unsigned int nr_pages)
+{
+       unsigned long bytes = nr_pages * PAGE_SIZE;
+
+       if (mem_cgroup_is_root(memcg))
+               return;
+
+       res_counter_uncharge_until(&memcg->res, memcg->res.parent, bytes);
+       if (do_swap_account)
+               res_counter_uncharge_until(&memcg->memsw,
+                                               memcg->memsw.parent, bytes);
+}
+
 /*
  * A helper function to get mem_cgroup from ID. must be called under
  * rcu_read_lock(). The caller must check css_is_removed() or some if
@@ -2481,6 +2468,7 @@ static void __mem_cgroup_commit_charge(struct mem_cgroup *memcg,
 {
        struct page_cgroup *pc = lookup_page_cgroup(page);
        struct zone *uninitialized_var(zone);
+       struct lruvec *lruvec;
        bool was_on_lru = false;
        bool anon;
 
@@ -2503,8 +2491,9 @@ static void __mem_cgroup_commit_charge(struct mem_cgroup *memcg,
                zone = page_zone(page);
                spin_lock_irq(&zone->lru_lock);
                if (PageLRU(page)) {
+                       lruvec = mem_cgroup_zone_lruvec(zone, pc->mem_cgroup);
                        ClearPageLRU(page);
-                       del_page_from_lru_list(zone, page, page_lru(page));
+                       del_page_from_lru_list(page, lruvec, page_lru(page));
                        was_on_lru = true;
                }
        }
@@ -2522,9 +2511,10 @@ static void __mem_cgroup_commit_charge(struct mem_cgroup *memcg,
 
        if (lrucare) {
                if (was_on_lru) {
+                       lruvec = mem_cgroup_zone_lruvec(zone, pc->mem_cgroup);
                        VM_BUG_ON(PageLRU(page));
                        SetPageLRU(page);
-                       add_page_to_lru_list(zone, page, page_lru(page));
+                       add_page_to_lru_list(page, lruvec, page_lru(page));
                }
                spin_unlock_irq(&zone->lru_lock);
        }
@@ -2547,7 +2537,7 @@ static void __mem_cgroup_commit_charge(struct mem_cgroup *memcg,
 
 #ifdef CONFIG_TRANSPARENT_HUGEPAGE
 
-#define PCGF_NOCOPY_AT_SPLIT ((1 << PCG_LOCK) | (1 << PCG_MIGRATION))
+#define PCGF_NOCOPY_AT_SPLIT (1 << PCG_LOCK | 1 << PCG_MIGRATION)
 /*
  * Because tail pages are not marked as "used", set it. We're under
  * zone->lru_lock, 'splitting on pmd' and compound_lock.
@@ -2578,23 +2568,19 @@ void mem_cgroup_split_huge_fixup(struct page *head)
  * @pc:        page_cgroup of the page.
  * @from: mem_cgroup which the page is moved from.
  * @to:        mem_cgroup which the page is moved to. @from != @to.
- * @uncharge: whether we should call uncharge and css_put against @from.
  *
  * The caller must confirm following.
  * - page is not on LRU (isolate_page() is useful.)
  * - compound_lock is held when nr_pages > 1
  *
- * This function doesn't do "charge" nor css_get to new cgroup. It should be
- * done by a caller(__mem_cgroup_try_charge would be useful). If @uncharge is
- * true, this function does "uncharge" from old cgroup, but it doesn't if
- * @uncharge is false, so a caller should do "uncharge".
+ * This function doesn't do "charge" to new cgroup and doesn't do "uncharge"
+ * from old cgroup.
  */
 static int mem_cgroup_move_account(struct page *page,
                                   unsigned int nr_pages,
                                   struct page_cgroup *pc,
                                   struct mem_cgroup *from,
-                                  struct mem_cgroup *to,
-                                  bool uncharge)
+                                  struct mem_cgroup *to)
 {
        unsigned long flags;
        int ret;
@@ -2628,9 +2614,6 @@ static int mem_cgroup_move_account(struct page *page,
                preempt_enable();
        }
        mem_cgroup_charge_statistics(from, anon, -nr_pages);
-       if (uncharge)
-               /* This is not "cancel", but cancel_charge does all we need. */
-               __mem_cgroup_cancel_charge(from, nr_pages);
 
        /* caller should have done css_get */
        pc->mem_cgroup = to;
@@ -2664,15 +2647,13 @@ static int mem_cgroup_move_parent(struct page *page,
                                  struct mem_cgroup *child,
                                  gfp_t gfp_mask)
 {
-       struct cgroup *cg = child->css.cgroup;
-       struct cgroup *pcg = cg->parent;
        struct mem_cgroup *parent;
        unsigned int nr_pages;
        unsigned long uninitialized_var(flags);
        int ret;
 
        /* Is ROOT ? */
-       if (!pcg)
+       if (mem_cgroup_is_root(child))
                return -EINVAL;
 
        ret = -EBUSY;
@@ -2683,21 +2664,23 @@ static int mem_cgroup_move_parent(struct page *page,
 
        nr_pages = hpage_nr_pages(page);
 
-       parent = mem_cgroup_from_cont(pcg);
-       ret = __mem_cgroup_try_charge(NULL, gfp_mask, nr_pages, &parent, false);
-       if (ret)
-               goto put_back;
+       parent = parent_mem_cgroup(child);
+       /*
+        * If no parent, move charges to root cgroup.
+        */
+       if (!parent)
+               parent = root_mem_cgroup;
 
        if (nr_pages > 1)
                flags = compound_lock_irqsave(page);
 
-       ret = mem_cgroup_move_account(page, nr_pages, pc, child, parent, true);
-       if (ret)
-               __mem_cgroup_cancel_charge(parent, nr_pages);
+       ret = mem_cgroup_move_account(page, nr_pages,
+                               pc, child, parent);
+       if (!ret)
+               __mem_cgroup_cancel_local_charge(child, nr_pages);
 
        if (nr_pages > 1)
                compound_unlock_irqrestore(page, flags);
-put_back:
        putback_lru_page(page);
 put:
        put_page(page);
@@ -2845,24 +2828,7 @@ __mem_cgroup_commit_charge_swapin(struct page *page, struct mem_cgroup *memcg,
         */
        if (do_swap_account && PageSwapCache(page)) {
                swp_entry_t ent = {.val = page_private(page)};
-               struct mem_cgroup *swap_memcg;
-               unsigned short id;
-
-               id = swap_cgroup_record(ent, 0);
-               rcu_read_lock();
-               swap_memcg = mem_cgroup_lookup(id);
-               if (swap_memcg) {
-                       /*
-                        * This recorded memcg can be obsolete one. So, avoid
-                        * calling css_tryget
-                        */
-                       if (!mem_cgroup_is_root(swap_memcg))
-                               res_counter_uncharge(&swap_memcg->memsw,
-                                                    PAGE_SIZE);
-                       mem_cgroup_swap_statistics(swap_memcg, false);
-                       mem_cgroup_put(swap_memcg);
-               }
-               rcu_read_unlock();
+               mem_cgroup_uncharge_swap(ent);
        }
        /*
         * At swapin, we may charge account against cgroup which has no tasks.
@@ -3155,7 +3121,6 @@ void mem_cgroup_uncharge_swap(swp_entry_t ent)
  * @entry: swap entry to be moved
  * @from:  mem_cgroup which the entry is moved from
  * @to:  mem_cgroup which the entry is moved to
- * @need_fixup: whether we should fixup res_counters and refcounts.
  *
  * It succeeds only when the swap_cgroup's record for this entry is the same
  * as the mem_cgroup's id of @from.
@@ -3166,7 +3131,7 @@ void mem_cgroup_uncharge_swap(swp_entry_t ent)
  * both res and memsw, and called css_get().
  */
 static int mem_cgroup_move_swap_account(swp_entry_t entry,
-               struct mem_cgroup *from, struct mem_cgroup *to, bool need_fixup)
+                               struct mem_cgroup *from, struct mem_cgroup *to)
 {
        unsigned short old_id, new_id;
 
@@ -3185,24 +3150,13 @@ static int mem_cgroup_move_swap_account(swp_entry_t entry,
                 * swap-in, the refcount of @to might be decreased to 0.
                 */
                mem_cgroup_get(to);
-               if (need_fixup) {
-                       if (!mem_cgroup_is_root(from))
-                               res_counter_uncharge(&from->memsw, PAGE_SIZE);
-                       mem_cgroup_put(from);
-                       /*
-                        * we charged both to->res and to->memsw, so we should
-                        * uncharge to->res.
-                        */
-                       if (!mem_cgroup_is_root(to))
-                               res_counter_uncharge(&to->res, PAGE_SIZE);
-               }
                return 0;
        }
        return -EINVAL;
 }
 #else
 static inline int mem_cgroup_move_swap_account(swp_entry_t entry,
-               struct mem_cgroup *from, struct mem_cgroup *to, bool need_fixup)
+                               struct mem_cgroup *from, struct mem_cgroup *to)
 {
        return -EINVAL;
 }
@@ -3363,7 +3317,7 @@ void mem_cgroup_end_migration(struct mem_cgroup *memcg,
 void mem_cgroup_replace_page_cache(struct page *oldpage,
                                  struct page *newpage)
 {
-       struct mem_cgroup *memcg;
+       struct mem_cgroup *memcg = NULL;
        struct page_cgroup *pc;
        enum charge_type type = MEM_CGROUP_CHARGE_TYPE_CACHE;
 
@@ -3373,11 +3327,20 @@ void mem_cgroup_replace_page_cache(struct page *oldpage,
        pc = lookup_page_cgroup(oldpage);
        /* fix accounting on old pages */
        lock_page_cgroup(pc);
-       memcg = pc->mem_cgroup;
-       mem_cgroup_charge_statistics(memcg, false, -1);
-       ClearPageCgroupUsed(pc);
+       if (PageCgroupUsed(pc)) {
+               memcg = pc->mem_cgroup;
+               mem_cgroup_charge_statistics(memcg, false, -1);
+               ClearPageCgroupUsed(pc);
+       }
        unlock_page_cgroup(pc);
 
+       /*
+        * When called from shmem_replace_page(), in some cases the
+        * oldpage has already been charged, and in some cases not.
+        */
+       if (!memcg)
+               return;
+
        if (PageSwapBacked(oldpage))
                type = MEM_CGROUP_CHARGE_TYPE_SHMEM;
 
@@ -3793,7 +3756,7 @@ try_to_free:
        goto move_account;
 }
 
-int mem_cgroup_force_empty_write(struct cgroup *cont, unsigned int event)
+static int mem_cgroup_force_empty_write(struct cgroup *cont, unsigned int event)
 {
        return mem_cgroup_force_empty(mem_cgroup_from_cont(cont), true);
 }
@@ -4051,103 +4014,13 @@ static int mem_cgroup_move_charge_write(struct cgroup *cgrp,
 }
 #endif
 
-
-/* For read statistics */
-enum {
-       MCS_CACHE,
-       MCS_RSS,
-       MCS_FILE_MAPPED,
-       MCS_PGPGIN,
-       MCS_PGPGOUT,
-       MCS_SWAP,
-       MCS_PGFAULT,
-       MCS_PGMAJFAULT,
-       MCS_INACTIVE_ANON,
-       MCS_ACTIVE_ANON,
-       MCS_INACTIVE_FILE,
-       MCS_ACTIVE_FILE,
-       MCS_UNEVICTABLE,
-       NR_MCS_STAT,
-};
-
-struct mcs_total_stat {
-       s64 stat[NR_MCS_STAT];
-};
-
-struct {
-       char *local_name;
-       char *total_name;
-} memcg_stat_strings[NR_MCS_STAT] = {
-       {"cache", "total_cache"},
-       {"rss", "total_rss"},
-       {"mapped_file", "total_mapped_file"},
-       {"pgpgin", "total_pgpgin"},
-       {"pgpgout", "total_pgpgout"},
-       {"swap", "total_swap"},
-       {"pgfault", "total_pgfault"},
-       {"pgmajfault", "total_pgmajfault"},
-       {"inactive_anon", "total_inactive_anon"},
-       {"active_anon", "total_active_anon"},
-       {"inactive_file", "total_inactive_file"},
-       {"active_file", "total_active_file"},
-       {"unevictable", "total_unevictable"}
-};
-
-
-static void
-mem_cgroup_get_local_stat(struct mem_cgroup *memcg, struct mcs_total_stat *s)
-{
-       s64 val;
-
-       /* per cpu stat */
-       val = mem_cgroup_read_stat(memcg, MEM_CGROUP_STAT_CACHE);
-       s->stat[MCS_CACHE] += val * PAGE_SIZE;
-       val = mem_cgroup_read_stat(memcg, MEM_CGROUP_STAT_RSS);
-       s->stat[MCS_RSS] += val * PAGE_SIZE;
-       val = mem_cgroup_read_stat(memcg, MEM_CGROUP_STAT_FILE_MAPPED);
-       s->stat[MCS_FILE_MAPPED] += val * PAGE_SIZE;
-       val = mem_cgroup_read_events(memcg, MEM_CGROUP_EVENTS_PGPGIN);
-       s->stat[MCS_PGPGIN] += val;
-       val = mem_cgroup_read_events(memcg, MEM_CGROUP_EVENTS_PGPGOUT);
-       s->stat[MCS_PGPGOUT] += val;
-       if (do_swap_account) {
-               val = mem_cgroup_read_stat(memcg, MEM_CGROUP_STAT_SWAPOUT);
-               s->stat[MCS_SWAP] += val * PAGE_SIZE;
-       }
-       val = mem_cgroup_read_events(memcg, MEM_CGROUP_EVENTS_PGFAULT);
-       s->stat[MCS_PGFAULT] += val;
-       val = mem_cgroup_read_events(memcg, MEM_CGROUP_EVENTS_PGMAJFAULT);
-       s->stat[MCS_PGMAJFAULT] += val;
-
-       /* per zone stat */
-       val = mem_cgroup_nr_lru_pages(memcg, BIT(LRU_INACTIVE_ANON));
-       s->stat[MCS_INACTIVE_ANON] += val * PAGE_SIZE;
-       val = mem_cgroup_nr_lru_pages(memcg, BIT(LRU_ACTIVE_ANON));
-       s->stat[MCS_ACTIVE_ANON] += val * PAGE_SIZE;
-       val = mem_cgroup_nr_lru_pages(memcg, BIT(LRU_INACTIVE_FILE));
-       s->stat[MCS_INACTIVE_FILE] += val * PAGE_SIZE;
-       val = mem_cgroup_nr_lru_pages(memcg, BIT(LRU_ACTIVE_FILE));
-       s->stat[MCS_ACTIVE_FILE] += val * PAGE_SIZE;
-       val = mem_cgroup_nr_lru_pages(memcg, BIT(LRU_UNEVICTABLE));
-       s->stat[MCS_UNEVICTABLE] += val * PAGE_SIZE;
-}
-
-static void
-mem_cgroup_get_total_stat(struct mem_cgroup *memcg, struct mcs_total_stat *s)
-{
-       struct mem_cgroup *iter;
-
-       for_each_mem_cgroup_tree(iter, memcg)
-               mem_cgroup_get_local_stat(iter, s);
-}
-
 #ifdef CONFIG_NUMA
-static int mem_control_numa_stat_show(struct seq_file *m, void *arg)
+static int mem_control_numa_stat_show(struct cgroup *cont, struct cftype *cft,
+                                     struct seq_file *m)
 {
        int nid;
        unsigned long total_nr, file_nr, anon_nr, unevictable_nr;
        unsigned long node_nr;
-       struct cgroup *cont = m->private;
        struct mem_cgroup *memcg = mem_cgroup_from_cont(cont);
 
        total_nr = mem_cgroup_nr_lru_pages(memcg, LRU_ALL);
@@ -4188,64 +4061,100 @@ static int mem_control_numa_stat_show(struct seq_file *m, void *arg)
 }
 #endif /* CONFIG_NUMA */
 
+static const char * const mem_cgroup_lru_names[] = {
+       "inactive_anon",
+       "active_anon",
+       "inactive_file",
+       "active_file",
+       "unevictable",
+};
+
+static inline void mem_cgroup_lru_names_not_uptodate(void)
+{
+       BUILD_BUG_ON(ARRAY_SIZE(mem_cgroup_lru_names) != NR_LRU_LISTS);
+}
+
 static int mem_control_stat_show(struct cgroup *cont, struct cftype *cft,
-                                struct cgroup_map_cb *cb)
+                                struct seq_file *m)
 {
        struct mem_cgroup *memcg = mem_cgroup_from_cont(cont);
-       struct mcs_total_stat mystat;
-       int i;
-
-       memset(&mystat, 0, sizeof(mystat));
-       mem_cgroup_get_local_stat(memcg, &mystat);
+       struct mem_cgroup *mi;
+       unsigned int i;
 
-
-       for (i = 0; i < NR_MCS_STAT; i++) {
-               if (i == MCS_SWAP && !do_swap_account)
+       for (i = 0; i < MEM_CGROUP_STAT_NSTATS; i++) {
+               if (i == MEM_CGROUP_STAT_SWAPOUT && !do_swap_account)
                        continue;
-               cb->fill(cb, memcg_stat_strings[i].local_name, mystat.stat[i]);
+               seq_printf(m, "%s %ld\n", mem_cgroup_stat_names[i],
+                          mem_cgroup_read_stat(memcg, i) * PAGE_SIZE);
        }
 
+       for (i = 0; i < MEM_CGROUP_EVENTS_NSTATS; i++)
+               seq_printf(m, "%s %lu\n", mem_cgroup_events_names[i],
+                          mem_cgroup_read_events(memcg, i));
+
+       for (i = 0; i < NR_LRU_LISTS; i++)
+               seq_printf(m, "%s %lu\n", mem_cgroup_lru_names[i],
+                          mem_cgroup_nr_lru_pages(memcg, BIT(i)) * PAGE_SIZE);
+
        /* Hierarchical information */
        {
                unsigned long long limit, memsw_limit;
                memcg_get_hierarchical_limit(memcg, &limit, &memsw_limit);
-               cb->fill(cb, "hierarchical_memory_limit", limit);
+               seq_printf(m, "hierarchical_memory_limit %llu\n", limit);
                if (do_swap_account)
-                       cb->fill(cb, "hierarchical_memsw_limit", memsw_limit);
+                       seq_printf(m, "hierarchical_memsw_limit %llu\n",
+                                  memsw_limit);
        }
 
-       memset(&mystat, 0, sizeof(mystat));
-       mem_cgroup_get_total_stat(memcg, &mystat);
-       for (i = 0; i < NR_MCS_STAT; i++) {
-               if (i == MCS_SWAP && !do_swap_account)
+       for (i = 0; i < MEM_CGROUP_STAT_NSTATS; i++) {
+               long long val = 0;
+
+               if (i == MEM_CGROUP_STAT_SWAPOUT && !do_swap_account)
                        continue;
-               cb->fill(cb, memcg_stat_strings[i].total_name, mystat.stat[i]);
+               for_each_mem_cgroup_tree(mi, memcg)
+                       val += mem_cgroup_read_stat(mi, i) * PAGE_SIZE;
+               seq_printf(m, "total_%s %lld\n", mem_cgroup_stat_names[i], val);
+       }
+
+       for (i = 0; i < MEM_CGROUP_EVENTS_NSTATS; i++) {
+               unsigned long long val = 0;
+
+               for_each_mem_cgroup_tree(mi, memcg)
+                       val += mem_cgroup_read_events(mi, i);
+               seq_printf(m, "total_%s %llu\n",
+                          mem_cgroup_events_names[i], val);
+       }
+
+       for (i = 0; i < NR_LRU_LISTS; i++) {
+               unsigned long long val = 0;
+
+               for_each_mem_cgroup_tree(mi, memcg)
+                       val += mem_cgroup_nr_lru_pages(mi, BIT(i)) * PAGE_SIZE;
+               seq_printf(m, "total_%s %llu\n", mem_cgroup_lru_names[i], val);
        }
 
 #ifdef CONFIG_DEBUG_VM
        {
                int nid, zid;
                struct mem_cgroup_per_zone *mz;
+               struct zone_reclaim_stat *rstat;
                unsigned long recent_rotated[2] = {0, 0};
                unsigned long recent_scanned[2] = {0, 0};
 
                for_each_online_node(nid)
                        for (zid = 0; zid < MAX_NR_ZONES; zid++) {
                                mz = mem_cgroup_zoneinfo(memcg, nid, zid);
+                               rstat = &mz->lruvec.reclaim_stat;
 
-                               recent_rotated[0] +=
-                                       mz->reclaim_stat.recent_rotated[0];
-                               recent_rotated[1] +=
-                                       mz->reclaim_stat.recent_rotated[1];
-                               recent_scanned[0] +=
-                                       mz->reclaim_stat.recent_scanned[0];
-                               recent_scanned[1] +=
-                                       mz->reclaim_stat.recent_scanned[1];
+                               recent_rotated[0] += rstat->recent_rotated[0];
+                               recent_rotated[1] += rstat->recent_rotated[1];
+                               recent_scanned[0] += rstat->recent_scanned[0];
+                               recent_scanned[1] += rstat->recent_scanned[1];
                        }
-               cb->fill(cb, "recent_rotated_anon", recent_rotated[0]);
-               cb->fill(cb, "recent_rotated_file", recent_rotated[1]);
-               cb->fill(cb, "recent_scanned_anon", recent_scanned[0]);
-               cb->fill(cb, "recent_scanned_file", recent_scanned[1]);
+               seq_printf(m, "recent_rotated_anon %lu\n", recent_rotated[0]);
+               seq_printf(m, "recent_rotated_file %lu\n", recent_rotated[1]);
+               seq_printf(m, "recent_scanned_anon %lu\n", recent_scanned[0]);
+               seq_printf(m, "recent_scanned_file %lu\n", recent_scanned[1]);
        }
 #endif
 
@@ -4307,7 +4216,7 @@ static void __mem_cgroup_threshold(struct mem_cgroup *memcg, bool swap)
        usage = mem_cgroup_usage(memcg, swap);
 
        /*
-        * current_threshold points to threshold just below usage.
+        * current_threshold points to threshold just below or equal to usage.
         * If it's not true, a threshold was crossed after last
         * call of __mem_cgroup_threshold().
         */
@@ -4433,14 +4342,15 @@ static int mem_cgroup_usage_register_event(struct cgroup *cgrp,
        /* Find current threshold */
        new->current_threshold = -1;
        for (i = 0; i < size; i++) {
-               if (new->entries[i].threshold < usage) {
+               if (new->entries[i].threshold <= usage) {
                        /*
                         * new->current_threshold will not be used until
                         * rcu_assign_pointer(), so it's safe to increment
                         * it here.
                         */
                        ++new->current_threshold;
-               }
+               } else
+                       break;
        }
 
        /* Free old spare buffer and save old primary buffer as spare */
@@ -4509,7 +4419,7 @@ static void mem_cgroup_usage_unregister_event(struct cgroup *cgrp,
                        continue;
 
                new->entries[j] = thresholds->primary->entries[i];
-               if (new->entries[j].threshold < usage) {
+               if (new->entries[j].threshold <= usage) {
                        /*
                         * new->current_threshold will not be used
                         * until rcu_assign_pointer(), so it's safe to increment
@@ -4623,22 +4533,6 @@ static int mem_cgroup_oom_control_write(struct cgroup *cgrp,
        return 0;
 }
 
-#ifdef CONFIG_NUMA
-static const struct file_operations mem_control_numa_stat_file_operations = {
-       .read = seq_read,
-       .llseek = seq_lseek,
-       .release = single_release,
-};
-
-static int mem_control_numa_stat_open(struct inode *unused, struct file *file)
-{
-       struct cgroup *cont = file->f_dentry->d_parent->d_fsdata;
-
-       file->f_op = &mem_control_numa_stat_file_operations;
-       return single_open(file, mem_control_numa_stat_show, cont);
-}
-#endif /* CONFIG_NUMA */
-
 #ifdef CONFIG_CGROUP_MEM_RES_CTLR_KMEM
 static int memcg_init_kmem(struct mem_cgroup *memcg, struct cgroup_subsys *ss)
 {
@@ -4694,7 +4588,7 @@ static struct cftype mem_cgroup_files[] = {
        },
        {
                .name = "stat",
-               .read_map = mem_control_stat_show,
+               .read_seq_string = mem_control_stat_show,
        },
        {
                .name = "force_empty",
@@ -4726,8 +4620,7 @@ static struct cftype mem_cgroup_files[] = {
 #ifdef CONFIG_NUMA
        {
                .name = "numa_stat",
-               .open = mem_control_numa_stat_open,
-               .mode = S_IRUGO,
+               .read_seq_string = mem_control_numa_stat_show,
        },
 #endif
 #ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP
@@ -4764,7 +4657,6 @@ static int alloc_mem_cgroup_per_zone_info(struct mem_cgroup *memcg, int node)
 {
        struct mem_cgroup_per_node *pn;
        struct mem_cgroup_per_zone *mz;
-       enum lru_list lru;
        int zone, tmp = node;
        /*
         * This routine is called against possible nodes.
@@ -4782,8 +4674,7 @@ static int alloc_mem_cgroup_per_zone_info(struct mem_cgroup *memcg, int node)
 
        for (zone = 0; zone < MAX_NR_ZONES; zone++) {
                mz = &pn->zoneinfo[zone];
-               for_each_lru(lru)
-                       INIT_LIST_HEAD(&mz->lruvec.lists[lru]);
+               lruvec_init(&mz->lruvec, &NODE_DATA(node)->node_zones[zone]);
                mz->usage_in_excess = 0;
                mz->on_tree = false;
                mz->memcg = memcg;
@@ -4826,23 +4717,40 @@ out_free:
 }
 
 /*
- * Helpers for freeing a vzalloc()ed mem_cgroup by RCU,
+ * Helpers for freeing a kmalloc()ed/vzalloc()ed mem_cgroup by RCU,
  * but in process context.  The work_freeing structure is overlaid
  * on the rcu_freeing structure, which itself is overlaid on memsw.
  */
-static void vfree_work(struct work_struct *work)
+static void free_work(struct work_struct *work)
 {
        struct mem_cgroup *memcg;
+       int size = sizeof(struct mem_cgroup);
 
        memcg = container_of(work, struct mem_cgroup, work_freeing);
-       vfree(memcg);
+       /*
+        * We need to make sure that (at least for now), the jump label
+        * destruction code runs outside of the cgroup lock. This is because
+        * get_online_cpus(), which is called from the static_branch update,
+        * can't be called inside the cgroup_lock. cpusets are the ones
+        * enforcing this dependency, so if they ever change, we might as well.
+        *
+        * schedule_work() will guarantee this happens. Be careful if you need
+        * to move this code around, and make sure it is outside
+        * the cgroup_lock.
+        */
+       disarm_sock_keys(memcg);
+       if (size < PAGE_SIZE)
+               kfree(memcg);
+       else
+               vfree(memcg);
 }
-static void vfree_rcu(struct rcu_head *rcu_head)
+
+static void free_rcu(struct rcu_head *rcu_head)
 {
        struct mem_cgroup *memcg;
 
        memcg = container_of(rcu_head, struct mem_cgroup, rcu_freeing);
-       INIT_WORK(&memcg->work_freeing, vfree_work);
+       INIT_WORK(&memcg->work_freeing, free_work);
        schedule_work(&memcg->work_freeing);
 }
 
@@ -4868,10 +4776,7 @@ static void __mem_cgroup_free(struct mem_cgroup *memcg)
                free_mem_cgroup_per_zone_info(memcg, node);
 
        free_percpu(memcg->stat);
-       if (sizeof(struct mem_cgroup) < PAGE_SIZE)
-               kfree_rcu(memcg, rcu_freeing);
-       else
-               call_rcu(&memcg->rcu_freeing, vfree_rcu);
+       call_rcu(&memcg->rcu_freeing, free_rcu);
 }
 
 static void mem_cgroup_get(struct mem_cgroup *memcg)
@@ -5135,7 +5040,7 @@ static struct page *mc_handle_present_pte(struct vm_area_struct *vma,
                return NULL;
        if (PageAnon(page)) {
                /* we don't move shared anon */
-               if (!move_anon() || page_mapcount(page) > 2)
+               if (!move_anon())
                        return NULL;
        } else if (!move_file())
                /* we ignore mapcount for file pages */
@@ -5146,32 +5051,37 @@ static struct page *mc_handle_present_pte(struct vm_area_struct *vma,
        return page;
 }
 
+#ifdef CONFIG_SWAP
 static struct page *mc_handle_swap_pte(struct vm_area_struct *vma,
                        unsigned long addr, pte_t ptent, swp_entry_t *entry)
 {
-       int usage_count;
        struct page *page = NULL;
        swp_entry_t ent = pte_to_swp_entry(ptent);
 
        if (!move_anon() || non_swap_entry(ent))
                return NULL;
-       usage_count = mem_cgroup_count_swap_user(ent, &page);
-       if (usage_count > 1) { /* we don't move shared anon */
-               if (page)
-                       put_page(page);
-               return NULL;
-       }
+       /*
+        * Because lookup_swap_cache() updates some statistics counter,
+        * we call find_get_page() with swapper_space directly.
+        */
+       page = find_get_page(&swapper_space, ent.val);
        if (do_swap_account)
                entry->val = ent.val;
 
        return page;
 }
+#else
+static struct page *mc_handle_swap_pte(struct vm_area_struct *vma,
+                       unsigned long addr, pte_t ptent, swp_entry_t *entry)
+{
+       return NULL;
+}
+#endif
 
 static struct page *mc_handle_file_pte(struct vm_area_struct *vma,
                        unsigned long addr, pte_t ptent, swp_entry_t *entry)
 {
        struct page *page = NULL;
-       struct inode *inode;
        struct address_space *mapping;
        pgoff_t pgoff;
 
@@ -5180,7 +5090,6 @@ static struct page *mc_handle_file_pte(struct vm_area_struct *vma,
        if (!move_file())
                return NULL;
 
-       inode = vma->vm_file->f_path.dentry->d_inode;
        mapping = vma->vm_file->f_mapping;
        if (pte_none(ptent))
                pgoff = linear_page_index(vma, addr);
@@ -5479,8 +5388,7 @@ static int mem_cgroup_move_charge_pte_range(pmd_t *pmd,
                        if (!isolate_lru_page(page)) {
                                pc = lookup_page_cgroup(page);
                                if (!mem_cgroup_move_account(page, HPAGE_PMD_NR,
-                                                            pc, mc.from, mc.to,
-                                                            false)) {
+                                                       pc, mc.from, mc.to)) {
                                        mc.precharge -= HPAGE_PMD_NR;
                                        mc.moved_charge += HPAGE_PMD_NR;
                                }
@@ -5510,7 +5418,7 @@ retry:
                                goto put;
                        pc = lookup_page_cgroup(page);
                        if (!mem_cgroup_move_account(page, 1, pc,
-                                                    mc.from, mc.to, false)) {
+                                                    mc.from, mc.to)) {
                                mc.precharge--;
                                /* we uncharge from mc.from later. */
                                mc.moved_charge++;
@@ -5521,8 +5429,7 @@ put:                      /* get_mctgt_type() gets the page */
                        break;
                case MC_TARGET_SWAP:
                        ent = target.ent;
-                       if (!mem_cgroup_move_swap_account(ent,
-                                               mc.from, mc.to, false)) {
+                       if (!mem_cgroup_move_swap_account(ent, mc.from, mc.to)) {
                                mc.precharge--;
                                /* we fixup refcnts and charges later. */
                                mc.moved_swap++;
@@ -5598,7 +5505,6 @@ static void mem_cgroup_move_task(struct cgroup *cont,
        if (mm) {
                if (mc.to)
                        mem_cgroup_move_charge(mm);
-               put_swap_token(mm);
                mmput(mm);
        }
        if (mc.to)
index c99ad4e6b88c91c2c1d348103cd51524183cfc11..ab1e7145e2909c8e1a02359fb1e148118c42e1b2 100644 (file)
@@ -1388,16 +1388,16 @@ static int get_any_page(struct page *p, unsigned long pfn, int flags)
         */
        if (!get_page_unless_zero(compound_head(p))) {
                if (PageHuge(p)) {
-                       pr_info("get_any_page: %#lx free huge page\n", pfn);
+                       pr_info("%s: %#lx free huge page\n", __func__, pfn);
                        ret = dequeue_hwpoisoned_huge_page(compound_head(p));
                } else if (is_free_buddy_page(p)) {
-                       pr_info("get_any_page: %#lx free buddy page\n", pfn);
+                       pr_info("%s: %#lx free buddy page\n", __func__, pfn);
                        /* Set hwpoison bit while page is still isolated */
                        SetPageHWPoison(p);
                        ret = 0;
                } else {
-                       pr_info("get_any_page: %#lx: unknown zero refcount page type %lx\n",
-                               pfn, p->flags);
+                       pr_info("%s: %#lx: unknown zero refcount page type %lx\n",
+                               __func__, pfn, p->flags);
                        ret = -EIO;
                }
        } else {
index e40f6759ba98b9ebb0ad7af82e6cef6a13382942..1b7dc662bf9f229063cb3e7b97e8e4c22147b92b 100644 (file)
@@ -2908,7 +2908,6 @@ static int do_swap_page(struct mm_struct *mm, struct vm_area_struct *vma,
        delayacct_set_flag(DELAYACCT_PF_SWAPIN);
        page = lookup_swap_cache(entry);
        if (!page) {
-               grab_swap_token(mm); /* Contend for token _before_ read-in */
                page = swapin_readahead(entry,
                                        GFP_HIGHUSER_MOVABLE, vma, address);
                if (!page) {
@@ -2938,6 +2937,7 @@ static int do_swap_page(struct mm_struct *mm, struct vm_area_struct *vma,
        }
 
        locked = lock_page_or_retry(page, mm, flags);
+
        delayacct_clear_flag(DELAYACCT_PF_SWAPIN);
        if (!locked) {
                ret |= VM_FAULT_RETRY;
@@ -3486,6 +3486,7 @@ int handle_mm_fault(struct mm_struct *mm, struct vm_area_struct *vma,
        if (unlikely(is_vm_hugetlb_page(vma)))
                return hugetlb_fault(mm, vma, address, flags);
 
+retry:
        pgd = pgd_offset(mm, address);
        pud = pud_alloc(mm, pgd, address);
        if (!pud)
@@ -3499,13 +3500,24 @@ int handle_mm_fault(struct mm_struct *mm, struct vm_area_struct *vma,
                                                          pmd, flags);
        } else {
                pmd_t orig_pmd = *pmd;
+               int ret;
+
                barrier();
                if (pmd_trans_huge(orig_pmd)) {
                        if (flags & FAULT_FLAG_WRITE &&
                            !pmd_write(orig_pmd) &&
-                           !pmd_trans_splitting(orig_pmd))
-                               return do_huge_pmd_wp_page(mm, vma, address,
-                                                          pmd, orig_pmd);
+                           !pmd_trans_splitting(orig_pmd)) {
+                               ret = do_huge_pmd_wp_page(mm, vma, address, pmd,
+                                                         orig_pmd);
+                               /*
+                                * If COW results in an oom, the huge pmd will
+                                * have been split, so retry the fault on the
+                                * pte for a smaller charge.
+                                */
+                               if (unlikely(ret & VM_FAULT_OOM))
+                                       goto retry;
+                               return ret;
+                       }
                        return 0;
                }
        }
index fc898cb4fe8f39becdaeb6dd2f4ddf519bc599a0..0d7e3ec8e0f3cc997b5fa0b422f5fa39caffe413 100644 (file)
@@ -74,8 +74,7 @@ static struct resource *register_memory_resource(u64 start, u64 size)
        res->end = start + size - 1;
        res->flags = IORESOURCE_MEM | IORESOURCE_BUSY;
        if (request_resource(&iomem_resource, res) < 0) {
-               printk("System RAM resource %llx - %llx cannot be added\n",
-               (unsigned long long)res->start, (unsigned long long)res->end);
+               printk("System RAM resource %pR cannot be added\n", res);
                kfree(res);
                res = NULL;
        }
@@ -502,8 +501,10 @@ int __ref online_pages(unsigned long pfn, unsigned long nr_pages)
                online_pages_range);
        if (ret) {
                mutex_unlock(&zonelists_mutex);
-               printk(KERN_DEBUG "online_pages %lx at %lx failed\n",
-                       nr_pages, pfn);
+               printk(KERN_DEBUG "online_pages [mem %#010llx-%#010llx] failed\n",
+                      (unsigned long long) pfn << PAGE_SHIFT,
+                      (((unsigned long long) pfn + nr_pages)
+                           << PAGE_SHIFT) - 1);
                memory_notify(MEM_CANCEL_ONLINE, &arg);
                unlock_memory_hotplug();
                return ret;
@@ -977,8 +978,9 @@ repeat:
        return 0;
 
 failed_removal:
-       printk(KERN_INFO "memory offlining %lx to %lx failed\n",
-               start_pfn, end_pfn);
+       printk(KERN_INFO "memory offlining [mem %#010llx-%#010llx] failed\n",
+              (unsigned long long) start_pfn << PAGE_SHIFT,
+              ((unsigned long long) end_pfn << PAGE_SHIFT) - 1);
        memory_notify(MEM_CANCEL_OFFLINE, &arg);
        /* pushback to free area */
        undo_isolate_page_range(start_pfn, end_pfn, MIGRATE_MOVABLE);
index 88f9422b92e73afc2be25161f20a7957d4a54160..f15c1b24ca1822c7808b1ea825ca932a9e063b23 100644 (file)
@@ -390,7 +390,7 @@ static void mpol_rebind_policy(struct mempolicy *pol, const nodemask_t *newmask,
 {
        if (!pol)
                return;
-       if (!mpol_store_user_nodemask(pol) && step == 0 &&
+       if (!mpol_store_user_nodemask(pol) && step == MPOL_REBIND_ONCE &&
            nodes_equal(pol->w.cpuset_mems_allowed, *newmask))
                return;
 
@@ -950,8 +950,8 @@ static int migrate_to_node(struct mm_struct *mm, int source, int dest,
  *
  * Returns the number of page that could not be moved.
  */
-int do_migrate_pages(struct mm_struct *mm,
-       const nodemask_t *from_nodes, const nodemask_t *to_nodes, int flags)
+int do_migrate_pages(struct mm_struct *mm, const nodemask_t *from,
+                    const nodemask_t *to, int flags)
 {
        int busy = 0;
        int err;
@@ -963,7 +963,7 @@ int do_migrate_pages(struct mm_struct *mm,
 
        down_read(&mm->mmap_sem);
 
-       err = migrate_vmas(mm, from_nodes, to_nodes, flags);
+       err = migrate_vmas(mm, from, to, flags);
        if (err)
                goto out;
 
@@ -998,14 +998,34 @@ int do_migrate_pages(struct mm_struct *mm,
         * moved to an empty node, then there is nothing left worth migrating.
         */
 
-       tmp = *from_nodes;
+       tmp = *from;
        while (!nodes_empty(tmp)) {
                int s,d;
                int source = -1;
                int dest = 0;
 
                for_each_node_mask(s, tmp) {
-                       d = node_remap(s, *from_nodes, *to_nodes);
+
+                       /*
+                        * do_migrate_pages() tries to maintain the relative
+                        * node relationship of the pages established between
+                        * threads and memory areas.
+                         *
+                        * However if the number of source nodes is not equal to
+                        * the number of destination nodes we can not preserve
+                        * this node relative relationship.  In that case, skip
+                        * copying memory from a node that is in the destination
+                        * mask.
+                        *
+                        * Example: [2,3,4] -> [3,4,5] moves everything.
+                        *          [0-7] - > [3,4,5] moves only 0,1,2,6,7.
+                        */
+
+                       if ((nodes_weight(*from) != nodes_weight(*to)) &&
+                                               (node_isset(s, *to)))
+                               continue;
+
+                       d = node_remap(s, *from, *to);
                        if (s == d)
                                continue;
 
@@ -1065,8 +1085,8 @@ static void migrate_page_add(struct page *page, struct list_head *pagelist,
 {
 }
 
-int do_migrate_pages(struct mm_struct *mm,
-       const nodemask_t *from_nodes, const nodemask_t *to_nodes, int flags)
+int do_migrate_pages(struct mm_struct *mm, const nodemask_t *from,
+                    const nodemask_t *to, int flags)
 {
        return -ENOSYS;
 }
index e8dcfc7de866e2b7c1a1dccd90eb4a9aff89161d..3edfcdfa42d9f27a5238780065220ec3b4fc702a 100644 (file)
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -971,15 +971,13 @@ static inline unsigned long round_hint_to_min(unsigned long hint)
  * The caller must hold down_write(&current->mm->mmap_sem).
  */
 
-static unsigned long do_mmap_pgoff(struct file *file, unsigned long addr,
+unsigned long do_mmap_pgoff(struct file *file, unsigned long addr,
                        unsigned long len, unsigned long prot,
                        unsigned long flags, unsigned long pgoff)
 {
        struct mm_struct * mm = current->mm;
        struct inode *inode;
        vm_flags_t vm_flags;
-       int error;
-       unsigned long reqprot = prot;
 
        /*
         * Does the application expect PROT_READ to imply PROT_EXEC?
@@ -1101,39 +1099,9 @@ static unsigned long do_mmap_pgoff(struct file *file, unsigned long addr,
                }
        }
 
-       error = security_file_mmap(file, reqprot, prot, flags, addr, 0);
-       if (error)
-               return error;
-
        return mmap_region(file, addr, len, flags, vm_flags, pgoff);
 }
 
-unsigned long do_mmap(struct file *file, unsigned long addr,
-       unsigned long len, unsigned long prot,
-       unsigned long flag, unsigned long offset)
-{
-       if (unlikely(offset + PAGE_ALIGN(len) < offset))
-               return -EINVAL;
-       if (unlikely(offset & ~PAGE_MASK))
-               return -EINVAL;
-       return do_mmap_pgoff(file, addr, len, prot, flag, offset >> PAGE_SHIFT);
-}
-EXPORT_SYMBOL(do_mmap);
-
-unsigned long vm_mmap(struct file *file, unsigned long addr,
-       unsigned long len, unsigned long prot,
-       unsigned long flag, unsigned long offset)
-{
-       unsigned long ret;
-       struct mm_struct *mm = current->mm;
-
-       down_write(&mm->mmap_sem);
-       ret = do_mmap(file, addr, len, prot, flag, offset);
-       up_write(&mm->mmap_sem);
-       return ret;
-}
-EXPORT_SYMBOL(vm_mmap);
-
 SYSCALL_DEFINE6(mmap_pgoff, unsigned long, addr, unsigned long, len,
                unsigned long, prot, unsigned long, flags,
                unsigned long, fd, unsigned long, pgoff)
@@ -1165,10 +1133,7 @@ SYSCALL_DEFINE6(mmap_pgoff, unsigned long, addr, unsigned long, len,
 
        flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
 
-       down_write(&current->mm->mmap_sem);
-       retval = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
-       up_write(&current->mm->mmap_sem);
-
+       retval = vm_mmap_pgoff(file, addr, len, prot, flags, pgoff);
        if (file)
                fput(file);
 out:
@@ -1629,7 +1594,9 @@ get_unmapped_area(struct file *file, unsigned long addr, unsigned long len,
        if (addr & ~PAGE_MASK)
                return -EINVAL;
 
-       return arch_rebalance_pgtables(addr, len);
+       addr = arch_rebalance_pgtables(addr, len);
+       error = security_mmap_addr(addr);
+       return error ? error : addr;
 }
 
 EXPORT_SYMBOL(get_unmapped_area);
@@ -1639,33 +1606,34 @@ struct vm_area_struct *find_vma(struct mm_struct *mm, unsigned long addr)
 {
        struct vm_area_struct *vma = NULL;
 
-       if (mm) {
-               /* Check the cache first. */
-               /* (Cache hit rate is typically around 35%.) */
-               vma = mm->mmap_cache;
-               if (!(vma && vma->vm_end > addr && vma->vm_start <= addr)) {
-                       struct rb_node * rb_node;
-
-                       rb_node = mm->mm_rb.rb_node;
-                       vma = NULL;
-
-                       while (rb_node) {
-                               struct vm_area_struct * vma_tmp;
-
-                               vma_tmp = rb_entry(rb_node,
-                                               struct vm_area_struct, vm_rb);
-
-                               if (vma_tmp->vm_end > addr) {
-                                       vma = vma_tmp;
-                                       if (vma_tmp->vm_start <= addr)
-                                               break;
-                                       rb_node = rb_node->rb_left;
-                               } else
-                                       rb_node = rb_node->rb_right;
-                       }
-                       if (vma)
-                               mm->mmap_cache = vma;
+       if (WARN_ON_ONCE(!mm))          /* Remove this in linux-3.6 */
+               return NULL;
+
+       /* Check the cache first. */
+       /* (Cache hit rate is typically around 35%.) */
+       vma = mm->mmap_cache;
+       if (!(vma && vma->vm_end > addr && vma->vm_start <= addr)) {
+               struct rb_node *rb_node;
+
+               rb_node = mm->mm_rb.rb_node;
+               vma = NULL;
+
+               while (rb_node) {
+                       struct vm_area_struct *vma_tmp;
+
+                       vma_tmp = rb_entry(rb_node,
+                                          struct vm_area_struct, vm_rb);
+
+                       if (vma_tmp->vm_end > addr) {
+                               vma = vma_tmp;
+                               if (vma_tmp->vm_start <= addr)
+                                       break;
+                               rb_node = rb_node->rb_left;
+                       } else
+                               rb_node = rb_node->rb_right;
                }
+               if (vma)
+                       mm->mmap_cache = vma;
        }
        return vma;
 }
@@ -1818,7 +1786,7 @@ int expand_downwards(struct vm_area_struct *vma,
                return -ENOMEM;
 
        address &= PAGE_MASK;
-       error = security_file_mmap(NULL, 0, 0, 0, address, 1);
+       error = security_mmap_addr(address);
        if (error)
                return error;
 
@@ -2158,7 +2126,6 @@ int do_munmap(struct mm_struct *mm, unsigned long start, size_t len)
 
        return 0;
 }
-EXPORT_SYMBOL(do_munmap);
 
 int vm_munmap(unsigned long start, size_t len)
 {
@@ -2206,10 +2173,6 @@ static unsigned long do_brk(unsigned long addr, unsigned long len)
        if (!len)
                return addr;
 
-       error = security_file_mmap(NULL, 0, 0, 0, addr, 1);
-       if (error)
-               return error;
-
        flags = VM_DATA_DEFAULT_FLAGS | VM_ACCOUNT | mm->def_flags;
 
        error = get_unmapped_area(NULL, addr, len, 0, MAP_FIXED);
@@ -2562,10 +2525,6 @@ int install_special_mapping(struct mm_struct *mm,
        vma->vm_ops = &special_mapping_vmops;
        vma->vm_private_data = pages;
 
-       ret = security_file_mmap(NULL, 0, 0, 0, vma->vm_start, 1);
-       if (ret)
-               goto out;
-
        ret = insert_vm_struct(mm, vma);
        if (ret)
                goto out;
index 7cf7b7ddc7c552d4d514a696b30d6cd20a649d4d..6830eab5bf09c5c5432801ed7ad23d4bad054779 100644 (file)
@@ -86,3 +86,17 @@ int memmap_valid_within(unsigned long pfn,
        return 1;
 }
 #endif /* CONFIG_ARCH_HAS_HOLES_MEMORYMODEL */
+
+void lruvec_init(struct lruvec *lruvec, struct zone *zone)
+{
+       enum lru_list lru;
+
+       memset(lruvec, 0, sizeof(struct lruvec));
+
+       for_each_lru(lru)
+               INIT_LIST_HEAD(&lruvec->lists[lru]);
+
+#ifdef CONFIG_CGROUP_MEM_RES_CTLR
+       lruvec->zone = zone;
+#endif
+}
index db8d983b5a7d7a2d6746ccbf74471d2ced3cdd96..21fed202ddad865bb3ee70d07d8ebce17fd37493 100644 (file)
@@ -371,10 +371,6 @@ static unsigned long mremap_to(unsigned long addr,
        if ((addr <= new_addr) && (addr+old_len) > new_addr)
                goto out;
 
-       ret = security_file_mmap(NULL, 0, 0, 0, new_addr, 1);
-       if (ret)
-               goto out;
-
        ret = do_munmap(mm, new_addr, new_len);
        if (ret)
                goto out;
@@ -432,15 +428,17 @@ static int vma_expandable(struct vm_area_struct *vma, unsigned long delta)
  * MREMAP_FIXED option added 5-Dec-1999 by Benjamin LaHaise
  * This option implies MREMAP_MAYMOVE.
  */
-unsigned long do_mremap(unsigned long addr,
-       unsigned long old_len, unsigned long new_len,
-       unsigned long flags, unsigned long new_addr)
+SYSCALL_DEFINE5(mremap, unsigned long, addr, unsigned long, old_len,
+               unsigned long, new_len, unsigned long, flags,
+               unsigned long, new_addr)
 {
        struct mm_struct *mm = current->mm;
        struct vm_area_struct *vma;
        unsigned long ret = -EINVAL;
        unsigned long charged = 0;
 
+       down_write(&current->mm->mmap_sem);
+
        if (flags & ~(MREMAP_FIXED | MREMAP_MAYMOVE))
                goto out;
 
@@ -530,25 +528,11 @@ unsigned long do_mremap(unsigned long addr,
                        goto out;
                }
 
-               ret = security_file_mmap(NULL, 0, 0, 0, new_addr, 1);
-               if (ret)
-                       goto out;
                ret = move_vma(vma, addr, old_len, new_len, new_addr);
        }
 out:
        if (ret & ~PAGE_MASK)
                vm_unacct_memory(charged);
-       return ret;
-}
-
-SYSCALL_DEFINE5(mremap, unsigned long, addr, unsigned long, old_len,
-               unsigned long, new_len, unsigned long, flags,
-               unsigned long, new_addr)
-{
-       unsigned long ret;
-
-       down_write(&current->mm->mmap_sem);
-       ret = do_mremap(addr, old_len, new_len, flags, new_addr);
        up_write(&current->mm->mmap_sem);
        return ret;
 }
index 1983fb1c7026c0ef4c1e705298876d0ab2449c55..d23415c001bc4c5847986c7659261ca335888382 100644 (file)
@@ -274,86 +274,85 @@ void * __init __alloc_bootmem(unsigned long size, unsigned long align,
        return ___alloc_bootmem(size, align, goal, limit);
 }
 
-/**
- * __alloc_bootmem_node - allocate boot memory from a specific node
- * @pgdat: node to allocate from
- * @size: size of the request in bytes
- * @align: alignment of the region
- * @goal: preferred starting address of the region
- *
- * The goal is dropped if it can not be satisfied and the allocation will
- * fall back to memory below @goal.
- *
- * Allocation may fall back to any node in the system if the specified node
- * can not hold the requested memory.
- *
- * The function panics if the request can not be satisfied.
- */
-void * __init __alloc_bootmem_node(pg_data_t *pgdat, unsigned long size,
-                                  unsigned long align, unsigned long goal)
+static void * __init ___alloc_bootmem_node_nopanic(pg_data_t *pgdat,
+                                                  unsigned long size,
+                                                  unsigned long align,
+                                                  unsigned long goal,
+                                                  unsigned long limit)
 {
        void *ptr;
 
-       if (WARN_ON_ONCE(slab_is_available()))
-               return kzalloc_node(size, GFP_NOWAIT, pgdat->node_id);
-
 again:
        ptr = __alloc_memory_core_early(pgdat->node_id, size, align,
-                                        goal, -1ULL);
+                                       goal, limit);
        if (ptr)
                return ptr;
 
        ptr = __alloc_memory_core_early(MAX_NUMNODES, size, align,
-                                       goal, -1ULL);
-       if (!ptr && goal) {
+                                       goal, limit);
+       if (ptr)
+               return ptr;
+
+       if (goal) {
                goal = 0;
                goto again;
        }
-       return ptr;
+
+       return NULL;
 }
 
-void * __init __alloc_bootmem_node_high(pg_data_t *pgdat, unsigned long size,
+void * __init __alloc_bootmem_node_nopanic(pg_data_t *pgdat, unsigned long size,
                                   unsigned long align, unsigned long goal)
 {
-       return __alloc_bootmem_node(pgdat, size, align, goal);
+       if (WARN_ON_ONCE(slab_is_available()))
+               return kzalloc_node(size, GFP_NOWAIT, pgdat->node_id);
+
+       return ___alloc_bootmem_node_nopanic(pgdat, size, align, goal, 0);
 }
 
-#ifdef CONFIG_SPARSEMEM
-/**
- * alloc_bootmem_section - allocate boot memory from a specific section
- * @size: size of the request in bytes
- * @section_nr: sparse map section to allocate from
- *
- * Return NULL on failure.
- */
-void * __init alloc_bootmem_section(unsigned long size,
-                                   unsigned long section_nr)
+void * __init ___alloc_bootmem_node(pg_data_t *pgdat, unsigned long size,
+                                   unsigned long align, unsigned long goal,
+                                   unsigned long limit)
 {
-       unsigned long pfn, goal, limit;
+       void *ptr;
 
-       pfn = section_nr_to_pfn(section_nr);
-       goal = pfn << PAGE_SHIFT;
-       limit = section_nr_to_pfn(section_nr + 1) << PAGE_SHIFT;
+       ptr = ___alloc_bootmem_node_nopanic(pgdat, size, align, goal, limit);
+       if (ptr)
+               return ptr;
 
-       return __alloc_memory_core_early(early_pfn_to_nid(pfn), size,
-                                        SMP_CACHE_BYTES, goal, limit);
+       printk(KERN_ALERT "bootmem alloc of %lu bytes failed!\n", size);
+       panic("Out of memory");
+       return NULL;
 }
-#endif
 
-void * __init __alloc_bootmem_node_nopanic(pg_data_t *pgdat, unsigned long size,
+/**
+ * __alloc_bootmem_node - allocate boot memory from a specific node
+ * @pgdat: node to allocate from
+ * @size: size of the request in bytes
+ * @align: alignment of the region
+ * @goal: preferred starting address of the region
+ *
+ * The goal is dropped if it can not be satisfied and the allocation will
+ * fall back to memory below @goal.
+ *
+ * Allocation may fall back to any node in the system if the specified node
+ * can not hold the requested memory.
+ *
+ * The function panics if the request can not be satisfied.
+ */
+void * __init __alloc_bootmem_node(pg_data_t *pgdat, unsigned long size,
                                   unsigned long align, unsigned long goal)
 {
-       void *ptr;
-
        if (WARN_ON_ONCE(slab_is_available()))
                return kzalloc_node(size, GFP_NOWAIT, pgdat->node_id);
 
-       ptr =  __alloc_memory_core_early(pgdat->node_id, size, align,
-                                                goal, -1ULL);
-       if (ptr)
-               return ptr;
+       return ___alloc_bootmem_node(pgdat, size, align, goal, 0);
+}
 
-       return __alloc_bootmem_nopanic(size, align, goal);
+void * __init __alloc_bootmem_node_high(pg_data_t *pgdat, unsigned long size,
+                                  unsigned long align, unsigned long goal)
+{
+       return __alloc_bootmem_node(pgdat, size, align, goal);
 }
 
 #ifndef ARCH_LOW_ADDRESS_LIMIT
@@ -397,16 +396,9 @@ void * __init __alloc_bootmem_low(unsigned long size, unsigned long align,
 void * __init __alloc_bootmem_low_node(pg_data_t *pgdat, unsigned long size,
                                       unsigned long align, unsigned long goal)
 {
-       void *ptr;
-
        if (WARN_ON_ONCE(slab_is_available()))
                return kzalloc_node(size, GFP_NOWAIT, pgdat->node_id);
 
-       ptr = __alloc_memory_core_early(pgdat->node_id, size, align,
-                               goal, ARCH_LOW_ADDRESS_LIMIT);
-       if (ptr)
-               return ptr;
-
-       return  __alloc_memory_core_early(MAX_NUMNODES, size, align,
-                               goal, ARCH_LOW_ADDRESS_LIMIT);
+       return ___alloc_bootmem_node(pgdat, size, align, goal,
+                                    ARCH_LOW_ADDRESS_LIMIT);
 }
index bb8f4f004a82ce57abb0653a9a8ed72d533f5c45..c4acfbc099727b3f5151ed5917961ff59efb7481 100644 (file)
@@ -889,7 +889,6 @@ static int validate_mmap_request(struct file *file,
                                 unsigned long *_capabilities)
 {
        unsigned long capabilities, rlen;
-       unsigned long reqprot = prot;
        int ret;
 
        /* do the simple checks first */
@@ -1047,7 +1046,7 @@ static int validate_mmap_request(struct file *file,
        }
 
        /* allow the security API to have its say */
-       ret = security_file_mmap(file, reqprot, prot, flags, addr, 0);
+       ret = security_mmap_addr(addr);
        if (ret < 0)
                return ret;
 
@@ -1233,7 +1232,7 @@ enomem:
 /*
  * handle mapping creation for uClinux
  */
-static unsigned long do_mmap_pgoff(struct file *file,
+unsigned long do_mmap_pgoff(struct file *file,
                            unsigned long addr,
                            unsigned long len,
                            unsigned long prot,
@@ -1471,32 +1470,6 @@ error_getting_region:
        return -ENOMEM;
 }
 
-unsigned long do_mmap(struct file *file, unsigned long addr,
-       unsigned long len, unsigned long prot,
-       unsigned long flag, unsigned long offset)
-{
-       if (unlikely(offset + PAGE_ALIGN(len) < offset))
-               return -EINVAL;
-       if (unlikely(offset & ~PAGE_MASK))
-               return -EINVAL;
-       return do_mmap_pgoff(file, addr, len, prot, flag, offset >> PAGE_SHIFT);
-}
-EXPORT_SYMBOL(do_mmap);
-
-unsigned long vm_mmap(struct file *file, unsigned long addr,
-       unsigned long len, unsigned long prot,
-       unsigned long flag, unsigned long offset)
-{
-       unsigned long ret;
-       struct mm_struct *mm = current->mm;
-
-       down_write(&mm->mmap_sem);
-       ret = do_mmap(file, addr, len, prot, flag, offset);
-       up_write(&mm->mmap_sem);
-       return ret;
-}
-EXPORT_SYMBOL(vm_mmap);
-
 SYSCALL_DEFINE6(mmap_pgoff, unsigned long, addr, unsigned long, len,
                unsigned long, prot, unsigned long, flags,
                unsigned long, fd, unsigned long, pgoff)
@@ -1513,9 +1486,7 @@ SYSCALL_DEFINE6(mmap_pgoff, unsigned long, addr, unsigned long, len,
 
        flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
 
-       down_write(&current->mm->mmap_sem);
-       retval = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
-       up_write(&current->mm->mmap_sem);
+       ret = vm_mmap_pgoff(file, addr, len, prot, flags, pgoff);
 
        if (file)
                fput(file);
index 9f09a1fde9f9954616473e1452c9e7e67c0778b8..ed0e19677360fa55f62e3944208e82cab7eeacc8 100644 (file)
@@ -180,10 +180,10 @@ static bool oom_unkillable_task(struct task_struct *p,
  * predictable as possible.  The goal is to return the highest value for the
  * task consuming the most memory to avoid subsequent oom failures.
  */
-unsigned int oom_badness(struct task_struct *p, struct mem_cgroup *memcg,
-                     const nodemask_t *nodemask, unsigned long totalpages)
+unsigned long oom_badness(struct task_struct *p, struct mem_cgroup *memcg,
+                         const nodemask_t *nodemask, unsigned long totalpages)
 {
-       long points;
+       unsigned long points;
 
        if (oom_unkillable_task(p, memcg, nodemask))
                return 0;
@@ -197,22 +197,12 @@ unsigned int oom_badness(struct task_struct *p, struct mem_cgroup *memcg,
                return 0;
        }
 
-       /*
-        * The memory controller may have a limit of 0 bytes, so avoid a divide
-        * by zero, if necessary.
-        */
-       if (!totalpages)
-               totalpages = 1;
-
        /*
         * The baseline for the badness score is the proportion of RAM that each
         * task's rss, pagetable and swap space use.
         */
-       points = get_mm_rss(p->mm) + p->mm->nr_ptes;
-       points += get_mm_counter(p->mm, MM_SWAPENTS);
-
-       points *= 1000;
-       points /= totalpages;
+       points = get_mm_rss(p->mm) + p->mm->nr_ptes +
+                get_mm_counter(p->mm, MM_SWAPENTS);
        task_unlock(p);
 
        /*
@@ -220,23 +210,20 @@ unsigned int oom_badness(struct task_struct *p, struct mem_cgroup *memcg,
         * implementation used by LSMs.
         */
        if (has_capability_noaudit(p, CAP_SYS_ADMIN))
-               points -= 30;
+               points -= 30 * totalpages / 1000;
 
        /*
         * /proc/pid/oom_score_adj ranges from -1000 to +1000 such that it may
         * either completely disable oom killing or always prefer a certain
         * task.
         */
-       points += p->signal->oom_score_adj;
+       points += p->signal->oom_score_adj * totalpages / 1000;
 
        /*
-        * Never return 0 for an eligible task that may be killed since it's
-        * possible that no single user task uses more than 0.1% of memory and
-        * no single admin tasks uses more than 3.0%.
+        * Never return 0 for an eligible task regardless of the root bonus and
+        * oom_score_adj (oom_score_adj can't be OOM_SCORE_ADJ_MIN here).
         */
-       if (points <= 0)
-               return 1;
-       return (points < 1000) ? points : 1000;
+       return points ? points : 1;
 }
 
 /*
@@ -314,7 +301,7 @@ static struct task_struct *select_bad_process(unsigned int *ppoints,
 {
        struct task_struct *g, *p;
        struct task_struct *chosen = NULL;
-       *ppoints = 0;
+       unsigned long chosen_points = 0;
 
        do_each_thread(g, p) {
                unsigned int points;
@@ -354,7 +341,7 @@ static struct task_struct *select_bad_process(unsigned int *ppoints,
                         */
                        if (p == current) {
                                chosen = p;
-                               *ppoints = 1000;
+                               chosen_points = ULONG_MAX;
                        } else if (!force_kill) {
                                /*
                                 * If this task is not being ptraced on exit,
@@ -367,12 +354,13 @@ static struct task_struct *select_bad_process(unsigned int *ppoints,
                }
 
                points = oom_badness(p, memcg, nodemask, totalpages);
-               if (points > *ppoints) {
+               if (points > chosen_points) {
                        chosen = p;
-                       *ppoints = points;
+                       chosen_points = points;
                }
        } while_each_thread(g, p);
 
+       *ppoints = chosen_points * 1000 / totalpages;
        return chosen;
 }
 
@@ -572,7 +560,7 @@ void mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask,
        }
 
        check_panic_on_oom(CONSTRAINT_MEMCG, gfp_mask, order, NULL);
-       limit = mem_cgroup_get_limit(memcg) >> PAGE_SHIFT;
+       limit = mem_cgroup_get_limit(memcg) >> PAGE_SHIFT ? : 1;
        read_lock(&tasklist_lock);
        p = select_bad_process(&points, limit, memcg, NULL, false);
        if (p && PTR_ERR(p) != -1UL)
index bab8e3bc4202a2d0b07f02ca0ddfae09026c59cd..6092f331b32e496bb522f4f17741c3594cc4ba0d 100644 (file)
@@ -219,7 +219,7 @@ EXPORT_SYMBOL(nr_online_nodes);
 
 int page_group_by_mobility_disabled __read_mostly;
 
-static void set_pageblock_migratetype(struct page *page, int migratetype)
+void set_pageblock_migratetype(struct page *page, int migratetype)
 {
 
        if (unlikely(page_group_by_mobility_disabled))
@@ -954,8 +954,8 @@ static int move_freepages(struct zone *zone,
        return pages_moved;
 }
 
-static int move_freepages_block(struct zone *zone, struct page *page,
-                               int migratetype)
+int move_freepages_block(struct zone *zone, struct page *page,
+                        int migratetype)
 {
        unsigned long start_pfn, end_pfn;
        struct page *start_page, *end_page;
@@ -4300,25 +4300,24 @@ static inline void setup_usemap(struct pglist_data *pgdat,
 
 #ifdef CONFIG_HUGETLB_PAGE_SIZE_VARIABLE
 
-/* Return a sensible default order for the pageblock size. */
-static inline int pageblock_default_order(void)
-{
-       if (HPAGE_SHIFT > PAGE_SHIFT)
-               return HUGETLB_PAGE_ORDER;
-
-       return MAX_ORDER-1;
-}
-
 /* Initialise the number of pages represented by NR_PAGEBLOCK_BITS */
-static inline void __init set_pageblock_order(unsigned int order)
+static inline void __init set_pageblock_order(void)
 {
+       unsigned int order;
+
        /* Check that pageblock_nr_pages has not already been setup */
        if (pageblock_order)
                return;
 
+       if (HPAGE_SHIFT > PAGE_SHIFT)
+               order = HUGETLB_PAGE_ORDER;
+       else
+               order = MAX_ORDER - 1;
+
        /*
         * Assume the largest contiguous order of interest is a huge page.
-        * This value may be variable depending on boot parameters on IA64
+        * This value may be variable depending on boot parameters on IA64 and
+        * powerpc.
         */
        pageblock_order = order;
 }
@@ -4326,15 +4325,13 @@ static inline void __init set_pageblock_order(unsigned int order)
 
 /*
  * When CONFIG_HUGETLB_PAGE_SIZE_VARIABLE is not set, set_pageblock_order()
- * and pageblock_default_order() are unused as pageblock_order is set
- * at compile-time. See include/linux/pageblock-flags.h for the values of
- * pageblock_order based on the kernel config
+ * is unused as pageblock_order is set at compile-time. See
+ * include/linux/pageblock-flags.h for the values of pageblock_order based on
+ * the kernel config
  */
-static inline int pageblock_default_order(unsigned int order)
+static inline void set_pageblock_order(void)
 {
-       return MAX_ORDER-1;
 }
-#define set_pageblock_order(x) do {} while (0)
 
 #endif /* CONFIG_HUGETLB_PAGE_SIZE_VARIABLE */
 
@@ -4361,7 +4358,6 @@ static void __paginginit free_area_init_core(struct pglist_data *pgdat,
        for (j = 0; j < MAX_NR_ZONES; j++) {
                struct zone *zone = pgdat->node_zones + j;
                unsigned long size, realsize, memmap_pages;
-               enum lru_list lru;
 
                size = zone_spanned_pages_in_node(nid, j, zones_size);
                realsize = size - zone_absent_pages_in_node(nid, j,
@@ -4411,18 +4407,13 @@ static void __paginginit free_area_init_core(struct pglist_data *pgdat,
                zone->zone_pgdat = pgdat;
 
                zone_pcp_init(zone);
-               for_each_lru(lru)
-                       INIT_LIST_HEAD(&zone->lruvec.lists[lru]);
-               zone->reclaim_stat.recent_rotated[0] = 0;
-               zone->reclaim_stat.recent_rotated[1] = 0;
-               zone->reclaim_stat.recent_scanned[0] = 0;
-               zone->reclaim_stat.recent_scanned[1] = 0;
+               lruvec_init(&zone->lruvec, zone);
                zap_zone_vm_stats(zone);
                zone->flags = 0;
                if (!size)
                        continue;
 
-               set_pageblock_order(pageblock_default_order());
+               set_pageblock_order();
                setup_usemap(pgdat, zone, size);
                ret = init_currently_empty_zone(zone, zone_start_pfn,
                                                size, MEMMAP_EARLY);
@@ -4815,7 +4806,7 @@ void __init free_area_init_nodes(unsigned long *max_zone_pfn)
        find_zone_movable_pfns_for_nodes();
 
        /* Print out the zone ranges */
-       printk("Zone PFN ranges:\n");
+       printk("Zone ranges:\n");
        for (i = 0; i < MAX_NR_ZONES; i++) {
                if (i == ZONE_MOVABLE)
                        continue;
@@ -4824,22 +4815,25 @@ void __init free_area_init_nodes(unsigned long *max_zone_pfn)
                                arch_zone_highest_possible_pfn[i])
                        printk(KERN_CONT "empty\n");
                else
-                       printk(KERN_CONT "%0#10lx -> %0#10lx\n",
-                               arch_zone_lowest_possible_pfn[i],
-                               arch_zone_highest_possible_pfn[i]);
+                       printk(KERN_CONT "[mem %0#10lx-%0#10lx]\n",
+                               arch_zone_lowest_possible_pfn[i] << PAGE_SHIFT,
+                               (arch_zone_highest_possible_pfn[i]
+                                       << PAGE_SHIFT) - 1);
        }
 
        /* Print out the PFNs ZONE_MOVABLE begins at in each node */
-       printk("Movable zone start PFN for each node\n");
+       printk("Movable zone start for each node\n");
        for (i = 0; i < MAX_NUMNODES; i++) {
                if (zone_movable_pfn[i])
-                       printk("  Node %d: %lu\n", i, zone_movable_pfn[i]);
+                       printk("  Node %d: %#010lx\n", i,
+                              zone_movable_pfn[i] << PAGE_SHIFT);
        }
 
        /* Print out the early_node_map[] */
-       printk("Early memory PFN ranges\n");
+       printk("Early memory node ranges\n");
        for_each_mem_pfn_range(i, MAX_NUMNODES, &start_pfn, &end_pfn, &nid)
-               printk("  %3d: %0#10lx -> %0#10lx\n", nid, start_pfn, end_pfn);
+               printk("  node %3d: [mem %#010lx-%#010lx]\n", nid,
+                      start_pfn << PAGE_SHIFT, (end_pfn << PAGE_SHIFT) - 1);
 
        /* Initialise every node */
        mminit_verify_pageflags_layout();
@@ -5657,7 +5651,7 @@ static int __alloc_contig_migrate_range(unsigned long start, unsigned long end)
                .nr_migratepages = 0,
                .order = -1,
                .zone = page_zone(pfn_to_page(start)),
-               .sync = true,
+               .mode = COMPACT_SYNC,
        };
        INIT_LIST_HEAD(&cc.migratepages);
 
@@ -5938,7 +5932,7 @@ bool is_free_buddy_page(struct page *page)
 }
 #endif
 
-static struct trace_print_flags pageflag_names[] = {
+static const struct trace_print_flags pageflag_names[] = {
        {1UL << PG_locked,              "locked"        },
        {1UL << PG_error,               "error"         },
        {1UL << PG_referenced,          "referenced"    },
@@ -5973,7 +5967,9 @@ static struct trace_print_flags pageflag_names[] = {
 #ifdef CONFIG_MEMORY_FAILURE
        {1UL << PG_hwpoison,            "hwpoison"      },
 #endif
-       {-1UL,                          NULL            },
+#ifdef CONFIG_TRANSPARENT_HUGEPAGE
+       {1UL << PG_compound_lock,       "compound_lock" },
+#endif
 };
 
 static void dump_page_flags(unsigned long flags)
@@ -5982,12 +5978,14 @@ static void dump_page_flags(unsigned long flags)
        unsigned long mask;
        int i;
 
+       BUILD_BUG_ON(ARRAY_SIZE(pageflag_names) != __NR_PAGEFLAGS);
+
        printk(KERN_ALERT "page flags: %#lx(", flags);
 
        /* remove zone id */
        flags &= (1UL << NR_PAGEFLAGS) - 1;
 
-       for (i = 0; pageflag_names[i].name && flags; i++) {
+       for (i = 0; i < ARRAY_SIZE(pageflag_names) && flags; i++) {
 
                mask = pageflag_names[i].mask;
                if ((flags & mask) != mask)
index c20ff48994c29050953c79fcdb0633e690bb653e..926b466497492f3f8463ebc623adc4fbddf9547a 100644 (file)
@@ -371,15 +371,15 @@ static ssize_t process_vm_rw(pid_t pid,
        /* Check iovecs */
        if (vm_write)
                rc = rw_copy_check_uvector(WRITE, lvec, liovcnt, UIO_FASTIOV,
-                                          iovstack_l, &iov_l, 1);
+                                          iovstack_l, &iov_l);
        else
                rc = rw_copy_check_uvector(READ, lvec, liovcnt, UIO_FASTIOV,
-                                          iovstack_l, &iov_l, 1);
+                                          iovstack_l, &iov_l);
        if (rc <= 0)
                goto free_iovecs;
 
-       rc = rw_copy_check_uvector(READ, rvec, riovcnt, UIO_FASTIOV,
-                                  iovstack_r, &iov_r, 0);
+       rc = rw_copy_check_uvector(CHECK_IOVEC_ONLY, rvec, riovcnt, UIO_FASTIOV,
+                                  iovstack_r, &iov_r);
        if (rc <= 0)
                goto free_iovecs;
 
@@ -438,16 +438,16 @@ compat_process_vm_rw(compat_pid_t pid,
        if (vm_write)
                rc = compat_rw_copy_check_uvector(WRITE, lvec, liovcnt,
                                                  UIO_FASTIOV, iovstack_l,
-                                                 &iov_l, 1);
+                                                 &iov_l);
        else
                rc = compat_rw_copy_check_uvector(READ, lvec, liovcnt,
                                                  UIO_FASTIOV, iovstack_l,
-                                                 &iov_l, 1);
+                                                 &iov_l);
        if (rc <= 0)
                goto free_iovecs;
-       rc = compat_rw_copy_check_uvector(READ, rvec, riovcnt,
+       rc = compat_rw_copy_check_uvector(CHECK_IOVEC_ONLY, rvec, riovcnt,
                                          UIO_FASTIOV, iovstack_r,
-                                         &iov_r, 0);
+                                         &iov_r);
        if (rc <= 0)
                goto free_iovecs;
 
index cbcbb02f3e28ab9667d196e6316588856206df16..ea8f8fa21649d7069543e19e0e48ae38d63c175e 100644 (file)
@@ -17,6 +17,8 @@
 #include <linux/task_io_accounting_ops.h>
 #include <linux/pagevec.h>
 #include <linux/pagemap.h>
+#include <linux/syscalls.h>
+#include <linux/file.h>
 
 /*
  * Initialise a struct file's readahead state.  Assumes that the caller has
@@ -562,3 +564,41 @@ page_cache_async_readahead(struct address_space *mapping,
        ondemand_readahead(mapping, ra, filp, true, offset, req_size);
 }
 EXPORT_SYMBOL_GPL(page_cache_async_readahead);
+
+static ssize_t
+do_readahead(struct address_space *mapping, struct file *filp,
+            pgoff_t index, unsigned long nr)
+{
+       if (!mapping || !mapping->a_ops || !mapping->a_ops->readpage)
+               return -EINVAL;
+
+       force_page_cache_readahead(mapping, filp, index, nr);
+       return 0;
+}
+
+SYSCALL_DEFINE(readahead)(int fd, loff_t offset, size_t count)
+{
+       ssize_t ret;
+       struct file *file;
+
+       ret = -EBADF;
+       file = fget(fd);
+       if (file) {
+               if (file->f_mode & FMODE_READ) {
+                       struct address_space *mapping = file->f_mapping;
+                       pgoff_t start = offset >> PAGE_CACHE_SHIFT;
+                       pgoff_t end = (offset + count - 1) >> PAGE_CACHE_SHIFT;
+                       unsigned long len = end - start + 1;
+                       ret = do_readahead(mapping, file, start, len);
+               }
+               fput(file);
+       }
+       return ret;
+}
+#ifdef CONFIG_HAVE_SYSCALL_WRAPPERS
+asmlinkage long SyS_readahead(long fd, loff_t offset, long count)
+{
+       return SYSC_readahead((int) fd, offset, (size_t) count);
+}
+SYSCALL_ALIAS(sys_readahead, SyS_readahead);
+#endif
index 5b5ad584ffb7dd7c9e00a885a018aaadba22f371..0f3b7cda2a24c5705ea4ad6e7ef127f53fdf3633 100644 (file)
--- a/mm/rmap.c
+++ b/mm/rmap.c
@@ -755,12 +755,6 @@ int page_referenced_one(struct page *page, struct vm_area_struct *vma,
                pte_unmap_unlock(pte, ptl);
        }
 
-       /* Pretend the page is referenced if the task has the
-          swap token and is in the middle of a page fault. */
-       if (mm != current->mm && has_swap_token(mm) &&
-                       rwsem_is_locked(&mm->mmap_sem))
-               referenced++;
-
        (*mapcount)--;
 
        if (referenced)
index be5af34a070dcdb73e9cad2accea606aeae53336..585bd220a21ee4e5eefaec2b9c46dc9ae68fde98 100644 (file)
@@ -53,6 +53,7 @@ static struct vfsmount *shm_mnt;
 #include <linux/blkdev.h>
 #include <linux/pagevec.h>
 #include <linux/percpu_counter.h>
+#include <linux/falloc.h>
 #include <linux/splice.h>
 #include <linux/security.h>
 #include <linux/swapops.h>
@@ -83,12 +84,25 @@ struct shmem_xattr {
        char value[0];
 };
 
+/*
+ * shmem_fallocate and shmem_writepage communicate via inode->i_private
+ * (with i_mutex making sure that it has only one user at a time):
+ * we would prefer not to enlarge the shmem inode just for that.
+ */
+struct shmem_falloc {
+       pgoff_t start;          /* start of range currently being fallocated */
+       pgoff_t next;           /* the next page offset to be fallocated */
+       pgoff_t nr_falloced;    /* how many new pages have been fallocated */
+       pgoff_t nr_unswapped;   /* how often writepage refused to swap out */
+};
+
 /* Flag allocation requirements to shmem_getpage */
 enum sgp_type {
        SGP_READ,       /* don't exceed i_size, don't allocate page */
        SGP_CACHE,      /* don't exceed i_size, may allocate page */
        SGP_DIRTY,      /* like SGP_CACHE, but set new page dirty */
-       SGP_WRITE,      /* may exceed i_size, may allocate page */
+       SGP_WRITE,      /* may exceed i_size, may allocate !Uptodate page */
+       SGP_FALLOC,     /* like SGP_WRITE, but make existing page Uptodate */
 };
 
 #ifdef CONFIG_TMPFS
@@ -103,6 +117,9 @@ static unsigned long shmem_default_max_inodes(void)
 }
 #endif
 
+static bool shmem_should_replace_page(struct page *page, gfp_t gfp);
+static int shmem_replace_page(struct page **pagep, gfp_t gfp,
+                               struct shmem_inode_info *info, pgoff_t index);
 static int shmem_getpage_gfp(struct inode *inode, pgoff_t index,
        struct page **pagep, enum sgp_type sgp, gfp_t gfp, int *fault_type);
 
@@ -423,27 +440,31 @@ void shmem_unlock_mapping(struct address_space *mapping)
 
 /*
  * Remove range of pages and swap entries from radix tree, and free them.
+ * If !unfalloc, truncate or punch hole; if unfalloc, undo failed fallocate.
  */
-void shmem_truncate_range(struct inode *inode, loff_t lstart, loff_t lend)
+static void shmem_undo_range(struct inode *inode, loff_t lstart, loff_t lend,
+                                                                bool unfalloc)
 {
        struct address_space *mapping = inode->i_mapping;
        struct shmem_inode_info *info = SHMEM_I(inode);
        pgoff_t start = (lstart + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
-       unsigned partial = lstart & (PAGE_CACHE_SIZE - 1);
-       pgoff_t end = (lend >> PAGE_CACHE_SHIFT);
+       pgoff_t end = (lend + 1) >> PAGE_CACHE_SHIFT;
+       unsigned int partial_start = lstart & (PAGE_CACHE_SIZE - 1);
+       unsigned int partial_end = (lend + 1) & (PAGE_CACHE_SIZE - 1);
        struct pagevec pvec;
        pgoff_t indices[PAGEVEC_SIZE];
        long nr_swaps_freed = 0;
        pgoff_t index;
        int i;
 
-       BUG_ON((lend & (PAGE_CACHE_SIZE - 1)) != (PAGE_CACHE_SIZE - 1));
+       if (lend == -1)
+               end = -1;       /* unsigned, so actually very big */
 
        pagevec_init(&pvec, 0);
        index = start;
-       while (index <= end) {
+       while (index < end) {
                pvec.nr = shmem_find_get_pages_and_swap(mapping, index,
-                       min(end - index, (pgoff_t)PAGEVEC_SIZE - 1) + 1,
+                               min(end - index, (pgoff_t)PAGEVEC_SIZE),
                                                        pvec.pages, indices);
                if (!pvec.nr)
                        break;
@@ -452,10 +473,12 @@ void shmem_truncate_range(struct inode *inode, loff_t lstart, loff_t lend)
                        struct page *page = pvec.pages[i];
 
                        index = indices[i];
-                       if (index > end)
+                       if (index >= end)
                                break;
 
                        if (radix_tree_exceptional_entry(page)) {
+                               if (unfalloc)
+                                       continue;
                                nr_swaps_freed += !shmem_free_swap(mapping,
                                                                index, page);
                                continue;
@@ -463,9 +486,11 @@ void shmem_truncate_range(struct inode *inode, loff_t lstart, loff_t lend)
 
                        if (!trylock_page(page))
                                continue;
-                       if (page->mapping == mapping) {
-                               VM_BUG_ON(PageWriteback(page));
-                               truncate_inode_page(mapping, page);
+                       if (!unfalloc || !PageUptodate(page)) {
+                               if (page->mapping == mapping) {
+                                       VM_BUG_ON(PageWriteback(page));
+                                       truncate_inode_page(mapping, page);
+                               }
                        }
                        unlock_page(page);
                }
@@ -476,30 +501,47 @@ void shmem_truncate_range(struct inode *inode, loff_t lstart, loff_t lend)
                index++;
        }
 
-       if (partial) {
+       if (partial_start) {
                struct page *page = NULL;
                shmem_getpage(inode, start - 1, &page, SGP_READ, NULL);
                if (page) {
-                       zero_user_segment(page, partial, PAGE_CACHE_SIZE);
+                       unsigned int top = PAGE_CACHE_SIZE;
+                       if (start > end) {
+                               top = partial_end;
+                               partial_end = 0;
+                       }
+                       zero_user_segment(page, partial_start, top);
+                       set_page_dirty(page);
+                       unlock_page(page);
+                       page_cache_release(page);
+               }
+       }
+       if (partial_end) {
+               struct page *page = NULL;
+               shmem_getpage(inode, end, &page, SGP_READ, NULL);
+               if (page) {
+                       zero_user_segment(page, 0, partial_end);
                        set_page_dirty(page);
                        unlock_page(page);
                        page_cache_release(page);
                }
        }
+       if (start >= end)
+               return;
 
        index = start;
        for ( ; ; ) {
                cond_resched();
                pvec.nr = shmem_find_get_pages_and_swap(mapping, index,
-                       min(end - index, (pgoff_t)PAGEVEC_SIZE - 1) + 1,
+                               min(end - index, (pgoff_t)PAGEVEC_SIZE),
                                                        pvec.pages, indices);
                if (!pvec.nr) {
-                       if (index == start)
+                       if (index == start || unfalloc)
                                break;
                        index = start;
                        continue;
                }
-               if (index == start && indices[0] > end) {
+               if ((index == start || unfalloc) && indices[0] >= end) {
                        shmem_deswap_pagevec(&pvec);
                        pagevec_release(&pvec);
                        break;
@@ -509,19 +551,23 @@ void shmem_truncate_range(struct inode *inode, loff_t lstart, loff_t lend)
                        struct page *page = pvec.pages[i];
 
                        index = indices[i];
-                       if (index > end)
+                       if (index >= end)
                                break;
 
                        if (radix_tree_exceptional_entry(page)) {
+                               if (unfalloc)
+                                       continue;
                                nr_swaps_freed += !shmem_free_swap(mapping,
                                                                index, page);
                                continue;
                        }
 
                        lock_page(page);
-                       if (page->mapping == mapping) {
-                               VM_BUG_ON(PageWriteback(page));
-                               truncate_inode_page(mapping, page);
+                       if (!unfalloc || !PageUptodate(page)) {
+                               if (page->mapping == mapping) {
+                                       VM_BUG_ON(PageWriteback(page));
+                                       truncate_inode_page(mapping, page);
+                               }
                        }
                        unlock_page(page);
                }
@@ -535,7 +581,11 @@ void shmem_truncate_range(struct inode *inode, loff_t lstart, loff_t lend)
        info->swapped -= nr_swaps_freed;
        shmem_recalc_inode(inode);
        spin_unlock(&info->lock);
+}
 
+void shmem_truncate_range(struct inode *inode, loff_t lstart, loff_t lend)
+{
+       shmem_undo_range(inode, lstart, lend, false);
        inode->i_ctime = inode->i_mtime = CURRENT_TIME;
 }
 EXPORT_SYMBOL_GPL(shmem_truncate_range);
@@ -604,12 +654,13 @@ static void shmem_evict_inode(struct inode *inode)
  * If swap found in inode, free it and move page from swapcache to filecache.
  */
 static int shmem_unuse_inode(struct shmem_inode_info *info,
-                            swp_entry_t swap, struct page *page)
+                            swp_entry_t swap, struct page **pagep)
 {
        struct address_space *mapping = info->vfs_inode.i_mapping;
        void *radswap;
        pgoff_t index;
-       int error;
+       gfp_t gfp;
+       int error = 0;
 
        radswap = swp_to_radix_entry(swap);
        index = radix_tree_locate_item(&mapping->page_tree, radswap);
@@ -625,22 +676,37 @@ static int shmem_unuse_inode(struct shmem_inode_info *info,
        if (shmem_swaplist.next != &info->swaplist)
                list_move_tail(&shmem_swaplist, &info->swaplist);
 
+       gfp = mapping_gfp_mask(mapping);
+       if (shmem_should_replace_page(*pagep, gfp)) {
+               mutex_unlock(&shmem_swaplist_mutex);
+               error = shmem_replace_page(pagep, gfp, info, index);
+               mutex_lock(&shmem_swaplist_mutex);
+               /*
+                * We needed to drop mutex to make that restrictive page
+                * allocation; but the inode might already be freed by now,
+                * and we cannot refer to inode or mapping or info to check.
+                * However, we do hold page lock on the PageSwapCache page,
+                * so can check if that still has our reference remaining.
+                */
+               if (!page_swapcount(*pagep))
+                       error = -ENOENT;
+       }
+
        /*
         * We rely on shmem_swaplist_mutex, not only to protect the swaplist,
         * but also to hold up shmem_evict_inode(): so inode cannot be freed
         * beneath us (pagelock doesn't help until the page is in pagecache).
         */
-       error = shmem_add_to_page_cache(page, mapping, index,
+       if (!error)
+               error = shmem_add_to_page_cache(*pagep, mapping, index,
                                                GFP_NOWAIT, radswap);
-       /* which does mem_cgroup_uncharge_cache_page on error */
-
        if (error != -ENOMEM) {
                /*
                 * Truncation and eviction use free_swap_and_cache(), which
                 * only does trylock page: if we raced, best clean up here.
                 */
-               delete_from_swap_cache(page);
-               set_page_dirty(page);
+               delete_from_swap_cache(*pagep);
+               set_page_dirty(*pagep);
                if (!error) {
                        spin_lock(&info->lock);
                        info->swapped--;
@@ -660,7 +726,14 @@ int shmem_unuse(swp_entry_t swap, struct page *page)
        struct list_head *this, *next;
        struct shmem_inode_info *info;
        int found = 0;
-       int error;
+       int error = 0;
+
+       /*
+        * There's a faint possibility that swap page was replaced before
+        * caller locked it: it will come back later with the right page.
+        */
+       if (unlikely(!PageSwapCache(page)))
+               goto out;
 
        /*
         * Charge page using GFP_KERNEL while we can wait, before taking
@@ -676,7 +749,7 @@ int shmem_unuse(swp_entry_t swap, struct page *page)
        list_for_each_safe(this, next, &shmem_swaplist) {
                info = list_entry(this, struct shmem_inode_info, swaplist);
                if (info->swapped)
-                       found = shmem_unuse_inode(info, swap, page);
+                       found = shmem_unuse_inode(info, swap, &page);
                else
                        list_del_init(&info->swaplist);
                cond_resched();
@@ -685,8 +758,6 @@ int shmem_unuse(swp_entry_t swap, struct page *page)
        }
        mutex_unlock(&shmem_swaplist_mutex);
 
-       if (!found)
-               mem_cgroup_uncharge_cache_page(page);
        if (found < 0)
                error = found;
 out:
@@ -727,6 +798,38 @@ static int shmem_writepage(struct page *page, struct writeback_control *wbc)
                WARN_ON_ONCE(1);        /* Still happens? Tell us about it! */
                goto redirty;
        }
+
+       /*
+        * This is somewhat ridiculous, but without plumbing a SWAP_MAP_FALLOC
+        * value into swapfile.c, the only way we can correctly account for a
+        * fallocated page arriving here is now to initialize it and write it.
+        *
+        * That's okay for a page already fallocated earlier, but if we have
+        * not yet completed the fallocation, then (a) we want to keep track
+        * of this page in case we have to undo it, and (b) it may not be a
+        * good idea to continue anyway, once we're pushing into swap.  So
+        * reactivate the page, and let shmem_fallocate() quit when too many.
+        */
+       if (!PageUptodate(page)) {
+               if (inode->i_private) {
+                       struct shmem_falloc *shmem_falloc;
+                       spin_lock(&inode->i_lock);
+                       shmem_falloc = inode->i_private;
+                       if (shmem_falloc &&
+                           index >= shmem_falloc->start &&
+                           index < shmem_falloc->next)
+                               shmem_falloc->nr_unswapped++;
+                       else
+                               shmem_falloc = NULL;
+                       spin_unlock(&inode->i_lock);
+                       if (shmem_falloc)
+                               goto redirty;
+               }
+               clear_highpage(page);
+               flush_dcache_page(page);
+               SetPageUptodate(page);
+       }
+
        swap = get_swap_page();
        if (!swap.val)
                goto redirty;
@@ -855,6 +958,84 @@ static inline struct mempolicy *shmem_get_sbmpol(struct shmem_sb_info *sbinfo)
 }
 #endif
 
+/*
+ * When a page is moved from swapcache to shmem filecache (either by the
+ * usual swapin of shmem_getpage_gfp(), or by the less common swapoff of
+ * shmem_unuse_inode()), it may have been read in earlier from swap, in
+ * ignorance of the mapping it belongs to.  If that mapping has special
+ * constraints (like the gma500 GEM driver, which requires RAM below 4GB),
+ * we may need to copy to a suitable page before moving to filecache.
+ *
+ * In a future release, this may well be extended to respect cpuset and
+ * NUMA mempolicy, and applied also to anonymous pages in do_swap_page();
+ * but for now it is a simple matter of zone.
+ */
+static bool shmem_should_replace_page(struct page *page, gfp_t gfp)
+{
+       return page_zonenum(page) > gfp_zone(gfp);
+}
+
+static int shmem_replace_page(struct page **pagep, gfp_t gfp,
+                               struct shmem_inode_info *info, pgoff_t index)
+{
+       struct page *oldpage, *newpage;
+       struct address_space *swap_mapping;
+       pgoff_t swap_index;
+       int error;
+
+       oldpage = *pagep;
+       swap_index = page_private(oldpage);
+       swap_mapping = page_mapping(oldpage);
+
+       /*
+        * We have arrived here because our zones are constrained, so don't
+        * limit chance of success by further cpuset and node constraints.
+        */
+       gfp &= ~GFP_CONSTRAINT_MASK;
+       newpage = shmem_alloc_page(gfp, info, index);
+       if (!newpage)
+               return -ENOMEM;
+       VM_BUG_ON(shmem_should_replace_page(newpage, gfp));
+
+       *pagep = newpage;
+       page_cache_get(newpage);
+       copy_highpage(newpage, oldpage);
+
+       VM_BUG_ON(!PageLocked(oldpage));
+       __set_page_locked(newpage);
+       VM_BUG_ON(!PageUptodate(oldpage));
+       SetPageUptodate(newpage);
+       VM_BUG_ON(!PageSwapBacked(oldpage));
+       SetPageSwapBacked(newpage);
+       VM_BUG_ON(!swap_index);
+       set_page_private(newpage, swap_index);
+       VM_BUG_ON(!PageSwapCache(oldpage));
+       SetPageSwapCache(newpage);
+
+       /*
+        * Our caller will very soon move newpage out of swapcache, but it's
+        * a nice clean interface for us to replace oldpage by newpage there.
+        */
+       spin_lock_irq(&swap_mapping->tree_lock);
+       error = shmem_radix_tree_replace(swap_mapping, swap_index, oldpage,
+                                                                  newpage);
+       __inc_zone_page_state(newpage, NR_FILE_PAGES);
+       __dec_zone_page_state(oldpage, NR_FILE_PAGES);
+       spin_unlock_irq(&swap_mapping->tree_lock);
+       BUG_ON(error);
+
+       mem_cgroup_replace_page_cache(oldpage, newpage);
+       lru_cache_add_anon(newpage);
+
+       ClearPageSwapCache(oldpage);
+       set_page_private(oldpage, 0);
+
+       unlock_page(oldpage);
+       page_cache_release(oldpage);
+       page_cache_release(oldpage);
+       return 0;
+}
+
 /*
  * shmem_getpage_gfp - find page in cache, or get from swap, or allocate
  *
@@ -872,6 +1053,7 @@ static int shmem_getpage_gfp(struct inode *inode, pgoff_t index,
        swp_entry_t swap;
        int error;
        int once = 0;
+       int alloced = 0;
 
        if (index > (MAX_LFS_FILESIZE >> PAGE_CACHE_SHIFT))
                return -EFBIG;
@@ -883,19 +1065,21 @@ repeat:
                page = NULL;
        }
 
-       if (sgp != SGP_WRITE &&
+       if (sgp != SGP_WRITE && sgp != SGP_FALLOC &&
            ((loff_t)index << PAGE_CACHE_SHIFT) >= i_size_read(inode)) {
                error = -EINVAL;
                goto failed;
        }
 
+       /* fallocated page? */
+       if (page && !PageUptodate(page)) {
+               if (sgp != SGP_READ)
+                       goto clear;
+               unlock_page(page);
+               page_cache_release(page);
+               page = NULL;
+       }
        if (page || (sgp == SGP_READ && !swap.val)) {
-               /*
-                * Once we can get the page lock, it must be uptodate:
-                * if there were an error in reading back from swap,
-                * the page would not be inserted into the filecache.
-                */
-               BUG_ON(page && !PageUptodate(page));
                *pagep = page;
                return 0;
        }
@@ -923,19 +1107,20 @@ repeat:
 
                /* We have to do this with page locked to prevent races */
                lock_page(page);
+               if (!PageSwapCache(page) || page->mapping) {
+                       error = -EEXIST;        /* try again */
+                       goto failed;
+               }
                if (!PageUptodate(page)) {
                        error = -EIO;
                        goto failed;
                }
                wait_on_page_writeback(page);
 
-               /* Someone may have already done it for us */
-               if (page->mapping) {
-                       if (page->mapping == mapping &&
-                           page->index == index)
-                               goto done;
-                       error = -EEXIST;
-                       goto failed;
+               if (shmem_should_replace_page(page, gfp)) {
+                       error = shmem_replace_page(&page, gfp, info, index);
+                       if (error)
+                               goto failed;
                }
 
                error = mem_cgroup_cache_charge(page, current->mm,
@@ -991,19 +1176,36 @@ repeat:
                inode->i_blocks += BLOCKS_PER_PAGE;
                shmem_recalc_inode(inode);
                spin_unlock(&info->lock);
+               alloced = true;
 
-               clear_highpage(page);
-               flush_dcache_page(page);
-               SetPageUptodate(page);
+               /*
+                * Let SGP_FALLOC use the SGP_WRITE optimization on a new page.
+                */
+               if (sgp == SGP_FALLOC)
+                       sgp = SGP_WRITE;
+clear:
+               /*
+                * Let SGP_WRITE caller clear ends if write does not fill page;
+                * but SGP_FALLOC on a page fallocated earlier must initialize
+                * it now, lest undo on failure cancel our earlier guarantee.
+                */
+               if (sgp != SGP_WRITE) {
+                       clear_highpage(page);
+                       flush_dcache_page(page);
+                       SetPageUptodate(page);
+               }
                if (sgp == SGP_DIRTY)
                        set_page_dirty(page);
        }
-done:
+
        /* Perhaps the file has been truncated since we checked */
-       if (sgp != SGP_WRITE &&
+       if (sgp != SGP_WRITE && sgp != SGP_FALLOC &&
            ((loff_t)index << PAGE_CACHE_SHIFT) >= i_size_read(inode)) {
                error = -EINVAL;
-               goto trunc;
+               if (alloced)
+                       goto trunc;
+               else
+                       goto failed;
        }
        *pagep = page;
        return 0;
@@ -1012,6 +1214,7 @@ done:
         * Error recovery.
         */
 trunc:
+       info = SHMEM_I(inode);
        ClearPageDirty(page);
        delete_from_page_cache(page);
        spin_lock(&info->lock);
@@ -1019,6 +1222,7 @@ trunc:
        inode->i_blocks -= BLOCKS_PER_PAGE;
        spin_unlock(&info->lock);
 decused:
+       sbinfo = SHMEM_SB(inode->i_sb);
        if (sbinfo->max_blocks)
                percpu_counter_add(&sbinfo->used_blocks, -1);
 unacct:
@@ -1204,6 +1408,14 @@ shmem_write_end(struct file *file, struct address_space *mapping,
        if (pos + copied > inode->i_size)
                i_size_write(inode, pos + copied);
 
+       if (!PageUptodate(page)) {
+               if (copied < PAGE_CACHE_SIZE) {
+                       unsigned from = pos & (PAGE_CACHE_SIZE - 1);
+                       zero_user_segments(page, 0, from,
+                                       from + copied, PAGE_CACHE_SIZE);
+               }
+               SetPageUptodate(page);
+       }
        set_page_dirty(page);
        unlock_page(page);
        page_cache_release(page);
@@ -1462,6 +1674,199 @@ static ssize_t shmem_file_splice_read(struct file *in, loff_t *ppos,
        return error;
 }
 
+/*
+ * llseek SEEK_DATA or SEEK_HOLE through the radix_tree.
+ */
+static pgoff_t shmem_seek_hole_data(struct address_space *mapping,
+                                   pgoff_t index, pgoff_t end, int origin)
+{
+       struct page *page;
+       struct pagevec pvec;
+       pgoff_t indices[PAGEVEC_SIZE];
+       bool done = false;
+       int i;
+
+       pagevec_init(&pvec, 0);
+       pvec.nr = 1;            /* start small: we may be there already */
+       while (!done) {
+               pvec.nr = shmem_find_get_pages_and_swap(mapping, index,
+                                       pvec.nr, pvec.pages, indices);
+               if (!pvec.nr) {
+                       if (origin == SEEK_DATA)
+                               index = end;
+                       break;
+               }
+               for (i = 0; i < pvec.nr; i++, index++) {
+                       if (index < indices[i]) {
+                               if (origin == SEEK_HOLE) {
+                                       done = true;
+                                       break;
+                               }
+                               index = indices[i];
+                       }
+                       page = pvec.pages[i];
+                       if (page && !radix_tree_exceptional_entry(page)) {
+                               if (!PageUptodate(page))
+                                       page = NULL;
+                       }
+                       if (index >= end ||
+                           (page && origin == SEEK_DATA) ||
+                           (!page && origin == SEEK_HOLE)) {
+                               done = true;
+                               break;
+                       }
+               }
+               shmem_deswap_pagevec(&pvec);
+               pagevec_release(&pvec);
+               pvec.nr = PAGEVEC_SIZE;
+               cond_resched();
+       }
+       return index;
+}
+
+static loff_t shmem_file_llseek(struct file *file, loff_t offset, int origin)
+{
+       struct address_space *mapping;
+       struct inode *inode;
+       pgoff_t start, end;
+       loff_t new_offset;
+
+       if (origin != SEEK_DATA && origin != SEEK_HOLE)
+               return generic_file_llseek_size(file, offset, origin,
+                                                       MAX_LFS_FILESIZE);
+       mapping = file->f_mapping;
+       inode = mapping->host;
+       mutex_lock(&inode->i_mutex);
+       /* We're holding i_mutex so we can access i_size directly */
+
+       if (offset < 0)
+               offset = -EINVAL;
+       else if (offset >= inode->i_size)
+               offset = -ENXIO;
+       else {
+               start = offset >> PAGE_CACHE_SHIFT;
+               end = (inode->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
+               new_offset = shmem_seek_hole_data(mapping, start, end, origin);
+               new_offset <<= PAGE_CACHE_SHIFT;
+               if (new_offset > offset) {
+                       if (new_offset < inode->i_size)
+                               offset = new_offset;
+                       else if (origin == SEEK_DATA)
+                               offset = -ENXIO;
+                       else
+                               offset = inode->i_size;
+               }
+       }
+
+       if (offset >= 0 && offset != file->f_pos) {
+               file->f_pos = offset;
+               file->f_version = 0;
+       }
+       mutex_unlock(&inode->i_mutex);
+       return offset;
+}
+
+static long shmem_fallocate(struct file *file, int mode, loff_t offset,
+                                                        loff_t len)
+{
+       struct inode *inode = file->f_path.dentry->d_inode;
+       struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb);
+       struct shmem_falloc shmem_falloc;
+       pgoff_t start, index, end;
+       int error;
+
+       mutex_lock(&inode->i_mutex);
+
+       if (mode & FALLOC_FL_PUNCH_HOLE) {
+               struct address_space *mapping = file->f_mapping;
+               loff_t unmap_start = round_up(offset, PAGE_SIZE);
+               loff_t unmap_end = round_down(offset + len, PAGE_SIZE) - 1;
+
+               if ((u64)unmap_end > (u64)unmap_start)
+                       unmap_mapping_range(mapping, unmap_start,
+                                           1 + unmap_end - unmap_start, 0);
+               shmem_truncate_range(inode, offset, offset + len - 1);
+               /* No need to unmap again: hole-punching leaves COWed pages */
+               error = 0;
+               goto out;
+       }
+
+       /* We need to check rlimit even when FALLOC_FL_KEEP_SIZE */
+       error = inode_newsize_ok(inode, offset + len);
+       if (error)
+               goto out;
+
+       start = offset >> PAGE_CACHE_SHIFT;
+       end = (offset + len + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
+       /* Try to avoid a swapstorm if len is impossible to satisfy */
+       if (sbinfo->max_blocks && end - start > sbinfo->max_blocks) {
+               error = -ENOSPC;
+               goto out;
+       }
+
+       shmem_falloc.start = start;
+       shmem_falloc.next  = start;
+       shmem_falloc.nr_falloced = 0;
+       shmem_falloc.nr_unswapped = 0;
+       spin_lock(&inode->i_lock);
+       inode->i_private = &shmem_falloc;
+       spin_unlock(&inode->i_lock);
+
+       for (index = start; index < end; index++) {
+               struct page *page;
+
+               /*
+                * Good, the fallocate(2) manpage permits EINTR: we may have
+                * been interrupted because we are using up too much memory.
+                */
+               if (signal_pending(current))
+                       error = -EINTR;
+               else if (shmem_falloc.nr_unswapped > shmem_falloc.nr_falloced)
+                       error = -ENOMEM;
+               else
+                       error = shmem_getpage(inode, index, &page, SGP_FALLOC,
+                                                                       NULL);
+               if (error) {
+                       /* Remove the !PageUptodate pages we added */
+                       shmem_undo_range(inode,
+                               (loff_t)start << PAGE_CACHE_SHIFT,
+                               (loff_t)index << PAGE_CACHE_SHIFT, true);
+                       goto undone;
+               }
+
+               /*
+                * Inform shmem_writepage() how far we have reached.
+                * No need for lock or barrier: we have the page lock.
+                */
+               shmem_falloc.next++;
+               if (!PageUptodate(page))
+                       shmem_falloc.nr_falloced++;
+
+               /*
+                * If !PageUptodate, leave it that way so that freeable pages
+                * can be recognized if we need to rollback on error later.
+                * But set_page_dirty so that memory pressure will swap rather
+                * than free the pages we are allocating (and SGP_CACHE pages
+                * might still be clean: we now need to mark those dirty too).
+                */
+               set_page_dirty(page);
+               unlock_page(page);
+               page_cache_release(page);
+               cond_resched();
+       }
+
+       if (!(mode & FALLOC_FL_KEEP_SIZE) && offset + len > inode->i_size)
+               i_size_write(inode, offset + len);
+       inode->i_ctime = CURRENT_TIME;
+undone:
+       spin_lock(&inode->i_lock);
+       inode->i_private = NULL;
+       spin_unlock(&inode->i_lock);
+out:
+       mutex_unlock(&inode->i_mutex);
+       return error;
+}
+
 static int shmem_statfs(struct dentry *dentry, struct kstatfs *buf)
 {
        struct shmem_sb_info *sbinfo = SHMEM_SB(dentry->d_sb);
@@ -1665,6 +2070,7 @@ static int shmem_symlink(struct inode *dir, struct dentry *dentry, const char *s
                kaddr = kmap_atomic(page);
                memcpy(kaddr, symname, len);
                kunmap_atomic(kaddr);
+               SetPageUptodate(page);
                set_page_dirty(page);
                unlock_page(page);
                page_cache_release(page);
@@ -2033,11 +2439,9 @@ static struct dentry *shmem_fh_to_dentry(struct super_block *sb,
        return dentry;
 }
 
-static int shmem_encode_fh(struct dentry *dentry, __u32 *fh, int *len,
-                               int connectable)
+static int shmem_encode_fh(struct inode *inode, __u32 *fh, int *len,
+                               struct inode *parent)
 {
-       struct inode *inode = dentry->d_inode;
-
        if (*len < 3) {
                *len = 3;
                return 255;
@@ -2270,6 +2674,7 @@ int shmem_fill_super(struct super_block *sb, void *data, int silent)
                }
        }
        sb->s_export_op = &shmem_export_ops;
+       sb->s_flags |= MS_NOSEC;
 #else
        sb->s_flags |= MS_NOUSER;
 #endif
@@ -2364,7 +2769,7 @@ static const struct address_space_operations shmem_aops = {
 static const struct file_operations shmem_file_operations = {
        .mmap           = shmem_mmap,
 #ifdef CONFIG_TMPFS
-       .llseek         = generic_file_llseek,
+       .llseek         = shmem_file_llseek,
        .read           = do_sync_read,
        .write          = do_sync_write,
        .aio_read       = shmem_file_aio_read,
@@ -2372,12 +2777,12 @@ static const struct file_operations shmem_file_operations = {
        .fsync          = noop_fsync,
        .splice_read    = shmem_file_splice_read,
        .splice_write   = generic_file_splice_write,
+       .fallocate      = shmem_fallocate,
 #endif
 };
 
 static const struct inode_operations shmem_inode_operations = {
        .setattr        = shmem_setattr,
-       .truncate_range = shmem_truncate_range,
 #ifdef CONFIG_TMPFS_XATTR
        .setxattr       = shmem_setxattr,
        .getxattr       = shmem_getxattr,
index a8bc7d364deb0a764cbd28956f1853fbb3ce421c..6a4bf9160e855ae1e2d61fefb4922918f710bb24 100644 (file)
@@ -273,10 +273,10 @@ static unsigned long *__kmalloc_section_usemap(void)
 #ifdef CONFIG_MEMORY_HOTREMOVE
 static unsigned long * __init
 sparse_early_usemaps_alloc_pgdat_section(struct pglist_data *pgdat,
-                                        unsigned long count)
+                                        unsigned long size)
 {
-       unsigned long section_nr;
-
+       pg_data_t *host_pgdat;
+       unsigned long goal;
        /*
         * A page may contain usemaps for other sections preventing the
         * page being freed and making a section unremovable while
@@ -287,8 +287,10 @@ sparse_early_usemaps_alloc_pgdat_section(struct pglist_data *pgdat,
         * from the same section as the pgdat where possible to avoid
         * this problem.
         */
-       section_nr = pfn_to_section_nr(__pa(pgdat) >> PAGE_SHIFT);
-       return alloc_bootmem_section(usemap_size() * count, section_nr);
+       goal = __pa(pgdat) & PAGE_SECTION_MASK;
+       host_pgdat = NODE_DATA(early_pfn_to_nid(goal >> PAGE_SHIFT));
+       return __alloc_bootmem_node_nopanic(host_pgdat, size,
+                                           SMP_CACHE_BYTES, goal);
 }
 
 static void __init check_usemap_section_nr(int nid, unsigned long *usemap)
@@ -332,9 +334,9 @@ static void __init check_usemap_section_nr(int nid, unsigned long *usemap)
 #else
 static unsigned long * __init
 sparse_early_usemaps_alloc_pgdat_section(struct pglist_data *pgdat,
-                                        unsigned long count)
+                                        unsigned long size)
 {
-       return NULL;
+       return alloc_bootmem_node_nopanic(pgdat, size);
 }
 
 static void __init check_usemap_section_nr(int nid, unsigned long *usemap)
@@ -352,13 +354,10 @@ static void __init sparse_early_usemaps_alloc_node(unsigned long**usemap_map,
        int size = usemap_size();
 
        usemap = sparse_early_usemaps_alloc_pgdat_section(NODE_DATA(nodeid),
-                                                                usemap_count);
+                                                         size * usemap_count);
        if (!usemap) {
-               usemap = alloc_bootmem_node(NODE_DATA(nodeid), size * usemap_count);
-               if (!usemap) {
-                       printk(KERN_WARNING "%s: allocation failed\n", __func__);
-                       return;
-               }
+               printk(KERN_WARNING "%s: allocation failed\n", __func__);
+               return;
        }
 
        for (pnum = pnum_begin; pnum < pnum_end; pnum++) {
index 5c13f13389721fe60756ffb4dabe66d0c1e86e47..4e7e2ec67078f783750eb43e8dc45db5600b1b94 100644 (file)
--- a/mm/swap.c
+++ b/mm/swap.c
@@ -47,13 +47,15 @@ static DEFINE_PER_CPU(struct pagevec, lru_deactivate_pvecs);
 static void __page_cache_release(struct page *page)
 {
        if (PageLRU(page)) {
-               unsigned long flags;
                struct zone *zone = page_zone(page);
+               struct lruvec *lruvec;
+               unsigned long flags;
 
                spin_lock_irqsave(&zone->lru_lock, flags);
+               lruvec = mem_cgroup_page_lruvec(page, zone);
                VM_BUG_ON(!PageLRU(page));
                __ClearPageLRU(page);
-               del_page_from_lru_list(zone, page, page_off_lru(page));
+               del_page_from_lru_list(page, lruvec, page_off_lru(page));
                spin_unlock_irqrestore(&zone->lru_lock, flags);
        }
 }
@@ -82,6 +84,25 @@ static void put_compound_page(struct page *page)
                if (likely(page != page_head &&
                           get_page_unless_zero(page_head))) {
                        unsigned long flags;
+
+                       /*
+                        * THP can not break up slab pages so avoid taking
+                        * compound_lock().  Slab performs non-atomic bit ops
+                        * on page->flags for better performance.  In particular
+                        * slab_unlock() in slub used to be a hot path.  It is
+                        * still hot on arches that do not support
+                        * this_cpu_cmpxchg_double().
+                        */
+                       if (PageSlab(page_head)) {
+                               if (PageTail(page)) {
+                                       if (put_page_testzero(page_head))
+                                               VM_BUG_ON(1);
+
+                                       atomic_dec(&page->_mapcount);
+                                       goto skip_lock_tail;
+                               } else
+                                       goto skip_lock;
+                       }
                        /*
                         * page_head wasn't a dangling pointer but it
                         * may not be a head page anymore by the time
@@ -92,10 +113,10 @@ static void put_compound_page(struct page *page)
                        if (unlikely(!PageTail(page))) {
                                /* __split_huge_page_refcount run before us */
                                compound_unlock_irqrestore(page_head, flags);
-                               VM_BUG_ON(PageHead(page_head));
+skip_lock:
                                if (put_page_testzero(page_head))
                                        __put_single_page(page_head);
-                       out_put_single:
+out_put_single:
                                if (put_page_testzero(page))
                                        __put_single_page(page);
                                return;
@@ -115,6 +136,8 @@ static void put_compound_page(struct page *page)
                        VM_BUG_ON(atomic_read(&page_head->_count) <= 0);
                        VM_BUG_ON(atomic_read(&page->_count) != 0);
                        compound_unlock_irqrestore(page_head, flags);
+
+skip_lock_tail:
                        if (put_page_testzero(page_head)) {
                                if (PageHead(page_head))
                                        __put_compound_page(page_head);
@@ -162,6 +185,18 @@ bool __get_page_tail(struct page *page)
        struct page *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;
+                       }
+               }
+
                /*
                 * page_head wasn't a dangling pointer but it
                 * may not be a head page anymore by the time
@@ -202,11 +237,12 @@ void put_pages_list(struct list_head *pages)
 EXPORT_SYMBOL(put_pages_list);
 
 static void pagevec_lru_move_fn(struct pagevec *pvec,
-                               void (*move_fn)(struct page *page, void *arg),
-                               void *arg)
+       void (*move_fn)(struct page *page, struct lruvec *lruvec, void *arg),
+       void *arg)
 {
        int i;
        struct zone *zone = NULL;
+       struct lruvec *lruvec;
        unsigned long flags = 0;
 
        for (i = 0; i < pagevec_count(pvec); i++) {
@@ -220,7 +256,8 @@ static void pagevec_lru_move_fn(struct pagevec *pvec,
                        spin_lock_irqsave(&zone->lru_lock, flags);
                }
 
-               (*move_fn)(page, arg);
+               lruvec = mem_cgroup_page_lruvec(page, zone);
+               (*move_fn)(page, lruvec, arg);
        }
        if (zone)
                spin_unlock_irqrestore(&zone->lru_lock, flags);
@@ -228,16 +265,13 @@ static void pagevec_lru_move_fn(struct pagevec *pvec,
        pagevec_reinit(pvec);
 }
 
-static void pagevec_move_tail_fn(struct page *page, void *arg)
+static void pagevec_move_tail_fn(struct page *page, struct lruvec *lruvec,
+                                void *arg)
 {
        int *pgmoved = arg;
 
        if (PageLRU(page) && !PageActive(page) && !PageUnevictable(page)) {
                enum lru_list lru = page_lru_base_type(page);
-               struct lruvec *lruvec;
-
-               lruvec = mem_cgroup_lru_move_lists(page_zone(page),
-                                                  page, lru, lru);
                list_move_tail(&page->lru, &lruvec->lists[lru]);
                (*pgmoved)++;
        }
@@ -276,41 +310,30 @@ void rotate_reclaimable_page(struct page *page)
        }
 }
 
-static void update_page_reclaim_stat(struct zone *zone, struct page *page,
+static void update_page_reclaim_stat(struct lruvec *lruvec,
                                     int file, int rotated)
 {
-       struct zone_reclaim_stat *reclaim_stat = &zone->reclaim_stat;
-       struct zone_reclaim_stat *memcg_reclaim_stat;
-
-       memcg_reclaim_stat = mem_cgroup_get_reclaim_stat_from_page(page);
+       struct zone_reclaim_stat *reclaim_stat = &lruvec->reclaim_stat;
 
        reclaim_stat->recent_scanned[file]++;
        if (rotated)
                reclaim_stat->recent_rotated[file]++;
-
-       if (!memcg_reclaim_stat)
-               return;
-
-       memcg_reclaim_stat->recent_scanned[file]++;
-       if (rotated)
-               memcg_reclaim_stat->recent_rotated[file]++;
 }
 
-static void __activate_page(struct page *page, void *arg)
+static void __activate_page(struct page *page, struct lruvec *lruvec,
+                           void *arg)
 {
-       struct zone *zone = page_zone(page);
-
        if (PageLRU(page) && !PageActive(page) && !PageUnevictable(page)) {
                int file = page_is_file_cache(page);
                int lru = page_lru_base_type(page);
-               del_page_from_lru_list(zone, page, lru);
 
+               del_page_from_lru_list(page, lruvec, lru);
                SetPageActive(page);
                lru += LRU_ACTIVE;
-               add_page_to_lru_list(zone, page, lru);
-               __count_vm_event(PGACTIVATE);
+               add_page_to_lru_list(page, lruvec, lru);
 
-               update_page_reclaim_stat(zone, page, file, 1);
+               __count_vm_event(PGACTIVATE);
+               update_page_reclaim_stat(lruvec, file, 1);
        }
 }
 
@@ -347,7 +370,7 @@ void activate_page(struct page *page)
        struct zone *zone = page_zone(page);
 
        spin_lock_irq(&zone->lru_lock);
-       __activate_page(page, NULL);
+       __activate_page(page, mem_cgroup_page_lruvec(page, zone), NULL);
        spin_unlock_irq(&zone->lru_lock);
 }
 #endif
@@ -414,11 +437,13 @@ void lru_cache_add_lru(struct page *page, enum lru_list lru)
 void add_page_to_unevictable_list(struct page *page)
 {
        struct zone *zone = page_zone(page);
+       struct lruvec *lruvec;
 
        spin_lock_irq(&zone->lru_lock);
+       lruvec = mem_cgroup_page_lruvec(page, zone);
        SetPageUnevictable(page);
        SetPageLRU(page);
-       add_page_to_lru_list(zone, page, LRU_UNEVICTABLE);
+       add_page_to_lru_list(page, lruvec, LRU_UNEVICTABLE);
        spin_unlock_irq(&zone->lru_lock);
 }
 
@@ -443,11 +468,11 @@ void add_page_to_unevictable_list(struct page *page)
  * be write it out by flusher threads as this is much more effective
  * than the single-page writeout from reclaim.
  */
-static void lru_deactivate_fn(struct page *page, void *arg)
+static void lru_deactivate_fn(struct page *page, struct lruvec *lruvec,
+                             void *arg)
 {
        int lru, file;
        bool active;
-       struct zone *zone = page_zone(page);
 
        if (!PageLRU(page))
                return;
@@ -460,13 +485,13 @@ static void lru_deactivate_fn(struct page *page, void *arg)
                return;
 
        active = PageActive(page);
-
        file = page_is_file_cache(page);
        lru = page_lru_base_type(page);
-       del_page_from_lru_list(zone, page, lru + active);
+
+       del_page_from_lru_list(page, lruvec, lru + active);
        ClearPageActive(page);
        ClearPageReferenced(page);
-       add_page_to_lru_list(zone, page, lru);
+       add_page_to_lru_list(page, lruvec, lru);
 
        if (PageWriteback(page) || PageDirty(page)) {
                /*
@@ -476,19 +501,17 @@ static void lru_deactivate_fn(struct page *page, void *arg)
                 */
                SetPageReclaim(page);
        } else {
-               struct lruvec *lruvec;
                /*
                 * The page's writeback ends up during pagevec
                 * We moves tha page into tail of inactive.
                 */
-               lruvec = mem_cgroup_lru_move_lists(zone, page, lru, lru);
                list_move_tail(&page->lru, &lruvec->lists[lru]);
                __count_vm_event(PGROTATED);
        }
 
        if (active)
                __count_vm_event(PGDEACTIVATE);
-       update_page_reclaim_stat(zone, page, file, 0);
+       update_page_reclaim_stat(lruvec, file, 0);
 }
 
 /*
@@ -588,6 +611,7 @@ void release_pages(struct page **pages, int nr, int cold)
        int i;
        LIST_HEAD(pages_to_free);
        struct zone *zone = NULL;
+       struct lruvec *lruvec;
        unsigned long uninitialized_var(flags);
 
        for (i = 0; i < nr; i++) {
@@ -615,9 +639,11 @@ void release_pages(struct page **pages, int nr, int cold)
                                zone = pagezone;
                                spin_lock_irqsave(&zone->lru_lock, flags);
                        }
+
+                       lruvec = mem_cgroup_page_lruvec(page, zone);
                        VM_BUG_ON(!PageLRU(page));
                        __ClearPageLRU(page);
-                       del_page_from_lru_list(zone, page, page_off_lru(page));
+                       del_page_from_lru_list(page, lruvec, page_off_lru(page));
                }
 
                list_add(&page->lru, &pages_to_free);
@@ -649,8 +675,8 @@ EXPORT_SYMBOL(__pagevec_release);
 
 #ifdef CONFIG_TRANSPARENT_HUGEPAGE
 /* used by __split_huge_page_refcount() */
-void lru_add_page_tail(struct zone* zone,
-                      struct page *page, struct page *page_tail)
+void lru_add_page_tail(struct page *page, struct page *page_tail,
+                      struct lruvec *lruvec)
 {
        int uninitialized_var(active);
        enum lru_list lru;
@@ -659,7 +685,8 @@ void lru_add_page_tail(struct zone* zone,
        VM_BUG_ON(!PageHead(page));
        VM_BUG_ON(PageCompound(page_tail));
        VM_BUG_ON(PageLRU(page_tail));
-       VM_BUG_ON(NR_CPUS != 1 && !spin_is_locked(&zone->lru_lock));
+       VM_BUG_ON(NR_CPUS != 1 &&
+                 !spin_is_locked(&lruvec_zone(lruvec)->lru_lock));
 
        SetPageLRU(page_tail);
 
@@ -688,20 +715,20 @@ void lru_add_page_tail(struct zone* zone,
                 * Use the standard add function to put page_tail on the list,
                 * but then correct its position so they all end up in order.
                 */
-               add_page_to_lru_list(zone, page_tail, lru);
+               add_page_to_lru_list(page_tail, lruvec, lru);
                list_head = page_tail->lru.prev;
                list_move_tail(&page_tail->lru, list_head);
        }
 
        if (!PageUnevictable(page))
-               update_page_reclaim_stat(zone, page_tail, file, active);
+               update_page_reclaim_stat(lruvec, file, active);
 }
 #endif /* CONFIG_TRANSPARENT_HUGEPAGE */
 
-static void __pagevec_lru_add_fn(struct page *page, void *arg)
+static void __pagevec_lru_add_fn(struct page *page, struct lruvec *lruvec,
+                                void *arg)
 {
        enum lru_list lru = (enum lru_list)arg;
-       struct zone *zone = page_zone(page);
        int file = is_file_lru(lru);
        int active = is_active_lru(lru);
 
@@ -712,8 +739,8 @@ static void __pagevec_lru_add_fn(struct page *page, void *arg)
        SetPageLRU(page);
        if (active)
                SetPageActive(page);
-       add_page_to_lru_list(zone, page, lru);
-       update_page_reclaim_stat(zone, page, file, active);
+       add_page_to_lru_list(page, lruvec, lru);
+       update_page_reclaim_stat(lruvec, file, active);
 }
 
 /*
index fafc26d1b1dc885d2541eda3bdc5705a4fe56012..457b10baef59414f591fb0bfab2b54619c5209b0 100644 (file)
@@ -601,7 +601,7 @@ void swapcache_free(swp_entry_t entry, struct page *page)
  * This does not give an exact answer when swap count is continued,
  * but does include the high COUNT_CONTINUED flag to allow for that.
  */
-static inline int page_swapcount(struct page *page)
+int page_swapcount(struct page *page)
 {
        int count = 0;
        struct swap_info_struct *p;
@@ -717,37 +717,6 @@ int free_swap_and_cache(swp_entry_t entry)
        return p != NULL;
 }
 
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR
-/**
- * mem_cgroup_count_swap_user - count the user of a swap entry
- * @ent: the swap entry to be checked
- * @pagep: the pointer for the swap cache page of the entry to be stored
- *
- * Returns the number of the user of the swap entry. The number is valid only
- * for swaps of anonymous pages.
- * If the entry is found on swap cache, the page is stored to pagep with
- * refcount of it being incremented.
- */
-int mem_cgroup_count_swap_user(swp_entry_t ent, struct page **pagep)
-{
-       struct page *page;
-       struct swap_info_struct *p;
-       int count = 0;
-
-       page = find_get_page(&swapper_space, ent.val);
-       if (page)
-               count += page_mapcount(page);
-       p = swap_info_get(ent);
-       if (p) {
-               count += swap_count(p->swap_map[swp_offset(ent)]);
-               spin_unlock(&swap_lock);
-       }
-
-       *pagep = page;
-       return count;
-}
-#endif
-
 #ifdef CONFIG_HIBERNATION
 /*
  * Find the swap type that corresponds to given device (if any).
diff --git a/mm/thrash.c b/mm/thrash.c
deleted file mode 100644 (file)
index 57ad495..0000000
+++ /dev/null
@@ -1,155 +0,0 @@
-/*
- * mm/thrash.c
- *
- * Copyright (C) 2004, Red Hat, Inc.
- * Copyright (C) 2004, Rik van Riel <riel@redhat.com>
- * Released under the GPL, see the file COPYING for details.
- *
- * Simple token based thrashing protection, using the algorithm
- * described in: http://www.cse.ohio-state.edu/hpcs/WWW/HTML/publications/abs05-1.html
- *
- * Sep 2006, Ashwin Chaugule <ashwin.chaugule@celunite.com>
- * Improved algorithm to pass token:
- * Each task has a priority which is incremented if it contended
- * for the token in an interval less than its previous attempt.
- * If the token is acquired, that task's priority is boosted to prevent
- * the token from bouncing around too often and to let the task make
- * some progress in its execution.
- */
-
-#include <linux/jiffies.h>
-#include <linux/mm.h>
-#include <linux/sched.h>
-#include <linux/swap.h>
-#include <linux/memcontrol.h>
-
-#include <trace/events/vmscan.h>
-
-#define TOKEN_AGING_INTERVAL   (0xFF)
-
-static DEFINE_SPINLOCK(swap_token_lock);
-struct mm_struct *swap_token_mm;
-static struct mem_cgroup *swap_token_memcg;
-
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR
-static struct mem_cgroup *swap_token_memcg_from_mm(struct mm_struct *mm)
-{
-       struct mem_cgroup *memcg;
-
-       memcg = try_get_mem_cgroup_from_mm(mm);
-       if (memcg)
-               css_put(mem_cgroup_css(memcg));
-
-       return memcg;
-}
-#else
-static struct mem_cgroup *swap_token_memcg_from_mm(struct mm_struct *mm)
-{
-       return NULL;
-}
-#endif
-
-void grab_swap_token(struct mm_struct *mm)
-{
-       int current_interval;
-       unsigned int old_prio = mm->token_priority;
-       static unsigned int global_faults;
-       static unsigned int last_aging;
-
-       global_faults++;
-
-       current_interval = global_faults - mm->faultstamp;
-
-       if (!spin_trylock(&swap_token_lock))
-               return;
-
-       /* First come first served */
-       if (!swap_token_mm)
-               goto replace_token;
-
-       /*
-        * Usually, we don't need priority aging because long interval faults
-        * makes priority decrease quickly. But there is one exception. If the
-        * token owner task is sleeping, it never make long interval faults.
-        * Thus, we need a priority aging mechanism instead. The requirements
-        * of priority aging are
-        *  1) An aging interval is reasonable enough long. Too short aging
-        *     interval makes quick swap token lost and decrease performance.
-        *  2) The swap token owner task have to get priority aging even if
-        *     it's under sleep.
-        */
-       if ((global_faults - last_aging) > TOKEN_AGING_INTERVAL) {
-               swap_token_mm->token_priority /= 2;
-               last_aging = global_faults;
-       }
-
-       if (mm == swap_token_mm) {
-               mm->token_priority += 2;
-               goto update_priority;
-       }
-
-       if (current_interval < mm->last_interval)
-               mm->token_priority++;
-       else {
-               if (likely(mm->token_priority > 0))
-                       mm->token_priority--;
-       }
-
-       /* Check if we deserve the token */
-       if (mm->token_priority > swap_token_mm->token_priority)
-               goto replace_token;
-
-update_priority:
-       trace_update_swap_token_priority(mm, old_prio, swap_token_mm);
-
-out:
-       mm->faultstamp = global_faults;
-       mm->last_interval = current_interval;
-       spin_unlock(&swap_token_lock);
-       return;
-
-replace_token:
-       mm->token_priority += 2;
-       trace_replace_swap_token(swap_token_mm, mm);
-       swap_token_mm = mm;
-       swap_token_memcg = swap_token_memcg_from_mm(mm);
-       last_aging = global_faults;
-       goto out;
-}
-
-/* Called on process exit. */
-void __put_swap_token(struct mm_struct *mm)
-{
-       spin_lock(&swap_token_lock);
-       if (likely(mm == swap_token_mm)) {
-               trace_put_swap_token(swap_token_mm);
-               swap_token_mm = NULL;
-               swap_token_memcg = NULL;
-       }
-       spin_unlock(&swap_token_lock);
-}
-
-static bool match_memcg(struct mem_cgroup *a, struct mem_cgroup *b)
-{
-       if (!a)
-               return true;
-       if (!b)
-               return true;
-       if (a == b)
-               return true;
-       return false;
-}
-
-void disable_swap_token(struct mem_cgroup *memcg)
-{
-       /* memcg reclaim don't disable unrelated mm token. */
-       if (match_memcg(memcg, swap_token_memcg)) {
-               spin_lock(&swap_token_lock);
-               if (match_memcg(memcg, swap_token_memcg)) {
-                       trace_disable_swap_token(swap_token_mm);
-                       swap_token_mm = NULL;
-                       swap_token_memcg = NULL;
-               }
-               spin_unlock(&swap_token_lock);
-       }
-}
index 61a183b89df6d15c358e7afc2b418411abe5e728..75801acdaac77449a15750c4b11b5e5f3739cb75 100644 (file)
@@ -602,31 +602,6 @@ int vmtruncate(struct inode *inode, loff_t newsize)
 }
 EXPORT_SYMBOL(vmtruncate);
 
-int vmtruncate_range(struct inode *inode, loff_t lstart, loff_t lend)
-{
-       struct address_space *mapping = inode->i_mapping;
-       loff_t holebegin = round_up(lstart, PAGE_SIZE);
-       loff_t holelen = 1 + lend - holebegin;
-
-       /*
-        * If the underlying filesystem is not going to provide
-        * a way to truncate a range of blocks (punch a hole) -
-        * we should return failure right now.
-        */
-       if (!inode->i_op->truncate_range)
-               return -ENOSYS;
-
-       mutex_lock(&inode->i_mutex);
-       inode_dio_wait(inode);
-       unmap_mapping_range(mapping, holebegin, holelen, 1);
-       inode->i_op->truncate_range(inode, lstart, lend);
-       /* unmap again to remove racily COWed private pages */
-       unmap_mapping_range(mapping, holebegin, holelen, 1);
-       mutex_unlock(&inode->i_mutex);
-
-       return 0;
-}
-
 /**
  * truncate_pagecache_range - unmap and remove pagecache that is hole-punched
  * @inode: inode
index ae962b31de888a55990769aae948bac3ef0db338..8c7265afa29f2109b884907daa050b79f0b25f8b 100644 (file)
--- a/mm/util.c
+++ b/mm/util.c
@@ -4,6 +4,7 @@
 #include <linux/export.h>
 #include <linux/err.h>
 #include <linux/sched.h>
+#include <linux/security.h>
 #include <asm/uaccess.h>
 
 #include "internal.h"
@@ -341,6 +342,35 @@ int __attribute__((weak)) get_user_pages_fast(unsigned long start,
 }
 EXPORT_SYMBOL_GPL(get_user_pages_fast);
 
+unsigned long vm_mmap_pgoff(struct file *file, unsigned long addr,
+       unsigned long len, unsigned long prot,
+       unsigned long flag, unsigned long pgoff)
+{
+       unsigned long ret;
+       struct mm_struct *mm = current->mm;
+
+       ret = security_mmap_file(file, prot, flag);
+       if (!ret) {
+               down_write(&mm->mmap_sem);
+               ret = do_mmap_pgoff(file, addr, len, prot, flag, pgoff);
+               up_write(&mm->mmap_sem);
+       }
+       return ret;
+}
+
+unsigned long vm_mmap(struct file *file, unsigned long addr,
+       unsigned long len, unsigned long prot,
+       unsigned long flag, unsigned long offset)
+{
+       if (unlikely(offset + PAGE_ALIGN(len) < offset))
+               return -EINVAL;
+       if (unlikely(offset & ~PAGE_MASK))
+               return -EINVAL;
+
+       return vm_mmap_pgoff(file, addr, len, prot, flag, offset >> PAGE_SHIFT);
+}
+EXPORT_SYMBOL(vm_mmap);
+
 /* Tracepoints definitions. */
 EXPORT_TRACEPOINT_SYMBOL(kmalloc);
 EXPORT_TRACEPOINT_SYMBOL(kmem_cache_alloc);
index 94dff883b449e8c1ed3aba28c1b7fd98f41c08db..2aad49981b5740ba496f30dad6eb5d5a0d862edf 100644 (file)
@@ -1185,9 +1185,10 @@ void __init vmalloc_init(void)
        /* Import existing vmlist entries. */
        for (tmp = vmlist; tmp; tmp = tmp->next) {
                va = kzalloc(sizeof(struct vmap_area), GFP_NOWAIT);
-               va->flags = tmp->flags | VM_VM_AREA;
+               va->flags = VM_VM_AREA;
                va->va_start = (unsigned long)tmp->addr;
                va->va_end = va->va_start + tmp->size;
+               va->vm = tmp;
                __insert_vmap_area(va);
        }
 
@@ -2375,8 +2376,8 @@ struct vm_struct **pcpu_get_vm_areas(const unsigned long *offsets,
                return NULL;
        }
 
-       vms = kzalloc(sizeof(vms[0]) * nr_vms, GFP_KERNEL);
-       vas = kzalloc(sizeof(vas[0]) * nr_vms, GFP_KERNEL);
+       vms = kcalloc(nr_vms, sizeof(vms[0]), GFP_KERNEL);
+       vas = kcalloc(nr_vms, sizeof(vas[0]), GFP_KERNEL);
        if (!vas || !vms)
                goto err_free2;
 
index 33dc256033b5020c3679a4578af854c640fc826a..eeb3bc9d1d361b6f20821073485f1b8e7c4931d3 100644 (file)
 #define CREATE_TRACE_POINTS
 #include <trace/events/vmscan.h>
 
-/*
- * reclaim_mode determines how the inactive list is shrunk
- * RECLAIM_MODE_SINGLE: Reclaim only order-0 pages
- * RECLAIM_MODE_ASYNC:  Do not block
- * RECLAIM_MODE_SYNC:   Allow blocking e.g. call wait_on_page_writeback
- * RECLAIM_MODE_LUMPYRECLAIM: For high-order allocations, take a reference
- *                     page from the LRU and reclaim all pages within a
- *                     naturally aligned range
- * RECLAIM_MODE_COMPACTION: For high-order allocations, reclaim a number of
- *                     order-0 pages and then compact the zone
- */
-typedef unsigned __bitwise__ reclaim_mode_t;
-#define RECLAIM_MODE_SINGLE            ((__force reclaim_mode_t)0x01u)
-#define RECLAIM_MODE_ASYNC             ((__force reclaim_mode_t)0x02u)
-#define RECLAIM_MODE_SYNC              ((__force reclaim_mode_t)0x04u)
-#define RECLAIM_MODE_LUMPYRECLAIM      ((__force reclaim_mode_t)0x08u)
-#define RECLAIM_MODE_COMPACTION                ((__force reclaim_mode_t)0x10u)
-
 struct scan_control {
        /* Incremented by the number of inactive pages that were scanned */
        unsigned long nr_scanned;
@@ -96,11 +78,8 @@ struct scan_control {
 
        int order;
 
-       /*
-        * Intend to reclaim enough continuous memory rather than reclaim
-        * enough amount of memory. i.e, mode for high order allocation.
-        */
-       reclaim_mode_t reclaim_mode;
+       /* Scan (total_size >> priority) pages at once */
+       int priority;
 
        /*
         * The memory cgroup that hit its limit and as a result is the
@@ -115,11 +94,6 @@ struct scan_control {
        nodemask_t      *nodemask;
 };
 
-struct mem_cgroup_zone {
-       struct mem_cgroup *mem_cgroup;
-       struct zone *zone;
-};
-
 #define lru_to_page(_head) (list_entry((_head)->prev, struct page, lru))
 
 #ifdef ARCH_HAS_PREFETCH
@@ -164,44 +138,21 @@ static bool global_reclaim(struct scan_control *sc)
 {
        return !sc->target_mem_cgroup;
 }
-
-static bool scanning_global_lru(struct mem_cgroup_zone *mz)
-{
-       return !mz->mem_cgroup;
-}
 #else
 static bool global_reclaim(struct scan_control *sc)
 {
        return true;
 }
-
-static bool scanning_global_lru(struct mem_cgroup_zone *mz)
-{
-       return true;
-}
 #endif
 
-static struct zone_reclaim_stat *get_reclaim_stat(struct mem_cgroup_zone *mz)
-{
-       if (!scanning_global_lru(mz))
-               return mem_cgroup_get_reclaim_stat(mz->mem_cgroup, mz->zone);
-
-       return &mz->zone->reclaim_stat;
-}
-
-static unsigned long zone_nr_lru_pages(struct mem_cgroup_zone *mz,
-                                      enum lru_list lru)
+static unsigned long get_lru_size(struct lruvec *lruvec, enum lru_list lru)
 {
-       if (!scanning_global_lru(mz))
-               return mem_cgroup_zone_nr_lru_pages(mz->mem_cgroup,
-                                                   zone_to_nid(mz->zone),
-                                                   zone_idx(mz->zone),
-                                                   BIT(lru));
+       if (!mem_cgroup_disabled())
+               return mem_cgroup_get_lru_size(lruvec, lru);
 
-       return zone_page_state(mz->zone, NR_LRU_BASE + lru);
+       return zone_page_state(lruvec_zone(lruvec), NR_LRU_BASE + lru);
 }
 
-
 /*
  * Add a shrinker callback to be called from the vm
  */
@@ -364,39 +315,6 @@ out:
        return ret;
 }
 
-static void set_reclaim_mode(int priority, struct scan_control *sc,
-                                  bool sync)
-{
-       reclaim_mode_t syncmode = sync ? RECLAIM_MODE_SYNC : RECLAIM_MODE_ASYNC;
-
-       /*
-        * Initially assume we are entering either lumpy reclaim or
-        * reclaim/compaction.Depending on the order, we will either set the
-        * sync mode or just reclaim order-0 pages later.
-        */
-       if (COMPACTION_BUILD)
-               sc->reclaim_mode = RECLAIM_MODE_COMPACTION;
-       else
-               sc->reclaim_mode = RECLAIM_MODE_LUMPYRECLAIM;
-
-       /*
-        * Avoid using lumpy reclaim or reclaim/compaction if possible by
-        * restricting when its set to either costly allocations or when
-        * under memory pressure
-        */
-       if (sc->order > PAGE_ALLOC_COSTLY_ORDER)
-               sc->reclaim_mode |= syncmode;
-       else if (sc->order && priority < DEF_PRIORITY - 2)
-               sc->reclaim_mode |= syncmode;
-       else
-               sc->reclaim_mode = RECLAIM_MODE_SINGLE | RECLAIM_MODE_ASYNC;
-}
-
-static void reset_reclaim_mode(struct scan_control *sc)
-{
-       sc->reclaim_mode = RECLAIM_MODE_SINGLE | RECLAIM_MODE_ASYNC;
-}
-
 static inline int is_page_cache_freeable(struct page *page)
 {
        /*
@@ -416,10 +334,6 @@ static int may_write_to_queue(struct backing_dev_info *bdi,
                return 1;
        if (bdi == current->backing_dev_info)
                return 1;
-
-       /* lumpy reclaim for hugepage often need a lot of write */
-       if (sc->order > PAGE_ALLOC_COSTLY_ORDER)
-               return 1;
        return 0;
 }
 
@@ -523,8 +437,7 @@ static pageout_t pageout(struct page *page, struct address_space *mapping,
                        /* synchronous write or broken a_ops? */
                        ClearPageReclaim(page);
                }
-               trace_mm_vmscan_writepage(page,
-                       trace_reclaim_flags(page, sc->reclaim_mode));
+               trace_mm_vmscan_writepage(page, trace_reclaim_flags(page));
                inc_zone_page_state(page, NR_VMSCAN_WRITE);
                return PAGE_SUCCESS;
        }
@@ -701,19 +614,15 @@ enum page_references {
 };
 
 static enum page_references page_check_references(struct page *page,
-                                                 struct mem_cgroup_zone *mz,
                                                  struct scan_control *sc)
 {
        int referenced_ptes, referenced_page;
        unsigned long vm_flags;
 
-       referenced_ptes = page_referenced(page, 1, mz->mem_cgroup, &vm_flags);
+       referenced_ptes = page_referenced(page, 1, sc->target_mem_cgroup,
+                                         &vm_flags);
        referenced_page = TestClearPageReferenced(page);
 
-       /* Lumpy reclaim - ignore references */
-       if (sc->reclaim_mode & RECLAIM_MODE_LUMPYRECLAIM)
-               return PAGEREF_RECLAIM;
-
        /*
         * Mlock lost the isolation race with us.  Let try_to_unmap()
         * move the page to the unevictable list.
@@ -722,7 +631,7 @@ static enum page_references page_check_references(struct page *page,
                return PAGEREF_RECLAIM;
 
        if (referenced_ptes) {
-               if (PageAnon(page))
+               if (PageSwapBacked(page))
                        return PAGEREF_ACTIVATE;
                /*
                 * All mapped pages start out with page table
@@ -763,9 +672,8 @@ static enum page_references page_check_references(struct page *page,
  * shrink_page_list() returns the number of reclaimed pages
  */
 static unsigned long shrink_page_list(struct list_head *page_list,
-                                     struct mem_cgroup_zone *mz,
+                                     struct zone *zone,
                                      struct scan_control *sc,
-                                     int priority,
                                      unsigned long *ret_nr_dirty,
                                      unsigned long *ret_nr_writeback)
 {
@@ -794,7 +702,7 @@ static unsigned long shrink_page_list(struct list_head *page_list,
                        goto keep;
 
                VM_BUG_ON(PageActive(page));
-               VM_BUG_ON(page_zone(page) != mz->zone);
+               VM_BUG_ON(page_zone(page) != zone);
 
                sc->nr_scanned++;
 
@@ -813,22 +721,11 @@ static unsigned long shrink_page_list(struct list_head *page_list,
 
                if (PageWriteback(page)) {
                        nr_writeback++;
-                       /*
-                        * Synchronous reclaim cannot queue pages for
-                        * writeback due to the possibility of stack overflow
-                        * but if it encounters a page under writeback, wait
-                        * for the IO to complete.
-                        */
-                       if ((sc->reclaim_mode & RECLAIM_MODE_SYNC) &&
-                           may_enter_fs)
-                               wait_on_page_writeback(page);
-                       else {
-                               unlock_page(page);
-                               goto keep_lumpy;
-                       }
+                       unlock_page(page);
+                       goto keep;
                }
 
-               references = page_check_references(page, mz, sc);
+               references = page_check_references(page, sc);
                switch (references) {
                case PAGEREF_ACTIVATE:
                        goto activate_locked;
@@ -879,7 +776,8 @@ static unsigned long shrink_page_list(struct list_head *page_list,
                         * unless under significant pressure.
                         */
                        if (page_is_file_cache(page) &&
-                                       (!current_is_kswapd() || priority >= DEF_PRIORITY - 2)) {
+                                       (!current_is_kswapd() ||
+                                        sc->priority >= DEF_PRIORITY - 2)) {
                                /*
                                 * Immediately reclaim when written back.
                                 * Similar in principal to deactivate_page()
@@ -908,7 +806,7 @@ static unsigned long shrink_page_list(struct list_head *page_list,
                                goto activate_locked;
                        case PAGE_SUCCESS:
                                if (PageWriteback(page))
-                                       goto keep_lumpy;
+                                       goto keep;
                                if (PageDirty(page))
                                        goto keep;
 
@@ -994,7 +892,6 @@ cull_mlocked:
                        try_to_free_swap(page);
                unlock_page(page);
                putback_lru_page(page);
-               reset_reclaim_mode(sc);
                continue;
 
 activate_locked:
@@ -1007,8 +904,6 @@ activate_locked:
 keep_locked:
                unlock_page(page);
 keep:
-               reset_reclaim_mode(sc);
-keep_lumpy:
                list_add(&page->lru, &ret_pages);
                VM_BUG_ON(PageLRU(page) || PageUnevictable(page));
        }
@@ -1020,7 +915,7 @@ keep_lumpy:
         * will encounter the same problem
         */
        if (nr_dirty && nr_dirty == nr_congested && global_reclaim(sc))
-               zone_set_flag(mz->zone, ZONE_CONGESTED);
+               zone_set_flag(zone, ZONE_CONGESTED);
 
        free_hot_cold_page_list(&free_pages, 1);
 
@@ -1041,34 +936,15 @@ keep_lumpy:
  *
  * returns 0 on success, -ve errno on failure.
  */
-int __isolate_lru_page(struct page *page, isolate_mode_t mode, int file)
+int __isolate_lru_page(struct page *page, isolate_mode_t mode)
 {
-       bool all_lru_mode;
        int ret = -EINVAL;
 
        /* Only take pages on the LRU. */
        if (!PageLRU(page))
                return ret;
 
-       all_lru_mode = (mode & (ISOLATE_ACTIVE|ISOLATE_INACTIVE)) ==
-               (ISOLATE_ACTIVE|ISOLATE_INACTIVE);
-
-       /*
-        * When checking the active state, we need to be sure we are
-        * dealing with comparible boolean values.  Take the logical not
-        * of each.
-        */
-       if (!all_lru_mode && !PageActive(page) != !(mode & ISOLATE_ACTIVE))
-               return ret;
-
-       if (!all_lru_mode && !!page_is_file_cache(page) != file)
-               return ret;
-
-       /*
-        * When this function is being called for lumpy reclaim, we
-        * initially look into all LRU pages, active, inactive and
-        * unevictable; only give shrink_page_list evictable pages.
-        */
+       /* Do not give back unevictable pages for compaction */
        if (PageUnevictable(page))
                return ret;
 
@@ -1135,54 +1011,39 @@ int __isolate_lru_page(struct page *page, isolate_mode_t mode, int file)
  * Appropriate locks must be held before calling this function.
  *
  * @nr_to_scan:        The number of pages to look through on the list.
- * @mz:                The mem_cgroup_zone to pull pages from.
+ * @lruvec:    The LRU vector to pull pages from.
  * @dst:       The temp list to put pages on to.
  * @nr_scanned:        The number of pages that were scanned.
  * @sc:                The scan_control struct for this reclaim session
  * @mode:      One of the LRU isolation modes
- * @active:    True [1] if isolating active pages
- * @file:      True [1] if isolating file [!anon] pages
+ * @lru:       LRU list id for isolating
  *
  * returns how many pages were moved onto *@dst.
  */
 static unsigned long isolate_lru_pages(unsigned long nr_to_scan,
-               struct mem_cgroup_zone *mz, struct list_head *dst,
+               struct lruvec *lruvec, struct list_head *dst,
                unsigned long *nr_scanned, struct scan_control *sc,
-               isolate_mode_t mode, int active, int file)
+               isolate_mode_t mode, enum lru_list lru)
 {
-       struct lruvec *lruvec;
-       struct list_head *src;
+       struct list_head *src = &lruvec->lists[lru];
        unsigned long nr_taken = 0;
-       unsigned long nr_lumpy_taken = 0;
-       unsigned long nr_lumpy_dirty = 0;
-       unsigned long nr_lumpy_failed = 0;
        unsigned long scan;
-       int lru = LRU_BASE;
-
-       lruvec = mem_cgroup_zone_lruvec(mz->zone, mz->mem_cgroup);
-       if (active)
-               lru += LRU_ACTIVE;
-       if (file)
-               lru += LRU_FILE;
-       src = &lruvec->lists[lru];
 
        for (scan = 0; scan < nr_to_scan && !list_empty(src); scan++) {
                struct page *page;
-               unsigned long pfn;
-               unsigned long end_pfn;
-               unsigned long page_pfn;
-               int zone_id;
+               int nr_pages;
 
                page = lru_to_page(src);
                prefetchw_prev_lru_page(page, src, flags);
 
                VM_BUG_ON(!PageLRU(page));
 
-               switch (__isolate_lru_page(page, mode, file)) {
+               switch (__isolate_lru_page(page, mode)) {
                case 0:
-                       mem_cgroup_lru_del(page);
+                       nr_pages = hpage_nr_pages(page);
+                       mem_cgroup_update_lru_size(lruvec, lru, -nr_pages);
                        list_move(&page->lru, dst);
-                       nr_taken += hpage_nr_pages(page);
+                       nr_taken += nr_pages;
                        break;
 
                case -EBUSY:
@@ -1193,93 +1054,11 @@ static unsigned long isolate_lru_pages(unsigned long nr_to_scan,
                default:
                        BUG();
                }
-
-               if (!sc->order || !(sc->reclaim_mode & RECLAIM_MODE_LUMPYRECLAIM))
-                       continue;
-
-               /*
-                * Attempt to take all pages in the order aligned region
-                * surrounding the tag page.  Only take those pages of
-                * the same active state as that tag page.  We may safely
-                * round the target page pfn down to the requested order
-                * as the mem_map is guaranteed valid out to MAX_ORDER,
-                * where that page is in a different zone we will detect
-                * it from its zone id and abort this block scan.
-                */
-               zone_id = page_zone_id(page);
-               page_pfn = page_to_pfn(page);
-               pfn = page_pfn & ~((1 << sc->order) - 1);
-               end_pfn = pfn + (1 << sc->order);
-               for (; pfn < end_pfn; pfn++) {
-                       struct page *cursor_page;
-
-                       /* The target page is in the block, ignore it. */
-                       if (unlikely(pfn == page_pfn))
-                               continue;
-
-                       /* Avoid holes within the zone. */
-                       if (unlikely(!pfn_valid_within(pfn)))
-                               break;
-
-                       cursor_page = pfn_to_page(pfn);
-
-                       /* Check that we have not crossed a zone boundary. */
-                       if (unlikely(page_zone_id(cursor_page) != zone_id))
-                               break;
-
-                       /*
-                        * If we don't have enough swap space, reclaiming of
-                        * anon page which don't already have a swap slot is
-                        * pointless.
-                        */
-                       if (nr_swap_pages <= 0 && PageSwapBacked(cursor_page) &&
-                           !PageSwapCache(cursor_page))
-                               break;
-
-                       if (__isolate_lru_page(cursor_page, mode, file) == 0) {
-                               unsigned int isolated_pages;
-
-                               mem_cgroup_lru_del(cursor_page);
-                               list_move(&cursor_page->lru, dst);
-                               isolated_pages = hpage_nr_pages(cursor_page);
-                               nr_taken += isolated_pages;
-                               nr_lumpy_taken += isolated_pages;
-                               if (PageDirty(cursor_page))
-                                       nr_lumpy_dirty += isolated_pages;
-                               scan++;
-                               pfn += isolated_pages - 1;
-                       } else {
-                               /*
-                                * Check if the page is freed already.
-                                *
-                                * We can't use page_count() as that
-                                * requires compound_head and we don't
-                                * have a pin on the page here. If a
-                                * page is tail, we may or may not
-                                * have isolated the head, so assume
-                                * it's not free, it'd be tricky to
-                                * track the head status without a
-                                * page pin.
-                                */
-                               if (!PageTail(cursor_page) &&
-                                   !atomic_read(&cursor_page->_count))
-                                       continue;
-                               break;
-                       }
-               }
-
-               /* If we break out of the loop above, lumpy reclaim failed */
-               if (pfn < end_pfn)
-                       nr_lumpy_failed++;
        }
 
        *nr_scanned = scan;
-
-       trace_mm_vmscan_lru_isolate(sc->order,
-                       nr_to_scan, scan,
-                       nr_taken,
-                       nr_lumpy_taken, nr_lumpy_dirty, nr_lumpy_failed,
-                       mode, file);
+       trace_mm_vmscan_lru_isolate(sc->order, nr_to_scan, scan,
+                                   nr_taken, mode, is_file_lru(lru));
        return nr_taken;
 }
 
@@ -1316,15 +1095,16 @@ int isolate_lru_page(struct page *page)
 
        if (PageLRU(page)) {
                struct zone *zone = page_zone(page);
+               struct lruvec *lruvec;
 
                spin_lock_irq(&zone->lru_lock);
+               lruvec = mem_cgroup_page_lruvec(page, zone);
                if (PageLRU(page)) {
                        int lru = page_lru(page);
-                       ret = 0;
                        get_page(page);
                        ClearPageLRU(page);
-
-                       del_page_from_lru_list(zone, page, lru);
+                       del_page_from_lru_list(page, lruvec, lru);
+                       ret = 0;
                }
                spin_unlock_irq(&zone->lru_lock);
        }
@@ -1357,11 +1137,10 @@ static int too_many_isolated(struct zone *zone, int file,
 }
 
 static noinline_for_stack void
-putback_inactive_pages(struct mem_cgroup_zone *mz,
-                      struct list_head *page_list)
+putback_inactive_pages(struct lruvec *lruvec, struct list_head *page_list)
 {
-       struct zone_reclaim_stat *reclaim_stat = get_reclaim_stat(mz);
-       struct zone *zone = mz->zone;
+       struct zone_reclaim_stat *reclaim_stat = &lruvec->reclaim_stat;
+       struct zone *zone = lruvec_zone(lruvec);
        LIST_HEAD(pages_to_free);
 
        /*
@@ -1379,9 +1158,13 @@ putback_inactive_pages(struct mem_cgroup_zone *mz,
                        spin_lock_irq(&zone->lru_lock);
                        continue;
                }
+
+               lruvec = mem_cgroup_page_lruvec(page, zone);
+
                SetPageLRU(page);
                lru = page_lru(page);
-               add_page_to_lru_list(zone, page, lru);
+               add_page_to_lru_list(page, lruvec, lru);
+
                if (is_active_lru(lru)) {
                        int file = is_file_lru(lru);
                        int numpages = hpage_nr_pages(page);
@@ -1390,7 +1173,7 @@ putback_inactive_pages(struct mem_cgroup_zone *mz,
                if (put_page_testzero(page)) {
                        __ClearPageLRU(page);
                        __ClearPageActive(page);
-                       del_page_from_lru_list(zone, page, lru);
+                       del_page_from_lru_list(page, lruvec, lru);
 
                        if (unlikely(PageCompound(page))) {
                                spin_unlock_irq(&zone->lru_lock);
@@ -1407,112 +1190,24 @@ putback_inactive_pages(struct mem_cgroup_zone *mz,
        list_splice(&pages_to_free, page_list);
 }
 
-static noinline_for_stack void
-update_isolated_counts(struct mem_cgroup_zone *mz,
-                      struct list_head *page_list,
-                      unsigned long *nr_anon,
-                      unsigned long *nr_file)
-{
-       struct zone *zone = mz->zone;
-       unsigned int count[NR_LRU_LISTS] = { 0, };
-       unsigned long nr_active = 0;
-       struct page *page;
-       int lru;
-
-       /*
-        * Count pages and clear active flags
-        */
-       list_for_each_entry(page, page_list, lru) {
-               int numpages = hpage_nr_pages(page);
-               lru = page_lru_base_type(page);
-               if (PageActive(page)) {
-                       lru += LRU_ACTIVE;
-                       ClearPageActive(page);
-                       nr_active += numpages;
-               }
-               count[lru] += numpages;
-       }
-
-       preempt_disable();
-       __count_vm_events(PGDEACTIVATE, nr_active);
-
-       __mod_zone_page_state(zone, NR_ACTIVE_FILE,
-                             -count[LRU_ACTIVE_FILE]);
-       __mod_zone_page_state(zone, NR_INACTIVE_FILE,
-                             -count[LRU_INACTIVE_FILE]);
-       __mod_zone_page_state(zone, NR_ACTIVE_ANON,
-                             -count[LRU_ACTIVE_ANON]);
-       __mod_zone_page_state(zone, NR_INACTIVE_ANON,
-                             -count[LRU_INACTIVE_ANON]);
-
-       *nr_anon = count[LRU_ACTIVE_ANON] + count[LRU_INACTIVE_ANON];
-       *nr_file = count[LRU_ACTIVE_FILE] + count[LRU_INACTIVE_FILE];
-
-       __mod_zone_page_state(zone, NR_ISOLATED_ANON, *nr_anon);
-       __mod_zone_page_state(zone, NR_ISOLATED_FILE, *nr_file);
-       preempt_enable();
-}
-
-/*
- * Returns true if a direct reclaim should wait on pages under writeback.
- *
- * If we are direct reclaiming for contiguous pages and we do not reclaim
- * everything in the list, try again and wait for writeback IO to complete.
- * This will stall high-order allocations noticeably. Only do that when really
- * need to free the pages under high memory pressure.
- */
-static inline bool should_reclaim_stall(unsigned long nr_taken,
-                                       unsigned long nr_freed,
-                                       int priority,
-                                       struct scan_control *sc)
-{
-       int lumpy_stall_priority;
-
-       /* kswapd should not stall on sync IO */
-       if (current_is_kswapd())
-               return false;
-
-       /* Only stall on lumpy reclaim */
-       if (sc->reclaim_mode & RECLAIM_MODE_SINGLE)
-               return false;
-
-       /* If we have reclaimed everything on the isolated list, no stall */
-       if (nr_freed == nr_taken)
-               return false;
-
-       /*
-        * For high-order allocations, there are two stall thresholds.
-        * High-cost allocations stall immediately where as lower
-        * order allocations such as stacks require the scanning
-        * priority to be much higher before stalling.
-        */
-       if (sc->order > PAGE_ALLOC_COSTLY_ORDER)
-               lumpy_stall_priority = DEF_PRIORITY;
-       else
-               lumpy_stall_priority = DEF_PRIORITY / 3;
-
-       return priority <= lumpy_stall_priority;
-}
-
 /*
  * shrink_inactive_list() is a helper for shrink_zone().  It returns the number
  * of reclaimed pages
  */
 static noinline_for_stack unsigned long
-shrink_inactive_list(unsigned long nr_to_scan, struct mem_cgroup_zone *mz,
-                    struct scan_control *sc, int priority, int file)
+shrink_inactive_list(unsigned long nr_to_scan, struct lruvec *lruvec,
+                    struct scan_control *sc, enum lru_list lru)
 {
        LIST_HEAD(page_list);
        unsigned long nr_scanned;
        unsigned long nr_reclaimed = 0;
        unsigned long nr_taken;
-       unsigned long nr_anon;
-       unsigned long nr_file;
        unsigned long nr_dirty = 0;
        unsigned long nr_writeback = 0;
-       isolate_mode_t isolate_mode = ISOLATE_INACTIVE;
-       struct zone *zone = mz->zone;
-       struct zone_reclaim_stat *reclaim_stat = get_reclaim_stat(mz);
+       isolate_mode_t isolate_mode = 0;
+       int file = is_file_lru(lru);
+       struct zone *zone = lruvec_zone(lruvec);
+       struct zone_reclaim_stat *reclaim_stat = &lruvec->reclaim_stat;
 
        while (unlikely(too_many_isolated(zone, file, sc))) {
                congestion_wait(BLK_RW_ASYNC, HZ/10);
@@ -1522,10 +1217,6 @@ shrink_inactive_list(unsigned long nr_to_scan, struct mem_cgroup_zone *mz,
                        return SWAP_CLUSTER_MAX;
        }
 
-       set_reclaim_mode(priority, sc, false);
-       if (sc->reclaim_mode & RECLAIM_MODE_LUMPYRECLAIM)
-               isolate_mode |= ISOLATE_ACTIVE;
-
        lru_add_drain();
 
        if (!sc->may_unmap)
@@ -1535,38 +1226,30 @@ shrink_inactive_list(unsigned long nr_to_scan, struct mem_cgroup_zone *mz,
 
        spin_lock_irq(&zone->lru_lock);
 
-       nr_taken = isolate_lru_pages(nr_to_scan, mz, &page_list, &nr_scanned,
-                                    sc, isolate_mode, 0, file);
+       nr_taken = isolate_lru_pages(nr_to_scan, lruvec, &page_list,
+                                    &nr_scanned, sc, isolate_mode, lru);
+
+       __mod_zone_page_state(zone, NR_LRU_BASE + lru, -nr_taken);
+       __mod_zone_page_state(zone, NR_ISOLATED_ANON + file, nr_taken);
+
        if (global_reclaim(sc)) {
                zone->pages_scanned += nr_scanned;
                if (current_is_kswapd())
-                       __count_zone_vm_events(PGSCAN_KSWAPD, zone,
-                                              nr_scanned);
+                       __count_zone_vm_events(PGSCAN_KSWAPD, zone, nr_scanned);
                else
-                       __count_zone_vm_events(PGSCAN_DIRECT, zone,
-                                              nr_scanned);
+                       __count_zone_vm_events(PGSCAN_DIRECT, zone, nr_scanned);
        }
        spin_unlock_irq(&zone->lru_lock);
 
        if (nr_taken == 0)
                return 0;
 
-       update_isolated_counts(mz, &page_list, &nr_anon, &nr_file);
-
-       nr_reclaimed = shrink_page_list(&page_list, mz, sc, priority,
+       nr_reclaimed = shrink_page_list(&page_list, zone, sc,
                                                &nr_dirty, &nr_writeback);
 
-       /* Check if we should syncronously wait for writeback */
-       if (should_reclaim_stall(nr_taken, nr_reclaimed, priority, sc)) {
-               set_reclaim_mode(priority, sc, true);
-               nr_reclaimed += shrink_page_list(&page_list, mz, sc,
-                                       priority, &nr_dirty, &nr_writeback);
-       }
-
        spin_lock_irq(&zone->lru_lock);
 
-       reclaim_stat->recent_scanned[0] += nr_anon;
-       reclaim_stat->recent_scanned[1] += nr_file;
+       reclaim_stat->recent_scanned[file] += nr_taken;
 
        if (global_reclaim(sc)) {
                if (current_is_kswapd())
@@ -1577,10 +1260,9 @@ shrink_inactive_list(unsigned long nr_to_scan, struct mem_cgroup_zone *mz,
                                               nr_reclaimed);
        }
 
-       putback_inactive_pages(mz, &page_list);
+       putback_inactive_pages(lruvec, &page_list);
 
-       __mod_zone_page_state(zone, NR_ISOLATED_ANON, -nr_anon);
-       __mod_zone_page_state(zone, NR_ISOLATED_FILE, -nr_file);
+       __mod_zone_page_state(zone, NR_ISOLATED_ANON + file, -nr_taken);
 
        spin_unlock_irq(&zone->lru_lock);
 
@@ -1609,14 +1291,15 @@ shrink_inactive_list(unsigned long nr_to_scan, struct mem_cgroup_zone *mz,
         * DEF_PRIORITY-6 For SWAP_CLUSTER_MAX isolated pages, throttle if any
         *                     isolated page is PageWriteback
         */
-       if (nr_writeback && nr_writeback >= (nr_taken >> (DEF_PRIORITY-priority)))
+       if (nr_writeback && nr_writeback >=
+                       (nr_taken >> (DEF_PRIORITY - sc->priority)))
                wait_iff_congested(zone, BLK_RW_ASYNC, HZ/10);
 
        trace_mm_vmscan_lru_shrink_inactive(zone->zone_pgdat->node_id,
                zone_idx(zone),
                nr_scanned, nr_reclaimed,
-               priority,
-               trace_shrink_flags(file, sc->reclaim_mode));
+               sc->priority,
+               trace_shrink_flags(file));
        return nr_reclaimed;
 }
 
@@ -1638,30 +1321,32 @@ shrink_inactive_list(unsigned long nr_to_scan, struct mem_cgroup_zone *mz,
  * But we had to alter page->flags anyway.
  */
 
-static void move_active_pages_to_lru(struct zone *zone,
+static void move_active_pages_to_lru(struct lruvec *lruvec,
                                     struct list_head *list,
                                     struct list_head *pages_to_free,
                                     enum lru_list lru)
 {
+       struct zone *zone = lruvec_zone(lruvec);
        unsigned long pgmoved = 0;
        struct page *page;
+       int nr_pages;
 
        while (!list_empty(list)) {
-               struct lruvec *lruvec;
-
                page = lru_to_page(list);
+               lruvec = mem_cgroup_page_lruvec(page, zone);
 
                VM_BUG_ON(PageLRU(page));
                SetPageLRU(page);
 
-               lruvec = mem_cgroup_lru_add_list(zone, page, lru);
+               nr_pages = hpage_nr_pages(page);
+               mem_cgroup_update_lru_size(lruvec, lru, nr_pages);
                list_move(&page->lru, &lruvec->lists[lru]);
-               pgmoved += hpage_nr_pages(page);
+               pgmoved += nr_pages;
 
                if (put_page_testzero(page)) {
                        __ClearPageLRU(page);
                        __ClearPageActive(page);
-                       del_page_from_lru_list(zone, page, lru);
+                       del_page_from_lru_list(page, lruvec, lru);
 
                        if (unlikely(PageCompound(page))) {
                                spin_unlock_irq(&zone->lru_lock);
@@ -1677,9 +1362,9 @@ static void move_active_pages_to_lru(struct zone *zone,
 }
 
 static void shrink_active_list(unsigned long nr_to_scan,
-                              struct mem_cgroup_zone *mz,
+                              struct lruvec *lruvec,
                               struct scan_control *sc,
-                              int priority, int file)
+                              enum lru_list lru)
 {
        unsigned long nr_taken;
        unsigned long nr_scanned;
@@ -1688,15 +1373,14 @@ static void shrink_active_list(unsigned long nr_to_scan,
        LIST_HEAD(l_active);
        LIST_HEAD(l_inactive);
        struct page *page;
-       struct zone_reclaim_stat *reclaim_stat = get_reclaim_stat(mz);
+       struct zone_reclaim_stat *reclaim_stat = &lruvec->reclaim_stat;
        unsigned long nr_rotated = 0;
-       isolate_mode_t isolate_mode = ISOLATE_ACTIVE;
-       struct zone *zone = mz->zone;
+       isolate_mode_t isolate_mode = 0;
+       int file = is_file_lru(lru);
+       struct zone *zone = lruvec_zone(lruvec);
 
        lru_add_drain();
 
-       reset_reclaim_mode(sc);
-
        if (!sc->may_unmap)
                isolate_mode |= ISOLATE_UNMAPPED;
        if (!sc->may_writepage)
@@ -1704,18 +1388,15 @@ static void shrink_active_list(unsigned long nr_to_scan,
 
        spin_lock_irq(&zone->lru_lock);
 
-       nr_taken = isolate_lru_pages(nr_to_scan, mz, &l_hold, &nr_scanned, sc,
-                                    isolate_mode, 1, file);
+       nr_taken = isolate_lru_pages(nr_to_scan, lruvec, &l_hold,
+                                    &nr_scanned, sc, isolate_mode, lru);
        if (global_reclaim(sc))
                zone->pages_scanned += nr_scanned;
 
        reclaim_stat->recent_scanned[file] += nr_taken;
 
        __count_zone_vm_events(PGREFILL, zone, nr_scanned);
-       if (file)
-               __mod_zone_page_state(zone, NR_ACTIVE_FILE, -nr_taken);
-       else
-               __mod_zone_page_state(zone, NR_ACTIVE_ANON, -nr_taken);
+       __mod_zone_page_state(zone, NR_LRU_BASE + lru, -nr_taken);
        __mod_zone_page_state(zone, NR_ISOLATED_ANON + file, nr_taken);
        spin_unlock_irq(&zone->lru_lock);
 
@@ -1737,7 +1418,8 @@ static void shrink_active_list(unsigned long nr_to_scan,
                        }
                }
 
-               if (page_referenced(page, 0, mz->mem_cgroup, &vm_flags)) {
+               if (page_referenced(page, 0, sc->target_mem_cgroup,
+                                   &vm_flags)) {
                        nr_rotated += hpage_nr_pages(page);
                        /*
                         * Identify referenced, file-backed active pages and
@@ -1770,10 +1452,8 @@ static void shrink_active_list(unsigned long nr_to_scan,
         */
        reclaim_stat->recent_rotated[file] += nr_rotated;
 
-       move_active_pages_to_lru(zone, &l_active, &l_hold,
-                                               LRU_ACTIVE + file * LRU_FILE);
-       move_active_pages_to_lru(zone, &l_inactive, &l_hold,
-                                               LRU_BASE   + file * LRU_FILE);
+       move_active_pages_to_lru(lruvec, &l_active, &l_hold, lru);
+       move_active_pages_to_lru(lruvec, &l_inactive, &l_hold, lru - LRU_ACTIVE);
        __mod_zone_page_state(zone, NR_ISOLATED_ANON + file, -nr_taken);
        spin_unlock_irq(&zone->lru_lock);
 
@@ -1796,13 +1476,12 @@ static int inactive_anon_is_low_global(struct zone *zone)
 
 /**
  * inactive_anon_is_low - check if anonymous pages need to be deactivated
- * @zone: zone to check
- * @sc:   scan control of this context
+ * @lruvec: LRU vector to check
  *
  * Returns true if the zone does not have enough inactive anon pages,
  * meaning some active anon pages need to be deactivated.
  */
-static int inactive_anon_is_low(struct mem_cgroup_zone *mz)
+static int inactive_anon_is_low(struct lruvec *lruvec)
 {
        /*
         * If we don't have swap space, anonymous page deactivation
@@ -1811,14 +1490,13 @@ static int inactive_anon_is_low(struct mem_cgroup_zone *mz)
        if (!total_swap_pages)
                return 0;
 
-       if (!scanning_global_lru(mz))
-               return mem_cgroup_inactive_anon_is_low(mz->mem_cgroup,
-                                                      mz->zone);
+       if (!mem_cgroup_disabled())
+               return mem_cgroup_inactive_anon_is_low(lruvec);
 
-       return inactive_anon_is_low_global(mz->zone);
+       return inactive_anon_is_low_global(lruvec_zone(lruvec));
 }
 #else
-static inline int inactive_anon_is_low(struct mem_cgroup_zone *mz)
+static inline int inactive_anon_is_low(struct lruvec *lruvec)
 {
        return 0;
 }
@@ -1836,7 +1514,7 @@ static int inactive_file_is_low_global(struct zone *zone)
 
 /**
  * inactive_file_is_low - check if file pages need to be deactivated
- * @mz: memory cgroup and zone to check
+ * @lruvec: LRU vector to check
  *
  * When the system is doing streaming IO, memory pressure here
  * ensures that active file pages get deactivated, until more
@@ -1848,44 +1526,39 @@ static int inactive_file_is_low_global(struct zone *zone)
  * This uses a different ratio than the anonymous pages, because
  * the page cache uses a use-once replacement algorithm.
  */
-static int inactive_file_is_low(struct mem_cgroup_zone *mz)
+static int inactive_file_is_low(struct lruvec *lruvec)
 {
-       if (!scanning_global_lru(mz))
-               return mem_cgroup_inactive_file_is_low(mz->mem_cgroup,
-                                                      mz->zone);
+       if (!mem_cgroup_disabled())
+               return mem_cgroup_inactive_file_is_low(lruvec);
 
-       return inactive_file_is_low_global(mz->zone);
+       return inactive_file_is_low_global(lruvec_zone(lruvec));
 }
 
-static int inactive_list_is_low(struct mem_cgroup_zone *mz, int file)
+static int inactive_list_is_low(struct lruvec *lruvec, enum lru_list lru)
 {
-       if (file)
-               return inactive_file_is_low(mz);
+       if (is_file_lru(lru))
+               return inactive_file_is_low(lruvec);
        else
-               return inactive_anon_is_low(mz);
+               return inactive_anon_is_low(lruvec);
 }
 
 static unsigned long shrink_list(enum lru_list lru, unsigned long nr_to_scan,
-                                struct mem_cgroup_zone *mz,
-                                struct scan_control *sc, int priority)
+                                struct lruvec *lruvec, struct scan_control *sc)
 {
-       int file = is_file_lru(lru);
-
        if (is_active_lru(lru)) {
-               if (inactive_list_is_low(mz, file))
-                       shrink_active_list(nr_to_scan, mz, sc, priority, file);
+               if (inactive_list_is_low(lruvec, lru))
+                       shrink_active_list(nr_to_scan, lruvec, sc, lru);
                return 0;
        }
 
-       return shrink_inactive_list(nr_to_scan, mz, sc, priority, file);
+       return shrink_inactive_list(nr_to_scan, lruvec, sc, lru);
 }
 
-static int vmscan_swappiness(struct mem_cgroup_zone *mz,
-                            struct scan_control *sc)
+static int vmscan_swappiness(struct scan_control *sc)
 {
        if (global_reclaim(sc))
                return vm_swappiness;
-       return mem_cgroup_swappiness(mz->mem_cgroup);
+       return mem_cgroup_swappiness(sc->target_mem_cgroup);
 }
 
 /*
@@ -1896,17 +1569,18 @@ static int vmscan_swappiness(struct mem_cgroup_zone *mz,
  *
  * nr[0] = anon pages to scan; nr[1] = file pages to scan
  */
-static void get_scan_count(struct mem_cgroup_zone *mz, struct scan_control *sc,
-                          unsigned long *nr, int priority)
+static void get_scan_count(struct lruvec *lruvec, struct scan_control *sc,
+                          unsigned long *nr)
 {
        unsigned long anon, file, free;
        unsigned long anon_prio, file_prio;
        unsigned long ap, fp;
-       struct zone_reclaim_stat *reclaim_stat = get_reclaim_stat(mz);
+       struct zone_reclaim_stat *reclaim_stat = &lruvec->reclaim_stat;
        u64 fraction[2], denominator;
        enum lru_list lru;
        int noswap = 0;
        bool force_scan = false;
+       struct zone *zone = lruvec_zone(lruvec);
 
        /*
         * If the zone or memcg is small, nr[l] can be 0.  This
@@ -1918,7 +1592,7 @@ static void get_scan_count(struct mem_cgroup_zone *mz, struct scan_control *sc,
         * latencies, so it's better to scan a minimum amount there as
         * well.
         */
-       if (current_is_kswapd() && mz->zone->all_unreclaimable)
+       if (current_is_kswapd() && zone->all_unreclaimable)
                force_scan = true;
        if (!global_reclaim(sc))
                force_scan = true;
@@ -1932,16 +1606,16 @@ static void get_scan_count(struct mem_cgroup_zone *mz, struct scan_control *sc,
                goto out;
        }
 
-       anon  = zone_nr_lru_pages(mz, LRU_ACTIVE_ANON) +
-               zone_nr_lru_pages(mz, LRU_INACTIVE_ANON);
-       file  = zone_nr_lru_pages(mz, LRU_ACTIVE_FILE) +
-               zone_nr_lru_pages(mz, LRU_INACTIVE_FILE);
+       anon  = get_lru_size(lruvec, LRU_ACTIVE_ANON) +
+               get_lru_size(lruvec, LRU_INACTIVE_ANON);
+       file  = get_lru_size(lruvec, LRU_ACTIVE_FILE) +
+               get_lru_size(lruvec, LRU_INACTIVE_FILE);
 
        if (global_reclaim(sc)) {
-               free  = zone_page_state(mz->zone, NR_FREE_PAGES);
+               free  = zone_page_state(zone, NR_FREE_PAGES);
                /* If we have very few page cache pages,
                   force-scan anon pages. */
-               if (unlikely(file + free <= high_wmark_pages(mz->zone))) {
+               if (unlikely(file + free <= high_wmark_pages(zone))) {
                        fraction[0] = 1;
                        fraction[1] = 0;
                        denominator = 1;
@@ -1953,8 +1627,8 @@ static void get_scan_count(struct mem_cgroup_zone *mz, struct scan_control *sc,
         * With swappiness at 100, anonymous and file have the same priority.
         * This scanning priority is essentially the inverse of IO cost.
         */
-       anon_prio = vmscan_swappiness(mz, sc);
-       file_prio = 200 - vmscan_swappiness(mz, sc);
+       anon_prio = vmscan_swappiness(sc);
+       file_prio = 200 - anon_prio;
 
        /*
         * OK, so we have swap space and a fair amount of page cache
@@ -1967,7 +1641,7 @@ static void get_scan_count(struct mem_cgroup_zone *mz, struct scan_control *sc,
         *
         * anon in [0], file in [1]
         */
-       spin_lock_irq(&mz->zone->lru_lock);
+       spin_lock_irq(&zone->lru_lock);
        if (unlikely(reclaim_stat->recent_scanned[0] > anon / 4)) {
                reclaim_stat->recent_scanned[0] /= 2;
                reclaim_stat->recent_rotated[0] /= 2;
@@ -1983,12 +1657,12 @@ static void get_scan_count(struct mem_cgroup_zone *mz, struct scan_control *sc,
         * proportional to the fraction of recently scanned pages on
         * each list that were recently referenced and in active use.
         */
-       ap = (anon_prio + 1) * (reclaim_stat->recent_scanned[0] + 1);
+       ap = anon_prio * (reclaim_stat->recent_scanned[0] + 1);
        ap /= reclaim_stat->recent_rotated[0] + 1;
 
-       fp = (file_prio + 1) * (reclaim_stat->recent_scanned[1] + 1);
+       fp = file_prio * (reclaim_stat->recent_scanned[1] + 1);
        fp /= reclaim_stat->recent_rotated[1] + 1;
-       spin_unlock_irq(&mz->zone->lru_lock);
+       spin_unlock_irq(&zone->lru_lock);
 
        fraction[0] = ap;
        fraction[1] = fp;
@@ -1998,9 +1672,9 @@ out:
                int file = is_file_lru(lru);
                unsigned long scan;
 
-               scan = zone_nr_lru_pages(mz, lru);
-               if (priority || noswap) {
-                       scan >>= priority;
+               scan = get_lru_size(lruvec, lru);
+               if (sc->priority || noswap || !vmscan_swappiness(sc)) {
+                       scan >>= sc->priority;
                        if (!scan && force_scan)
                                scan = SWAP_CLUSTER_MAX;
                        scan = div64_u64(scan * fraction[file], denominator);
@@ -2009,14 +1683,25 @@ out:
        }
 }
 
+/* Use reclaim/compaction for costly allocs or under memory pressure */
+static bool in_reclaim_compaction(struct scan_control *sc)
+{
+       if (COMPACTION_BUILD && sc->order &&
+                       (sc->order > PAGE_ALLOC_COSTLY_ORDER ||
+                        sc->priority < DEF_PRIORITY - 2))
+               return true;
+
+       return false;
+}
+
 /*
- * Reclaim/compaction depends on a number of pages being freed. To avoid
- * disruption to the system, a small number of order-0 pages continue to be
- * rotated and reclaimed in the normal fashion. However, by the time we get
- * back to the allocator and call try_to_compact_zone(), we ensure that
- * there are enough free pages for it to be likely successful
+ * Reclaim/compaction is used for high-order allocation requests. It reclaims
+ * order-0 pages before compacting the zone. should_continue_reclaim() returns
+ * true if more pages should be reclaimed such that when the page allocator
+ * calls try_to_compact_zone() that it will have enough free pages to succeed.
+ * It will give up earlier than that if there is difficulty reclaiming pages.
  */
-static inline bool should_continue_reclaim(struct mem_cgroup_zone *mz,
+static inline bool should_continue_reclaim(struct lruvec *lruvec,
                                        unsigned long nr_reclaimed,
                                        unsigned long nr_scanned,
                                        struct scan_control *sc)
@@ -2025,7 +1710,7 @@ static inline bool should_continue_reclaim(struct mem_cgroup_zone *mz,
        unsigned long inactive_lru_pages;
 
        /* If not in reclaim/compaction mode, stop */
-       if (!(sc->reclaim_mode & RECLAIM_MODE_COMPACTION))
+       if (!in_reclaim_compaction(sc))
                return false;
 
        /* Consider stopping depending on scan and reclaim activity */
@@ -2056,15 +1741,15 @@ static inline bool should_continue_reclaim(struct mem_cgroup_zone *mz,
         * inactive lists are large enough, continue reclaiming
         */
        pages_for_compaction = (2UL << sc->order);
-       inactive_lru_pages = zone_nr_lru_pages(mz, LRU_INACTIVE_FILE);
+       inactive_lru_pages = get_lru_size(lruvec, LRU_INACTIVE_FILE);
        if (nr_swap_pages > 0)
-               inactive_lru_pages += zone_nr_lru_pages(mz, LRU_INACTIVE_ANON);
+               inactive_lru_pages += get_lru_size(lruvec, LRU_INACTIVE_ANON);
        if (sc->nr_reclaimed < pages_for_compaction &&
                        inactive_lru_pages > pages_for_compaction)
                return true;
 
        /* If compaction would go ahead or the allocation would succeed, stop */
-       switch (compaction_suitable(mz->zone, sc->order)) {
+       switch (compaction_suitable(lruvec_zone(lruvec), sc->order)) {
        case COMPACT_PARTIAL:
        case COMPACT_CONTINUE:
                return false;
@@ -2076,8 +1761,7 @@ static inline bool should_continue_reclaim(struct mem_cgroup_zone *mz,
 /*
  * This is a basic per-zone page freer.  Used by both kswapd and direct reclaim.
  */
-static void shrink_mem_cgroup_zone(int priority, struct mem_cgroup_zone *mz,
-                                  struct scan_control *sc)
+static void shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc)
 {
        unsigned long nr[NR_LRU_LISTS];
        unsigned long nr_to_scan;
@@ -2089,7 +1773,7 @@ static void shrink_mem_cgroup_zone(int priority, struct mem_cgroup_zone *mz,
 restart:
        nr_reclaimed = 0;
        nr_scanned = sc->nr_scanned;
-       get_scan_count(mz, sc, nr, priority);
+       get_scan_count(lruvec, sc, nr);
 
        blk_start_plug(&plug);
        while (nr[LRU_INACTIVE_ANON] || nr[LRU_ACTIVE_FILE] ||
@@ -2101,7 +1785,7 @@ restart:
                                nr[lru] -= nr_to_scan;
 
                                nr_reclaimed += shrink_list(lru, nr_to_scan,
-                                                           mz, sc, priority);
+                                                           lruvec, sc);
                        }
                }
                /*
@@ -2112,7 +1796,8 @@ restart:
                 * with multiple processes reclaiming pages, the total
                 * freeing target can get unreasonably large.
                 */
-               if (nr_reclaimed >= nr_to_reclaim && priority < DEF_PRIORITY)
+               if (nr_reclaimed >= nr_to_reclaim &&
+                   sc->priority < DEF_PRIORITY)
                        break;
        }
        blk_finish_plug(&plug);
@@ -2122,35 +1807,33 @@ restart:
         * Even if we did not try to evict anon pages at all, we want to
         * rebalance the anon lru active/inactive ratio.
         */
-       if (inactive_anon_is_low(mz))
-               shrink_active_list(SWAP_CLUSTER_MAX, mz, sc, priority, 0);
+       if (inactive_anon_is_low(lruvec))
+               shrink_active_list(SWAP_CLUSTER_MAX, lruvec,
+                                  sc, LRU_ACTIVE_ANON);
 
        /* reclaim/compaction might need reclaim to continue */
-       if (should_continue_reclaim(mz, nr_reclaimed,
-                                       sc->nr_scanned - nr_scanned, sc))
+       if (should_continue_reclaim(lruvec, nr_reclaimed,
+                                   sc->nr_scanned - nr_scanned, sc))
                goto restart;
 
        throttle_vm_writeout(sc->gfp_mask);
 }
 
-static void shrink_zone(int priority, struct zone *zone,
-                       struct scan_control *sc)
+static void shrink_zone(struct zone *zone, struct scan_control *sc)
 {
        struct mem_cgroup *root = sc->target_mem_cgroup;
        struct mem_cgroup_reclaim_cookie reclaim = {
                .zone = zone,
-               .priority = priority,
+               .priority = sc->priority,
        };
        struct mem_cgroup *memcg;
 
        memcg = mem_cgroup_iter(root, NULL, &reclaim);
        do {
-               struct mem_cgroup_zone mz = {
-                       .mem_cgroup = memcg,
-                       .zone = zone,
-               };
+               struct lruvec *lruvec = mem_cgroup_zone_lruvec(zone, memcg);
+
+               shrink_lruvec(lruvec, sc);
 
-               shrink_mem_cgroup_zone(priority, &mz, sc);
                /*
                 * Limit reclaim has historically picked one memcg and
                 * scanned it with decreasing priority levels until
@@ -2226,8 +1909,7 @@ static inline bool compaction_ready(struct zone *zone, struct scan_control *sc)
  * the caller that it should consider retrying the allocation instead of
  * further reclaim.
  */
-static bool shrink_zones(int priority, struct zonelist *zonelist,
-                                       struct scan_control *sc)
+static bool shrink_zones(struct zonelist *zonelist, struct scan_control *sc)
 {
        struct zoneref *z;
        struct zone *zone;
@@ -2254,7 +1936,8 @@ static bool shrink_zones(int priority, struct zonelist *zonelist,
                if (global_reclaim(sc)) {
                        if (!cpuset_zone_allowed_hardwall(zone, GFP_KERNEL))
                                continue;
-                       if (zone->all_unreclaimable && priority != DEF_PRIORITY)
+                       if (zone->all_unreclaimable &&
+                                       sc->priority != DEF_PRIORITY)
                                continue;       /* Let kswapd poll it */
                        if (COMPACTION_BUILD) {
                                /*
@@ -2286,7 +1969,7 @@ static bool shrink_zones(int priority, struct zonelist *zonelist,
                        /* need some check for avoid more shrink_zone() */
                }
 
-               shrink_zone(priority, zone, sc);
+               shrink_zone(zone, sc);
        }
 
        return aborted_reclaim;
@@ -2337,7 +2020,6 @@ static unsigned long do_try_to_free_pages(struct zonelist *zonelist,
                                        struct scan_control *sc,
                                        struct shrink_control *shrink)
 {
-       int priority;
        unsigned long total_scanned = 0;
        struct reclaim_state *reclaim_state = current->reclaim_state;
        struct zoneref *z;
@@ -2350,11 +2032,9 @@ static unsigned long do_try_to_free_pages(struct zonelist *zonelist,
        if (global_reclaim(sc))
                count_vm_event(ALLOCSTALL);
 
-       for (priority = DEF_PRIORITY; priority >= 0; priority--) {
+       do {
                sc->nr_scanned = 0;
-               if (!priority)
-                       disable_swap_token(sc->target_mem_cgroup);
-               aborted_reclaim = shrink_zones(priority, zonelist, sc);
+               aborted_reclaim = shrink_zones(zonelist, sc);
 
                /*
                 * Don't shrink slabs when reclaiming memory from
@@ -2396,7 +2076,7 @@ static unsigned long do_try_to_free_pages(struct zonelist *zonelist,
 
                /* Take a nap, wait for some writeback to complete */
                if (!sc->hibernation_mode && sc->nr_scanned &&
-                   priority < DEF_PRIORITY - 2) {
+                   sc->priority < DEF_PRIORITY - 2) {
                        struct zone *preferred_zone;
 
                        first_zones_zonelist(zonelist, gfp_zone(sc->gfp_mask),
@@ -2404,7 +2084,7 @@ static unsigned long do_try_to_free_pages(struct zonelist *zonelist,
                                                &preferred_zone);
                        wait_iff_congested(preferred_zone, BLK_RW_ASYNC, HZ/10);
                }
-       }
+       } while (--sc->priority >= 0);
 
 out:
        delayacct_freepages_end();
@@ -2442,6 +2122,7 @@ unsigned long try_to_free_pages(struct zonelist *zonelist, int order,
                .may_unmap = 1,
                .may_swap = 1,
                .order = order,
+               .priority = DEF_PRIORITY,
                .target_mem_cgroup = NULL,
                .nodemask = nodemask,
        };
@@ -2474,17 +2155,15 @@ unsigned long mem_cgroup_shrink_node_zone(struct mem_cgroup *memcg,
                .may_unmap = 1,
                .may_swap = !noswap,
                .order = 0,
+               .priority = 0,
                .target_mem_cgroup = memcg,
        };
-       struct mem_cgroup_zone mz = {
-               .mem_cgroup = memcg,
-               .zone = zone,
-       };
+       struct lruvec *lruvec = mem_cgroup_zone_lruvec(zone, memcg);
 
        sc.gfp_mask = (gfp_mask & GFP_RECLAIM_MASK) |
                        (GFP_HIGHUSER_MOVABLE & ~GFP_RECLAIM_MASK);
 
-       trace_mm_vmscan_memcg_softlimit_reclaim_begin(0,
+       trace_mm_vmscan_memcg_softlimit_reclaim_begin(sc.order,
                                                      sc.may_writepage,
                                                      sc.gfp_mask);
 
@@ -2495,7 +2174,7 @@ unsigned long mem_cgroup_shrink_node_zone(struct mem_cgroup *memcg,
         * will pick up pages from other mem cgroup's as well. We hack
         * the priority and make it zero.
         */
-       shrink_mem_cgroup_zone(0, &mz, &sc);
+       shrink_lruvec(lruvec, &sc);
 
        trace_mm_vmscan_memcg_softlimit_reclaim_end(sc.nr_reclaimed);
 
@@ -2516,6 +2195,7 @@ unsigned long try_to_free_mem_cgroup_pages(struct mem_cgroup *memcg,
                .may_swap = !noswap,
                .nr_to_reclaim = SWAP_CLUSTER_MAX,
                .order = 0,
+               .priority = DEF_PRIORITY,
                .target_mem_cgroup = memcg,
                .nodemask = NULL, /* we don't care the placement */
                .gfp_mask = (gfp_mask & GFP_RECLAIM_MASK) |
@@ -2546,8 +2226,7 @@ unsigned long try_to_free_mem_cgroup_pages(struct mem_cgroup *memcg,
 }
 #endif
 
-static void age_active_anon(struct zone *zone, struct scan_control *sc,
-                           int priority)
+static void age_active_anon(struct zone *zone, struct scan_control *sc)
 {
        struct mem_cgroup *memcg;
 
@@ -2556,14 +2235,11 @@ static void age_active_anon(struct zone *zone, struct scan_control *sc,
 
        memcg = mem_cgroup_iter(NULL, NULL, NULL);
        do {
-               struct mem_cgroup_zone mz = {
-                       .mem_cgroup = memcg,
-                       .zone = zone,
-               };
+               struct lruvec *lruvec = mem_cgroup_zone_lruvec(zone, memcg);
 
-               if (inactive_anon_is_low(&mz))
-                       shrink_active_list(SWAP_CLUSTER_MAX, &mz,
-                                          sc, priority, 0);
+               if (inactive_anon_is_low(lruvec))
+                       shrink_active_list(SWAP_CLUSTER_MAX, lruvec,
+                                          sc, LRU_ACTIVE_ANON);
 
                memcg = mem_cgroup_iter(NULL, memcg, NULL);
        } while (memcg);
@@ -2672,7 +2348,6 @@ static unsigned long balance_pgdat(pg_data_t *pgdat, int order,
 {
        int all_zones_ok;
        unsigned long balanced;
-       int priority;
        int i;
        int end_zone = 0;       /* Inclusive.  0 = ZONE_DMA */
        unsigned long total_scanned;
@@ -2696,18 +2371,15 @@ static unsigned long balance_pgdat(pg_data_t *pgdat, int order,
        };
 loop_again:
        total_scanned = 0;
+       sc.priority = DEF_PRIORITY;
        sc.nr_reclaimed = 0;
        sc.may_writepage = !laptop_mode;
        count_vm_event(PAGEOUTRUN);
 
-       for (priority = DEF_PRIORITY; priority >= 0; priority--) {
+       do {
                unsigned long lru_pages = 0;
                int has_under_min_watermark_zone = 0;
 
-               /* The swap token gets in the way of swapout... */
-               if (!priority)
-                       disable_swap_token(NULL);
-
                all_zones_ok = 1;
                balanced = 0;
 
@@ -2721,14 +2393,15 @@ loop_again:
                        if (!populated_zone(zone))
                                continue;
 
-                       if (zone->all_unreclaimable && priority != DEF_PRIORITY)
+                       if (zone->all_unreclaimable &&
+                           sc.priority != DEF_PRIORITY)
                                continue;
 
                        /*
                         * Do some background aging of the anon list, to give
                         * pages a chance to be referenced before reclaiming.
                         */
-                       age_active_anon(zone, &sc, priority);
+                       age_active_anon(zone, &sc);
 
                        /*
                         * If the number of buffer_heads in the machine
@@ -2776,7 +2449,8 @@ loop_again:
                        if (!populated_zone(zone))
                                continue;
 
-                       if (zone->all_unreclaimable && priority != DEF_PRIORITY)
+                       if (zone->all_unreclaimable &&
+                           sc.priority != DEF_PRIORITY)
                                continue;
 
                        sc.nr_scanned = 0;
@@ -2820,7 +2494,7 @@ loop_again:
                                    !zone_watermark_ok_safe(zone, testorder,
                                        high_wmark_pages(zone) + balance_gap,
                                        end_zone, 0)) {
-                               shrink_zone(priority, zone, &sc);
+                               shrink_zone(zone, &sc);
 
                                reclaim_state->reclaimed_slab = 0;
                                nr_slab = shrink_slab(&shrink, sc.nr_scanned, lru_pages);
@@ -2877,7 +2551,7 @@ loop_again:
                 * OK, kswapd is getting into trouble.  Take a nap, then take
                 * another pass across the zones.
                 */
-               if (total_scanned && (priority < DEF_PRIORITY - 2)) {
+               if (total_scanned && (sc.priority < DEF_PRIORITY - 2)) {
                        if (has_under_min_watermark_zone)
                                count_vm_event(KSWAPD_SKIP_CONGESTION_WAIT);
                        else
@@ -2892,7 +2566,7 @@ loop_again:
                 */
                if (sc.nr_reclaimed >= SWAP_CLUSTER_MAX)
                        break;
-       }
+       } while (--sc.priority >= 0);
 out:
 
        /*
@@ -2942,7 +2616,8 @@ out:
                        if (!populated_zone(zone))
                                continue;
 
-                       if (zone->all_unreclaimable && priority != DEF_PRIORITY)
+                       if (zone->all_unreclaimable &&
+                           sc.priority != DEF_PRIORITY)
                                continue;
 
                        /* Would compaction fail due to lack of free memory? */
@@ -3209,6 +2884,7 @@ unsigned long shrink_all_memory(unsigned long nr_to_reclaim)
                .nr_to_reclaim = nr_to_reclaim,
                .hibernation_mode = 1,
                .order = 0,
+               .priority = DEF_PRIORITY,
        };
        struct shrink_control shrink = {
                .gfp_mask = sc.gfp_mask,
@@ -3386,7 +3062,6 @@ static int __zone_reclaim(struct zone *zone, gfp_t gfp_mask, unsigned int order)
        const unsigned long nr_pages = 1 << order;
        struct task_struct *p = current;
        struct reclaim_state reclaim_state;
-       int priority;
        struct scan_control sc = {
                .may_writepage = !!(zone_reclaim_mode & RECLAIM_WRITE),
                .may_unmap = !!(zone_reclaim_mode & RECLAIM_SWAP),
@@ -3395,6 +3070,7 @@ static int __zone_reclaim(struct zone *zone, gfp_t gfp_mask, unsigned int order)
                                       SWAP_CLUSTER_MAX),
                .gfp_mask = gfp_mask,
                .order = order,
+               .priority = ZONE_RECLAIM_PRIORITY,
        };
        struct shrink_control shrink = {
                .gfp_mask = sc.gfp_mask,
@@ -3417,11 +3093,9 @@ static int __zone_reclaim(struct zone *zone, gfp_t gfp_mask, unsigned int order)
                 * Free memory by calling shrink zone with increasing
                 * priorities until we have enough memory freed.
                 */
-               priority = ZONE_RECLAIM_PRIORITY;
                do {
-                       shrink_zone(priority, zone, &sc);
-                       priority--;
-               } while (priority >= 0 && sc.nr_reclaimed < nr_pages);
+                       shrink_zone(zone, &sc);
+               } while (sc.nr_reclaimed < nr_pages && --sc.priority >= 0);
        }
 
        nr_slab_pages0 = zone_page_state(zone, NR_SLAB_RECLAIMABLE);
@@ -3536,7 +3210,7 @@ int page_evictable(struct page *page, struct vm_area_struct *vma)
        if (mapping_unevictable(page_mapping(page)))
                return 0;
 
-       if (PageMlocked(page) || (vma && is_mlocked_vma(vma, page)))
+       if (PageMlocked(page) || (vma && mlocked_vma_newpage(vma, page)))
                return 0;
 
        return 1;
@@ -3572,6 +3246,7 @@ void check_move_unevictable_pages(struct page **pages, int nr_pages)
                        zone = pagezone;
                        spin_lock_irq(&zone->lru_lock);
                }
+               lruvec = mem_cgroup_page_lruvec(page, zone);
 
                if (!PageLRU(page) || !PageUnevictable(page))
                        continue;
@@ -3581,11 +3256,8 @@ void check_move_unevictable_pages(struct page **pages, int nr_pages)
 
                        VM_BUG_ON(PageActive(page));
                        ClearPageUnevictable(page);
-                       __dec_zone_state(zone, NR_UNEVICTABLE);
-                       lruvec = mem_cgroup_lru_move_lists(zone, page,
-                                               LRU_UNEVICTABLE, lru);
-                       list_move(&page->lru, &lruvec->lists[lru]);
-                       __inc_zone_state(zone, NR_INACTIVE_ANON + lru);
+                       del_page_from_lru_list(page, lruvec, LRU_UNEVICTABLE);
+                       add_page_to_lru_list(page, lruvec, lru);
                        pgrescued++;
                }
        }
index 0dad31dc161808176897b64b9afb6f0f2b18411f..1bbbbd9776ade1962a277c4e9c6d2161a2963c97 100644 (file)
@@ -1223,7 +1223,6 @@ module_init(setup_vmstat)
 #if defined(CONFIG_DEBUG_FS) && defined(CONFIG_COMPACTION)
 #include <linux/debugfs.h>
 
-static struct dentry *extfrag_debug_root;
 
 /*
  * Return an index indicating how much of the available free memory is
@@ -1361,19 +1360,24 @@ static const struct file_operations extfrag_file_ops = {
 
 static int __init extfrag_debug_init(void)
 {
+       struct dentry *extfrag_debug_root;
+
        extfrag_debug_root = debugfs_create_dir("extfrag", NULL);
        if (!extfrag_debug_root)
                return -ENOMEM;
 
        if (!debugfs_create_file("unusable_index", 0444,
                        extfrag_debug_root, NULL, &unusable_file_ops))
-               return -ENOMEM;
+               goto fail;
 
        if (!debugfs_create_file("extfrag_index", 0444,
                        extfrag_debug_root, NULL, &extfrag_file_ops))
-               return -ENOMEM;
+               goto fail;
 
        return 0;
+fail:
+       debugfs_remove_recursive(extfrag_debug_root);
+       return -ENOMEM;
 }
 
 module_init(extfrag_debug_init);
index 214c2bb43d6252056a7a77dcd2d8eb5877c719d1..925ca583c09c8eae2fbaebbd73603e9194132f97 100644 (file)
@@ -59,9 +59,7 @@ static int handle_reply(struct ceph_auth_client *ac, int result,
  */
 static int ceph_auth_none_create_authorizer(
        struct ceph_auth_client *ac, int peer_type,
-       struct ceph_authorizer **a,
-       void **buf, size_t *len,
-       void **reply_buf, size_t *reply_len)
+       struct ceph_auth_handshake *auth)
 {
        struct ceph_auth_none_info *ai = ac->private;
        struct ceph_none_authorizer *au = &ai->au;
@@ -82,11 +80,12 @@ static int ceph_auth_none_create_authorizer(
                dout("built authorizer len %d\n", au->buf_len);
        }
 
-       *a = (struct ceph_authorizer *)au;
-       *buf = au->buf;
-       *len = au->buf_len;
-       *reply_buf = au->reply_buf;
-       *reply_len = sizeof(au->reply_buf);
+       auth->authorizer = (struct ceph_authorizer *) au;
+       auth->authorizer_buf = au->buf;
+       auth->authorizer_buf_len = au->buf_len;
+       auth->authorizer_reply_buf = au->reply_buf;
+       auth->authorizer_reply_buf_len = sizeof (au->reply_buf);
+
        return 0;
 
 bad2:
index 1587dc6010c6276fd7c6e997bb7af6fe08d426be..a16bf14eb027cd4e765320f98b0fb378b5a5e7c4 100644 (file)
@@ -526,9 +526,7 @@ static int ceph_x_handle_reply(struct ceph_auth_client *ac, int result,
 
 static int ceph_x_create_authorizer(
        struct ceph_auth_client *ac, int peer_type,
-       struct ceph_authorizer **a,
-       void **buf, size_t *len,
-       void **reply_buf, size_t *reply_len)
+       struct ceph_auth_handshake *auth)
 {
        struct ceph_x_authorizer *au;
        struct ceph_x_ticket_handler *th;
@@ -548,11 +546,12 @@ static int ceph_x_create_authorizer(
                return ret;
        }
 
-       *a = (struct ceph_authorizer *)au;
-       *buf = au->buf->vec.iov_base;
-       *len = au->buf->vec.iov_len;
-       *reply_buf = au->reply_buf;
-       *reply_len = sizeof(au->reply_buf);
+       auth->authorizer = (struct ceph_authorizer *) au;
+       auth->authorizer_buf = au->buf->vec.iov_base;
+       auth->authorizer_buf_len = au->buf->vec.iov_len;
+       auth->authorizer_reply_buf = au->reply_buf;
+       auth->authorizer_reply_buf_len = sizeof (au->reply_buf);
+
        return 0;
 }
 
index d6ebb13a18a4bc787eb249ad3e4b62ec1c174f97..089613234f032610c05f25a239c1d2053e768b45 100644 (file)
@@ -26,9 +26,9 @@ const char *crush_bucket_alg_name(int alg)
  * @b: bucket pointer
  * @p: item index in bucket
  */
-int crush_get_bucket_item_weight(struct crush_bucket *b, int p)
+int crush_get_bucket_item_weight(const struct crush_bucket *b, int p)
 {
-       if (p >= b->size)
+       if ((__u32)p >= b->size)
                return 0;
 
        switch (b->alg) {
@@ -37,38 +37,13 @@ int crush_get_bucket_item_weight(struct crush_bucket *b, int p)
        case CRUSH_BUCKET_LIST:
                return ((struct crush_bucket_list *)b)->item_weights[p];
        case CRUSH_BUCKET_TREE:
-               if (p & 1)
-                       return ((struct crush_bucket_tree *)b)->node_weights[p];
-               return 0;
+               return ((struct crush_bucket_tree *)b)->node_weights[crush_calc_tree_node(p)];
        case CRUSH_BUCKET_STRAW:
                return ((struct crush_bucket_straw *)b)->item_weights[p];
        }
        return 0;
 }
 
-/**
- * crush_calc_parents - Calculate parent vectors for the given crush map.
- * @map: crush_map pointer
- */
-void crush_calc_parents(struct crush_map *map)
-{
-       int i, b, c;
-
-       for (b = 0; b < map->max_buckets; b++) {
-               if (map->buckets[b] == NULL)
-                       continue;
-               for (i = 0; i < map->buckets[b]->size; i++) {
-                       c = map->buckets[b]->items[i];
-                       BUG_ON(c >= map->max_devices ||
-                              c < -map->max_buckets);
-                       if (c >= 0)
-                               map->device_parents[c] = map->buckets[b]->id;
-                       else
-                               map->bucket_parents[-1-c] = map->buckets[b]->id;
-               }
-       }
-}
-
 void crush_destroy_bucket_uniform(struct crush_bucket_uniform *b)
 {
        kfree(b->h.perm);
@@ -87,6 +62,8 @@ void crush_destroy_bucket_list(struct crush_bucket_list *b)
 
 void crush_destroy_bucket_tree(struct crush_bucket_tree *b)
 {
+       kfree(b->h.perm);
+       kfree(b->h.items);
        kfree(b->node_weights);
        kfree(b);
 }
@@ -124,10 +101,9 @@ void crush_destroy_bucket(struct crush_bucket *b)
  */
 void crush_destroy(struct crush_map *map)
 {
-       int b;
-
        /* buckets */
        if (map->buckets) {
+               __s32 b;
                for (b = 0; b < map->max_buckets; b++) {
                        if (map->buckets[b] == NULL)
                                continue;
@@ -138,13 +114,12 @@ void crush_destroy(struct crush_map *map)
 
        /* rules */
        if (map->rules) {
+               __u32 b;
                for (b = 0; b < map->max_rules; b++)
                        kfree(map->rules[b]);
                kfree(map->rules);
        }
 
-       kfree(map->bucket_parents);
-       kfree(map->device_parents);
        kfree(map);
 }
 
index 363f8f7e6c3caa15fa03d0bcea1967b731f2ae1d..d7edc24333b84d5aab17da2d983878ff5044b2bb 100644 (file)
@@ -33,9 +33,9 @@
  * @type: storage ruleset type (user defined)
  * @size: output set size
  */
-int crush_find_rule(struct crush_map *map, int ruleset, int type, int size)
+int crush_find_rule(const struct crush_map *map, int ruleset, int type, int size)
 {
-       int i;
+       __u32 i;
 
        for (i = 0; i < map->max_rules; i++) {
                if (map->rules[i] &&
@@ -73,7 +73,7 @@ static int bucket_perm_choose(struct crush_bucket *bucket,
        unsigned int i, s;
 
        /* start a new permutation if @x has changed */
-       if (bucket->perm_x != x || bucket->perm_n == 0) {
+       if (bucket->perm_x != (__u32)x || bucket->perm_n == 0) {
                dprintk("bucket %d new x=%d\n", bucket->id, x);
                bucket->perm_x = x;
 
@@ -153,8 +153,8 @@ static int bucket_list_choose(struct crush_bucket_list *bucket,
                        return bucket->h.items[i];
        }
 
-       BUG_ON(1);
-       return 0;
+       dprintk("bad list sums for bucket %d\n", bucket->h.id);
+       return bucket->h.items[0];
 }
 
 
@@ -220,7 +220,7 @@ static int bucket_tree_choose(struct crush_bucket_tree *bucket,
 static int bucket_straw_choose(struct crush_bucket_straw *bucket,
                               int x, int r)
 {
-       int i;
+       __u32 i;
        int high = 0;
        __u64 high_draw = 0;
        __u64 draw;
@@ -240,6 +240,7 @@ static int bucket_straw_choose(struct crush_bucket_straw *bucket,
 static int crush_bucket_choose(struct crush_bucket *in, int x, int r)
 {
        dprintk(" crush_bucket_choose %d x=%d r=%d\n", in->id, x, r);
+       BUG_ON(in->size == 0);
        switch (in->alg) {
        case CRUSH_BUCKET_UNIFORM:
                return bucket_uniform_choose((struct crush_bucket_uniform *)in,
@@ -254,7 +255,7 @@ static int crush_bucket_choose(struct crush_bucket *in, int x, int r)
                return bucket_straw_choose((struct crush_bucket_straw *)in,
                                           x, r);
        default:
-               BUG_ON(1);
+               dprintk("unknown bucket %d alg %d\n", in->id, in->alg);
                return in->items[0];
        }
 }
@@ -263,7 +264,7 @@ static int crush_bucket_choose(struct crush_bucket *in, int x, int r)
  * true if device is marked "out" (failed, fully offloaded)
  * of the cluster
  */
-static int is_out(struct crush_map *map, __u32 *weight, int item, int x)
+static int is_out(const struct crush_map *map, const __u32 *weight, int item, int x)
 {
        if (weight[item] >= 0x10000)
                return 0;
@@ -288,16 +289,16 @@ static int is_out(struct crush_map *map, __u32 *weight, int item, int x)
  * @recurse_to_leaf: true if we want one device under each item of given type
  * @out2: second output vector for leaf items (if @recurse_to_leaf)
  */
-static int crush_choose(struct crush_map *map,
+static int crush_choose(const struct crush_map *map,
                        struct crush_bucket *bucket,
-                       __u32 *weight,
+                       const __u32 *weight,
                        int x, int numrep, int type,
                        int *out, int outpos,
                        int firstn, int recurse_to_leaf,
                        int *out2)
 {
        int rep;
-       int ftotal, flocal;
+       unsigned int ftotal, flocal;
        int retry_descent, retry_bucket, skip_rep;
        struct crush_bucket *in = bucket;
        int r;
@@ -305,7 +306,7 @@ static int crush_choose(struct crush_map *map,
        int item = 0;
        int itemtype;
        int collide, reject;
-       const int orig_tries = 5; /* attempts before we fall back to search */
+       const unsigned int orig_tries = 5; /* attempts before we fall back to search */
 
        dprintk("CHOOSE%s bucket %d x %d outpos %d numrep %d\n", recurse_to_leaf ? "_LEAF" : "",
                bucket->id, x, outpos, numrep);
@@ -326,7 +327,7 @@ static int crush_choose(struct crush_map *map,
                                r = rep;
                                if (in->alg == CRUSH_BUCKET_UNIFORM) {
                                        /* be careful */
-                                       if (firstn || numrep >= in->size)
+                                       if (firstn || (__u32)numrep >= in->size)
                                                /* r' = r + f_total */
                                                r += ftotal;
                                        else if (in->size % numrep == 0)
@@ -355,7 +356,11 @@ static int crush_choose(struct crush_map *map,
                                        item = bucket_perm_choose(in, x, r);
                                else
                                        item = crush_bucket_choose(in, x, r);
-                               BUG_ON(item >= map->max_devices);
+                               if (item >= map->max_devices) {
+                                       dprintk("   bad item %d\n", item);
+                                       skip_rep = 1;
+                                       break;
+                               }
 
                                /* desired type? */
                                if (item < 0)
@@ -366,8 +371,12 @@ static int crush_choose(struct crush_map *map,
 
                                /* keep going? */
                                if (itemtype != type) {
-                                       BUG_ON(item >= 0 ||
-                                              (-1-item) >= map->max_buckets);
+                                       if (item >= 0 ||
+                                           (-1-item) >= map->max_buckets) {
+                                               dprintk("   bad item type %d\n", type);
+                                               skip_rep = 1;
+                                               break;
+                                       }
                                        in = map->buckets[-1-item];
                                        retry_bucket = 1;
                                        continue;
@@ -416,7 +425,7 @@ reject:
                                        if (collide && flocal < 3)
                                                /* retry locally a few times */
                                                retry_bucket = 1;
-                                       else if (flocal < in->size + orig_tries)
+                                       else if (flocal <= in->size + orig_tries)
                                                /* exhaustive bucket search */
                                                retry_bucket = 1;
                                        else if (ftotal < 20)
@@ -426,7 +435,7 @@ reject:
                                                /* else give up */
                                                skip_rep = 1;
                                        dprintk("  reject %d  collide %d  "
-                                               "ftotal %d  flocal %d\n",
+                                               "ftotal %u  flocal %u\n",
                                                reject, collide, ftotal,
                                                flocal);
                                }
@@ -455,15 +464,12 @@ reject:
  * @x: hash input
  * @result: pointer to result vector
  * @result_max: maximum result size
- * @force: force initial replica choice; -1 for none
  */
-int crush_do_rule(struct crush_map *map,
+int crush_do_rule(const struct crush_map *map,
                  int ruleno, int x, int *result, int result_max,
-                 int force, __u32 *weight)
+                 const __u32 *weight)
 {
        int result_len;
-       int force_context[CRUSH_MAX_DEPTH];
-       int force_pos = -1;
        int a[CRUSH_MAX_SET];
        int b[CRUSH_MAX_SET];
        int c[CRUSH_MAX_SET];
@@ -474,66 +480,44 @@ int crush_do_rule(struct crush_map *map,
        int osize;
        int *tmp;
        struct crush_rule *rule;
-       int step;
+       __u32 step;
        int i, j;
        int numrep;
        int firstn;
 
-       BUG_ON(ruleno >= map->max_rules);
+       if ((__u32)ruleno >= map->max_rules) {
+               dprintk(" bad ruleno %d\n", ruleno);
+               return 0;
+       }
 
        rule = map->rules[ruleno];
        result_len = 0;
        w = a;
        o = b;
 
-       /*
-        * determine hierarchical context of force, if any.  note
-        * that this may or may not correspond to the specific types
-        * referenced by the crush rule.
-        */
-       if (force >= 0 &&
-           force < map->max_devices &&
-           map->device_parents[force] != 0 &&
-           !is_out(map, weight, force, x)) {
-               while (1) {
-                       force_context[++force_pos] = force;
-                       if (force >= 0)
-                               force = map->device_parents[force];
-                       else
-                               force = map->bucket_parents[-1-force];
-                       if (force == 0)
-                               break;
-               }
-       }
-
        for (step = 0; step < rule->len; step++) {
+               struct crush_rule_step *curstep = &rule->steps[step];
+
                firstn = 0;
-               switch (rule->steps[step].op) {
+               switch (curstep->op) {
                case CRUSH_RULE_TAKE:
-                       w[0] = rule->steps[step].arg1;
-
-                       /* find position in force_context/hierarchy */
-                       while (force_pos >= 0 &&
-                              force_context[force_pos] != w[0])
-                               force_pos--;
-                       /* and move past it */
-                       if (force_pos >= 0)
-                               force_pos--;
-
+                       w[0] = curstep->arg1;
                        wsize = 1;
                        break;
 
                case CRUSH_RULE_CHOOSE_LEAF_FIRSTN:
                case CRUSH_RULE_CHOOSE_FIRSTN:
                        firstn = 1;
+                       /* fall through */
                case CRUSH_RULE_CHOOSE_LEAF_INDEP:
                case CRUSH_RULE_CHOOSE_INDEP:
-                       BUG_ON(wsize == 0);
+                       if (wsize == 0)
+                               break;
 
                        recurse_to_leaf =
-                               rule->steps[step].op ==
+                               curstep->op ==
                                 CRUSH_RULE_CHOOSE_LEAF_FIRSTN ||
-                               rule->steps[step].op ==
+                               curstep->op ==
                                CRUSH_RULE_CHOOSE_LEAF_INDEP;
 
                        /* reset output */
@@ -545,32 +529,18 @@ int crush_do_rule(struct crush_map *map,
                                 * basically, numrep <= 0 means relative to
                                 * the provided result_max
                                 */
-                               numrep = rule->steps[step].arg1;
+                               numrep = curstep->arg1;
                                if (numrep <= 0) {
                                        numrep += result_max;
                                        if (numrep <= 0)
                                                continue;
                                }
                                j = 0;
-                               if (osize == 0 && force_pos >= 0) {
-                                       /* skip any intermediate types */
-                                       while (force_pos &&
-                                              force_context[force_pos] < 0 &&
-                                              rule->steps[step].arg2 !=
-                                              map->buckets[-1 -
-                                              force_context[force_pos]]->type)
-                                               force_pos--;
-                                       o[osize] = force_context[force_pos];
-                                       if (recurse_to_leaf)
-                                               c[osize] = force_context[0];
-                                       j++;
-                                       force_pos--;
-                               }
                                osize += crush_choose(map,
                                                      map->buckets[-1-w[i]],
                                                      weight,
                                                      x, numrep,
-                                                     rule->steps[step].arg2,
+                                                     curstep->arg2,
                                                      o+osize, j,
                                                      firstn,
                                                      recurse_to_leaf, c+osize);
@@ -597,7 +567,9 @@ int crush_do_rule(struct crush_map *map,
                        break;
 
                default:
-                       BUG_ON(1);
+                       dprintk(" unknown op %d at step %d\n",
+                               curstep->op, step);
+                       break;
                }
        }
        return result_len;
index 36fa6bf684981688ff95c22788acb8db721271de..524f4e4f598b845a7242c0243efb1a4e6a843955 100644 (file)
@@ -653,54 +653,57 @@ static void prepare_write_keepalive(struct ceph_connection *con)
  * Connection negotiation.
  */
 
-static int prepare_connect_authorizer(struct ceph_connection *con)
+static struct ceph_auth_handshake *get_connect_authorizer(struct ceph_connection *con,
+                                               int *auth_proto)
 {
-       void *auth_buf;
-       int auth_len = 0;
-       int auth_protocol = 0;
+       struct ceph_auth_handshake *auth;
+
+       if (!con->ops->get_authorizer) {
+               con->out_connect.authorizer_protocol = CEPH_AUTH_UNKNOWN;
+               con->out_connect.authorizer_len = 0;
+
+               return NULL;
+       }
+
+       /* Can't hold the mutex while getting authorizer */
 
        mutex_unlock(&con->mutex);
-       if (con->ops->get_authorizer)
-               con->ops->get_authorizer(con, &auth_buf, &auth_len,
-                                        &auth_protocol, &con->auth_reply_buf,
-                                        &con->auth_reply_buf_len,
-                                        con->auth_retry);
+
+       auth = con->ops->get_authorizer(con, auth_proto, con->auth_retry);
+
        mutex_lock(&con->mutex);
 
-       if (test_bit(CLOSED, &con->state) ||
-           test_bit(OPENING, &con->state))
-               return -EAGAIN;
+       if (IS_ERR(auth))
+               return auth;
+       if (test_bit(CLOSED, &con->state) || test_bit(OPENING, &con->state))
+               return ERR_PTR(-EAGAIN);
 
-       con->out_connect.authorizer_protocol = cpu_to_le32(auth_protocol);
-       con->out_connect.authorizer_len = cpu_to_le32(auth_len);
+       con->auth_reply_buf = auth->authorizer_reply_buf;
+       con->auth_reply_buf_len = auth->authorizer_reply_buf_len;
 
-       if (auth_len)
-               ceph_con_out_kvec_add(con, auth_len, auth_buf);
 
-       return 0;
+       return auth;
 }
 
 /*
  * We connected to a peer and are saying hello.
  */
-static void prepare_write_banner(struct ceph_messenger *msgr,
-                                struct ceph_connection *con)
+static void prepare_write_banner(struct ceph_connection *con)
 {
-       ceph_con_out_kvec_reset(con);
        ceph_con_out_kvec_add(con, strlen(CEPH_BANNER), CEPH_BANNER);
-       ceph_con_out_kvec_add(con, sizeof (msgr->my_enc_addr),
-                                       &msgr->my_enc_addr);
+       ceph_con_out_kvec_add(con, sizeof (con->msgr->my_enc_addr),
+                                       &con->msgr->my_enc_addr);
 
        con->out_more = 0;
        set_bit(WRITE_PENDING, &con->state);
 }
 
-static int prepare_write_connect(struct ceph_messenger *msgr,
-                                struct ceph_connection *con,
-                                int include_banner)
+static int prepare_write_connect(struct ceph_connection *con)
 {
        unsigned int global_seq = get_global_seq(con->msgr, 0);
        int proto;
+       int auth_proto;
+       struct ceph_auth_handshake *auth;
 
        switch (con->peer_name.type) {
        case CEPH_ENTITY_TYPE_MON:
@@ -719,23 +722,32 @@ static int prepare_write_connect(struct ceph_messenger *msgr,
        dout("prepare_write_connect %p cseq=%d gseq=%d proto=%d\n", con,
             con->connect_seq, global_seq, proto);
 
-       con->out_connect.features = cpu_to_le64(msgr->supported_features);
+       con->out_connect.features = cpu_to_le64(con->msgr->supported_features);
        con->out_connect.host_type = cpu_to_le32(CEPH_ENTITY_TYPE_CLIENT);
        con->out_connect.connect_seq = cpu_to_le32(con->connect_seq);
        con->out_connect.global_seq = cpu_to_le32(global_seq);
        con->out_connect.protocol_version = cpu_to_le32(proto);
        con->out_connect.flags = 0;
 
-       if (include_banner)
-               prepare_write_banner(msgr, con);
-       else
-               ceph_con_out_kvec_reset(con);
-       ceph_con_out_kvec_add(con, sizeof (con->out_connect), &con->out_connect);
+       auth_proto = CEPH_AUTH_UNKNOWN;
+       auth = get_connect_authorizer(con, &auth_proto);
+       if (IS_ERR(auth))
+               return PTR_ERR(auth);
+
+       con->out_connect.authorizer_protocol = cpu_to_le32(auth_proto);
+       con->out_connect.authorizer_len = auth ?
+               cpu_to_le32(auth->authorizer_buf_len) : 0;
+
+       ceph_con_out_kvec_add(con, sizeof (con->out_connect),
+                                       &con->out_connect);
+       if (auth && auth->authorizer_buf_len)
+               ceph_con_out_kvec_add(con, auth->authorizer_buf_len,
+                                       auth->authorizer_buf);
 
        con->out_more = 0;
        set_bit(WRITE_PENDING, &con->state);
 
-       return prepare_connect_authorizer(con);
+       return 0;
 }
 
 /*
@@ -992,11 +1004,10 @@ static int prepare_read_message(struct ceph_connection *con)
 
 
 static int read_partial(struct ceph_connection *con,
-                       int *to, int size, void *object)
+                       int end, int size, void *object)
 {
-       *to += size;
-       while (con->in_base_pos < *to) {
-               int left = *to - con->in_base_pos;
+       while (con->in_base_pos < end) {
+               int left = end - con->in_base_pos;
                int have = size - left;
                int ret = ceph_tcp_recvmsg(con->sock, object + have, left);
                if (ret <= 0)
@@ -1012,37 +1023,52 @@ static int read_partial(struct ceph_connection *con,
  */
 static int read_partial_banner(struct ceph_connection *con)
 {
-       int ret, to = 0;
+       int size;
+       int end;
+       int ret;
 
        dout("read_partial_banner %p at %d\n", con, con->in_base_pos);
 
        /* peer's banner */
-       ret = read_partial(con, &to, strlen(CEPH_BANNER), con->in_banner);
+       size = strlen(CEPH_BANNER);
+       end = size;
+       ret = read_partial(con, end, size, con->in_banner);
        if (ret <= 0)
                goto out;
-       ret = read_partial(con, &to, sizeof(con->actual_peer_addr),
-                          &con->actual_peer_addr);
+
+       size = sizeof (con->actual_peer_addr);
+       end += size;
+       ret = read_partial(con, end, size, &con->actual_peer_addr);
        if (ret <= 0)
                goto out;
-       ret = read_partial(con, &to, sizeof(con->peer_addr_for_me),
-                          &con->peer_addr_for_me);
+
+       size = sizeof (con->peer_addr_for_me);
+       end += size;
+       ret = read_partial(con, end, size, &con->peer_addr_for_me);
        if (ret <= 0)
                goto out;
+
 out:
        return ret;
 }
 
 static int read_partial_connect(struct ceph_connection *con)
 {
-       int ret, to = 0;
+       int size;
+       int end;
+       int ret;
 
        dout("read_partial_connect %p at %d\n", con, con->in_base_pos);
 
-       ret = read_partial(con, &to, sizeof(con->in_reply), &con->in_reply);
+       size = sizeof (con->in_reply);
+       end = size;
+       ret = read_partial(con, end, size, &con->in_reply);
        if (ret <= 0)
                goto out;
-       ret = read_partial(con, &to, le32_to_cpu(con->in_reply.authorizer_len),
-                          con->auth_reply_buf);
+
+       size = le32_to_cpu(con->in_reply.authorizer_len);
+       end += size;
+       ret = read_partial(con, end, size, con->auth_reply_buf);
        if (ret <= 0)
                goto out;
 
@@ -1377,7 +1403,8 @@ static int process_connect(struct ceph_connection *con)
                        return -1;
                }
                con->auth_retry = 1;
-               ret = prepare_write_connect(con->msgr, con, 0);
+               ceph_con_out_kvec_reset(con);
+               ret = prepare_write_connect(con);
                if (ret < 0)
                        return ret;
                prepare_read_connect(con);
@@ -1397,7 +1424,10 @@ static int process_connect(struct ceph_connection *con)
                       ENTITY_NAME(con->peer_name),
                       ceph_pr_addr(&con->peer_addr.in_addr));
                reset_connection(con);
-               prepare_write_connect(con->msgr, con, 0);
+               ceph_con_out_kvec_reset(con);
+               ret = prepare_write_connect(con);
+               if (ret < 0)
+                       return ret;
                prepare_read_connect(con);
 
                /* Tell ceph about it. */
@@ -1420,7 +1450,10 @@ static int process_connect(struct ceph_connection *con)
                     le32_to_cpu(con->out_connect.connect_seq),
                     le32_to_cpu(con->in_connect.connect_seq));
                con->connect_seq = le32_to_cpu(con->in_connect.connect_seq);
-               prepare_write_connect(con->msgr, con, 0);
+               ceph_con_out_kvec_reset(con);
+               ret = prepare_write_connect(con);
+               if (ret < 0)
+                       return ret;
                prepare_read_connect(con);
                break;
 
@@ -1434,7 +1467,10 @@ static int process_connect(struct ceph_connection *con)
                     le32_to_cpu(con->in_connect.global_seq));
                get_global_seq(con->msgr,
                               le32_to_cpu(con->in_connect.global_seq));
-               prepare_write_connect(con->msgr, con, 0);
+               ceph_con_out_kvec_reset(con);
+               ret = prepare_write_connect(con);
+               if (ret < 0)
+                       return ret;
                prepare_read_connect(con);
                break;
 
@@ -1491,10 +1527,10 @@ static int process_connect(struct ceph_connection *con)
  */
 static int read_partial_ack(struct ceph_connection *con)
 {
-       int to = 0;
+       int size = sizeof (con->in_temp_ack);
+       int end = size;
 
-       return read_partial(con, &to, sizeof(con->in_temp_ack),
-                           &con->in_temp_ack);
+       return read_partial(con, end, size, &con->in_temp_ack);
 }
 
 
@@ -1627,8 +1663,9 @@ static int read_partial_message_bio(struct ceph_connection *con,
 static int read_partial_message(struct ceph_connection *con)
 {
        struct ceph_msg *m = con->in_msg;
+       int size;
+       int end;
        int ret;
-       int to, left;
        unsigned int front_len, middle_len, data_len;
        bool do_datacrc = !con->msgr->nocrc;
        int skip;
@@ -1638,15 +1675,11 @@ static int read_partial_message(struct ceph_connection *con)
        dout("read_partial_message con %p msg %p\n", con, m);
 
        /* header */
-       while (con->in_base_pos < sizeof(con->in_hdr)) {
-               left = sizeof(con->in_hdr) - con->in_base_pos;
-               ret = ceph_tcp_recvmsg(con->sock,
-                                      (char *)&con->in_hdr + con->in_base_pos,
-                                      left);
-               if (ret <= 0)
-                       return ret;
-               con->in_base_pos += ret;
-       }
+       size = sizeof (con->in_hdr);
+       end = size;
+       ret = read_partial(con, end, size, &con->in_hdr);
+       if (ret <= 0)
+               return ret;
 
        crc = crc32c(0, &con->in_hdr, offsetof(struct ceph_msg_header, crc));
        if (cpu_to_le32(crc) != con->in_hdr.crc) {
@@ -1759,16 +1792,12 @@ static int read_partial_message(struct ceph_connection *con)
        }
 
        /* footer */
-       to = sizeof(m->hdr) + sizeof(m->footer);
-       while (con->in_base_pos < to) {
-               left = to - con->in_base_pos;
-               ret = ceph_tcp_recvmsg(con->sock, (char *)&m->footer +
-                                      (con->in_base_pos - sizeof(m->hdr)),
-                                      left);
-               if (ret <= 0)
-                       return ret;
-               con->in_base_pos += ret;
-       }
+       size = sizeof (m->footer);
+       end += size;
+       ret = read_partial(con, end, size, &m->footer);
+       if (ret <= 0)
+               return ret;
+
        dout("read_partial_message got msg %p %d (%u) + %d (%u) + %d (%u)\n",
             m, front_len, m->footer.front_crc, middle_len,
             m->footer.middle_crc, data_len, m->footer.data_crc);
@@ -1835,7 +1864,6 @@ static void process_message(struct ceph_connection *con)
  */
 static int try_write(struct ceph_connection *con)
 {
-       struct ceph_messenger *msgr = con->msgr;
        int ret = 1;
 
        dout("try_write start %p state %lu nref %d\n", con, con->state,
@@ -1846,7 +1874,11 @@ more:
 
        /* open the socket first? */
        if (con->sock == NULL) {
-               prepare_write_connect(msgr, con, 1);
+               ceph_con_out_kvec_reset(con);
+               prepare_write_banner(con);
+               ret = prepare_write_connect(con);
+               if (ret < 0)
+                       goto out;
                prepare_read_banner(con);
                set_bit(CONNECTING, &con->state);
                clear_bit(NEGOTIATING, &con->state);
index 1b0ef3c4d393c5221d30c15eb24b935ee292e3fc..1ffebed5ce0f9a629ad2733349b8e33c326850d5 100644 (file)
@@ -278,7 +278,7 @@ static void osd_req_encode_op(struct ceph_osd_request *req,
 {
        dst->op = cpu_to_le16(src->op);
 
-       switch (dst->op) {
+       switch (src->op) {
        case CEPH_OSD_OP_READ:
        case CEPH_OSD_OP_WRITE:
                dst->extent.offset =
@@ -664,11 +664,11 @@ static void put_osd(struct ceph_osd *osd)
 {
        dout("put_osd %p %d -> %d\n", osd, atomic_read(&osd->o_ref),
             atomic_read(&osd->o_ref) - 1);
-       if (atomic_dec_and_test(&osd->o_ref)) {
+       if (atomic_dec_and_test(&osd->o_ref) && osd->o_auth.authorizer) {
                struct ceph_auth_client *ac = osd->o_osdc->client->monc.auth;
 
-               if (osd->o_authorizer)
-                       ac->ops->destroy_authorizer(ac, osd->o_authorizer);
+               if (ac->ops && ac->ops->destroy_authorizer)
+                       ac->ops->destroy_authorizer(ac, osd->o_auth.authorizer);
                kfree(osd);
        }
 }
@@ -841,6 +841,12 @@ static void register_request(struct ceph_osd_client *osdc,
 static void __unregister_request(struct ceph_osd_client *osdc,
                                 struct ceph_osd_request *req)
 {
+       if (RB_EMPTY_NODE(&req->r_node)) {
+               dout("__unregister_request %p tid %lld not registered\n",
+                       req, req->r_tid);
+               return;
+       }
+
        dout("__unregister_request %p tid %lld\n", req, req->r_tid);
        rb_erase(&req->r_node, &osdc->requests);
        osdc->num_requests--;
@@ -2108,37 +2114,32 @@ static void put_osd_con(struct ceph_connection *con)
 /*
  * authentication
  */
-static int get_authorizer(struct ceph_connection *con,
-                         void **buf, int *len, int *proto,
-                         void **reply_buf, int *reply_len, int force_new)
+/*
+ * Note: returned pointer is the address of a structure that's
+ * managed separately.  Caller must *not* attempt to free it.
+ */
+static struct ceph_auth_handshake *get_authorizer(struct ceph_connection *con,
+                                       int *proto, int force_new)
 {
        struct ceph_osd *o = con->private;
        struct ceph_osd_client *osdc = o->o_osdc;
        struct ceph_auth_client *ac = osdc->client->monc.auth;
-       int ret = 0;
+       struct ceph_auth_handshake *auth = &o->o_auth;
 
-       if (force_new && o->o_authorizer) {
-               ac->ops->destroy_authorizer(ac, o->o_authorizer);
-               o->o_authorizer = NULL;
-       }
-       if (o->o_authorizer == NULL) {
-               ret = ac->ops->create_authorizer(
-                       ac, CEPH_ENTITY_TYPE_OSD,
-                       &o->o_authorizer,
-                       &o->o_authorizer_buf,
-                       &o->o_authorizer_buf_len,
-                       &o->o_authorizer_reply_buf,
-                       &o->o_authorizer_reply_buf_len);
+       if (force_new && auth->authorizer) {
+               if (ac->ops && ac->ops->destroy_authorizer)
+                       ac->ops->destroy_authorizer(ac, auth->authorizer);
+               auth->authorizer = NULL;
+       }
+       if (!auth->authorizer && ac->ops && ac->ops->create_authorizer) {
+               int ret = ac->ops->create_authorizer(ac, CEPH_ENTITY_TYPE_OSD,
+                                                       auth);
                if (ret)
-                       return ret;
+                       return ERR_PTR(ret);
        }
-
        *proto = ac->protocol;
-       *buf = o->o_authorizer_buf;
-       *len = o->o_authorizer_buf_len;
-       *reply_buf = o->o_authorizer_reply_buf;
-       *reply_len = o->o_authorizer_reply_buf_len;
-       return 0;
+
+       return auth;
 }
 
 
@@ -2148,7 +2149,11 @@ static int verify_authorizer_reply(struct ceph_connection *con, int len)
        struct ceph_osd_client *osdc = o->o_osdc;
        struct ceph_auth_client *ac = osdc->client->monc.auth;
 
-       return ac->ops->verify_authorizer_reply(ac, o->o_authorizer, len);
+       /*
+        * XXX If ac->ops or ac->ops->verify_authorizer_reply is null,
+        * XXX which do we do:  succeed or fail?
+        */
+       return ac->ops->verify_authorizer_reply(ac, o->o_auth.authorizer, len);
 }
 
 static int invalidate_authorizer(struct ceph_connection *con)
@@ -2157,7 +2162,7 @@ static int invalidate_authorizer(struct ceph_connection *con)
        struct ceph_osd_client *osdc = o->o_osdc;
        struct ceph_auth_client *ac = osdc->client->monc.auth;
 
-       if (ac->ops->invalidate_authorizer)
+       if (ac->ops && ac->ops->invalidate_authorizer)
                ac->ops->invalidate_authorizer(ac, CEPH_ENTITY_TYPE_OSD);
 
        return ceph_monc_validate_auth(&osdc->client->monc);
index 56e561a690044ee88b7fe22d4ed51c09910c8e99..81e3b84a77efdecb6c44603e7784a083fe94b980 100644 (file)
@@ -161,13 +161,6 @@ static struct crush_map *crush_decode(void *pbyval, void *end)
        c->max_rules = ceph_decode_32(p);
        c->max_devices = ceph_decode_32(p);
 
-       c->device_parents = kcalloc(c->max_devices, sizeof(u32), GFP_NOFS);
-       if (c->device_parents == NULL)
-               goto badmem;
-       c->bucket_parents = kcalloc(c->max_buckets, sizeof(u32), GFP_NOFS);
-       if (c->bucket_parents == NULL)
-               goto badmem;
-
        c->buckets = kcalloc(c->max_buckets, sizeof(*c->buckets), GFP_NOFS);
        if (c->buckets == NULL)
                goto badmem;
@@ -890,8 +883,12 @@ struct ceph_osdmap *osdmap_apply_incremental(void **p, void *end,
                pglen = ceph_decode_32(p);
 
                if (pglen) {
-                       /* insert */
                        ceph_decode_need(p, end, pglen*sizeof(u32), bad);
+
+                       /* removing existing (if any) */
+                       (void) __remove_pg_mapping(&map->pg_temp, pgid);
+
+                       /* insert */
                        pg = kmalloc(sizeof(*pg) + sizeof(u32)*pglen, GFP_NOFS);
                        if (!pg) {
                                err = -ENOMEM;
@@ -1000,7 +997,6 @@ int ceph_calc_object_layout(struct ceph_object_layout *ol,
 {
        unsigned int num, num_mask;
        struct ceph_pg pgid;
-       s32 preferred = (s32)le32_to_cpu(fl->fl_pg_preferred);
        int poolid = le32_to_cpu(fl->fl_pg_pool);
        struct ceph_pg_pool_info *pool;
        unsigned int ps;
@@ -1011,23 +1007,13 @@ int ceph_calc_object_layout(struct ceph_object_layout *ol,
        if (!pool)
                return -EIO;
        ps = ceph_str_hash(pool->v.object_hash, oid, strlen(oid));
-       if (preferred >= 0) {
-               ps += preferred;
-               num = le32_to_cpu(pool->v.lpg_num);
-               num_mask = pool->lpg_num_mask;
-       } else {
-               num = le32_to_cpu(pool->v.pg_num);
-               num_mask = pool->pg_num_mask;
-       }
+       num = le32_to_cpu(pool->v.pg_num);
+       num_mask = pool->pg_num_mask;
 
        pgid.ps = cpu_to_le16(ps);
-       pgid.preferred = cpu_to_le16(preferred);
+       pgid.preferred = cpu_to_le16(-1);
        pgid.pool = fl->fl_pg_pool;
-       if (preferred >= 0)
-               dout("calc_object_layout '%s' pgid %d.%xp%d\n", oid, poolid, ps,
-                    (int)preferred);
-       else
-               dout("calc_object_layout '%s' pgid %d.%x\n", oid, poolid, ps);
+       dout("calc_object_layout '%s' pgid %d.%x\n", oid, poolid, ps);
 
        ol->ol_pgid = pgid;
        ol->ol_stripe_unit = fl->fl_object_stripe_unit;
@@ -1045,24 +1031,18 @@ static int *calc_pg_raw(struct ceph_osdmap *osdmap, struct ceph_pg pgid,
        struct ceph_pg_mapping *pg;
        struct ceph_pg_pool_info *pool;
        int ruleno;
-       unsigned int poolid, ps, pps, t;
-       int preferred;
+       unsigned int poolid, ps, pps, t, r;
 
        poolid = le32_to_cpu(pgid.pool);
        ps = le16_to_cpu(pgid.ps);
-       preferred = (s16)le16_to_cpu(pgid.preferred);
 
        pool = __lookup_pg_pool(&osdmap->pg_pools, poolid);
        if (!pool)
                return NULL;
 
        /* pg_temp? */
-       if (preferred >= 0)
-               t = ceph_stable_mod(ps, le32_to_cpu(pool->v.lpg_num),
-                                   pool->lpgp_num_mask);
-       else
-               t = ceph_stable_mod(ps, le32_to_cpu(pool->v.pg_num),
-                                   pool->pgp_num_mask);
+       t = ceph_stable_mod(ps, le32_to_cpu(pool->v.pg_num),
+                           pool->pgp_num_mask);
        pgid.ps = cpu_to_le16(t);
        pg = __lookup_pg_mapping(&osdmap->pg_temp, pgid);
        if (pg) {
@@ -1080,23 +1060,20 @@ static int *calc_pg_raw(struct ceph_osdmap *osdmap, struct ceph_pg pgid,
                return NULL;
        }
 
-       /* don't forcefeed bad device ids to crush */
-       if (preferred >= osdmap->max_osd ||
-           preferred >= osdmap->crush->max_devices)
-               preferred = -1;
-
-       if (preferred >= 0)
-               pps = ceph_stable_mod(ps,
-                                     le32_to_cpu(pool->v.lpgp_num),
-                                     pool->lpgp_num_mask);
-       else
-               pps = ceph_stable_mod(ps,
-                                     le32_to_cpu(pool->v.pgp_num),
-                                     pool->pgp_num_mask);
+       pps = ceph_stable_mod(ps,
+                             le32_to_cpu(pool->v.pgp_num),
+                             pool->pgp_num_mask);
        pps += poolid;
-       *num = crush_do_rule(osdmap->crush, ruleno, pps, osds,
-                            min_t(int, pool->v.size, *num),
-                            preferred, osdmap->osd_weight);
+       r = crush_do_rule(osdmap->crush, ruleno, pps, osds,
+                         min_t(int, pool->v.size, *num),
+                         osdmap->osd_weight);
+       if (r < 0) {
+               pr_err("error %d from crush rule: pool %d ruleset %d type %d"
+                      " size %d\n", r, poolid, pool->v.crush_ruleset,
+                      pool->v.type, pool->v.size);
+               return NULL;
+       }
+       *num = r;
        return osds;
 }
 
index 3252e7e0a0055ad07fa1c02979500650703f6820..ea5fb9fcc3f5937777db311ea88a75ae3f4b81f4 100644 (file)
@@ -468,3 +468,4 @@ module_exit(exit_net_drop_monitor);
 
 MODULE_LICENSE("GPL v2");
 MODULE_AUTHOR("Neil Horman <nhorman@tuxdriver.com>");
+MODULE_ALIAS_GENL_FAMILY("NET_DM");
index 89a47b35905dcc6e1c3bb94b0db7c6a32a61e8fc..cb982a61536fade811908a18e6119f513914741e 100644 (file)
@@ -459,28 +459,22 @@ static u32 esp4_get_mtu(struct xfrm_state *x, int mtu)
        struct esp_data *esp = x->data;
        u32 blksize = ALIGN(crypto_aead_blocksize(esp->aead), 4);
        u32 align = max_t(u32, blksize, esp->padlen);
-       u32 rem;
-
-       mtu -= x->props.header_len + crypto_aead_authsize(esp->aead);
-       rem = mtu & (align - 1);
-       mtu &= ~(align - 1);
+       unsigned int net_adj;
 
        switch (x->props.mode) {
-       case XFRM_MODE_TUNNEL:
-               break;
-       default:
        case XFRM_MODE_TRANSPORT:
-               /* The worst case */
-               mtu -= blksize - 4;
-               mtu += min_t(u32, blksize - 4, rem);
-               break;
        case XFRM_MODE_BEET:
-               /* The worst case. */
-               mtu += min_t(u32, IPV4_BEET_PHMAXLEN, rem);
+               net_adj = sizeof(struct iphdr);
                break;
+       case XFRM_MODE_TUNNEL:
+               net_adj = 0;
+               break;
+       default:
+               BUG();
        }
 
-       return mtu - 2;
+       return ((mtu - x->props.header_len - crypto_aead_authsize(esp->aead) -
+                net_adj) & ~(align - 1)) + (net_adj - 2);
 }
 
 static void esp4_err(struct sk_buff *skb, u32 info)
index 151703791bb0d43818700349fb0a585736c7dc19..b6f3583ddfe83eae73370c9070c567e452518f57 100644 (file)
@@ -74,9 +74,6 @@ void tcp_destroy_cgroup(struct mem_cgroup *memcg)
        percpu_counter_destroy(&tcp->tcp_sockets_allocated);
 
        val = res_counter_read_u64(&tcp->tcp_memory_allocated, RES_LIMIT);
-
-       if (val != RESOURCE_MAX)
-               static_key_slow_dec(&memcg_socket_limit_enabled);
 }
 EXPORT_SYMBOL(tcp_destroy_cgroup);
 
@@ -107,10 +104,33 @@ static int tcp_update_limit(struct mem_cgroup *memcg, u64 val)
                tcp->tcp_prot_mem[i] = min_t(long, val >> PAGE_SHIFT,
                                             net->ipv4.sysctl_tcp_mem[i]);
 
-       if (val == RESOURCE_MAX && old_lim != RESOURCE_MAX)
-               static_key_slow_dec(&memcg_socket_limit_enabled);
-       else if (old_lim == RESOURCE_MAX && val != RESOURCE_MAX)
-               static_key_slow_inc(&memcg_socket_limit_enabled);
+       if (val == RESOURCE_MAX)
+               clear_bit(MEMCG_SOCK_ACTIVE, &cg_proto->flags);
+       else if (val != RESOURCE_MAX) {
+               /*
+                * The active bit needs to be written after the static_key
+                * update. This is what guarantees that the socket activation
+                * function is the last one to run. See sock_update_memcg() for
+                * details, and note that we don't mark any socket as belonging
+                * to this memcg until that flag is up.
+                *
+                * We need to do this, because static_keys will span multiple
+                * sites, but we can't control their order. If we mark a socket
+                * as accounted, but the accounting functions are not patched in
+                * yet, we'll lose accounting.
+                *
+                * We never race with the readers in sock_update_memcg(),
+                * because when this value change, the code to process it is not
+                * patched in yet.
+                *
+                * The activated bit is used to guarantee that no two writers
+                * will do the update in the same memcg. Without that, we can't
+                * properly shutdown the static key.
+                */
+               if (!test_and_set_bit(MEMCG_SOCK_ACTIVATED, &cg_proto->flags))
+                       static_key_slow_inc(&memcg_socket_limit_enabled);
+               set_bit(MEMCG_SOCK_ACTIVE, &cg_proto->flags);
+       }
 
        return 0;
 }
index 1e62b7557b00e1e0897f480d9391d018dfd01dcb..db1521fcda5b3fd182a3068c9b86d5161e5e4d56 100644 (file)
@@ -413,19 +413,15 @@ static u32 esp6_get_mtu(struct xfrm_state *x, int mtu)
        struct esp_data *esp = x->data;
        u32 blksize = ALIGN(crypto_aead_blocksize(esp->aead), 4);
        u32 align = max_t(u32, blksize, esp->padlen);
-       u32 rem;
+       unsigned int net_adj;
 
-       mtu -= x->props.header_len + crypto_aead_authsize(esp->aead);
-       rem = mtu & (align - 1);
-       mtu &= ~(align - 1);
-
-       if (x->props.mode != XFRM_MODE_TUNNEL) {
-               u32 padsize = ((blksize - 1) & 7) + 1;
-               mtu -= blksize - padsize;
-               mtu += min_t(u32, blksize - padsize, rem);
-       }
+       if (x->props.mode != XFRM_MODE_TUNNEL)
+               net_adj = sizeof(struct ipv6hdr);
+       else
+               net_adj = 0;
 
-       return mtu - 2;
+       return ((mtu - x->props.header_len - crypto_aead_authsize(esp->aead) -
+                net_adj) & ~(align - 1)) + (net_adj - 2);
 }
 
 static void esp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
index d99fdc699625ca34252a33e84e21d8179e0fad7a..17b8c67998bb80dc5e7052af210c7b64aa1471ee 100644 (file)
@@ -1187,6 +1187,29 @@ static inline struct ipv6_rt_hdr *ip6_rthdr_dup(struct ipv6_rt_hdr *src,
        return src ? kmemdup(src, (src->hdrlen + 1) * 8, gfp) : NULL;
 }
 
+static void ip6_append_data_mtu(int *mtu,
+                               int *maxfraglen,
+                               unsigned int fragheaderlen,
+                               struct sk_buff *skb,
+                               struct rt6_info *rt)
+{
+       if (!(rt->dst.flags & DST_XFRM_TUNNEL)) {
+               if (skb == NULL) {
+                       /* first fragment, reserve header_len */
+                       *mtu = *mtu - rt->dst.header_len;
+
+               } else {
+                       /*
+                        * this fragment is not first, the headers
+                        * space is regarded as data space.
+                        */
+                       *mtu = dst_mtu(rt->dst.path);
+               }
+               *maxfraglen = ((*mtu - fragheaderlen) & ~7)
+                             + fragheaderlen - sizeof(struct frag_hdr);
+       }
+}
+
 int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
        int offset, int len, int odd, struct sk_buff *skb),
        void *from, int length, int transhdrlen,
@@ -1196,7 +1219,7 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
        struct inet_sock *inet = inet_sk(sk);
        struct ipv6_pinfo *np = inet6_sk(sk);
        struct inet_cork *cork;
-       struct sk_buff *skb;
+       struct sk_buff *skb, *skb_prev = NULL;
        unsigned int maxfraglen, fragheaderlen;
        int exthdrlen;
        int dst_exthdrlen;
@@ -1253,8 +1276,12 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
                inet->cork.fl.u.ip6 = *fl6;
                np->cork.hop_limit = hlimit;
                np->cork.tclass = tclass;
-               mtu = np->pmtudisc == IPV6_PMTUDISC_PROBE ?
-                     rt->dst.dev->mtu : dst_mtu(&rt->dst);
+               if (rt->dst.flags & DST_XFRM_TUNNEL)
+                       mtu = np->pmtudisc == IPV6_PMTUDISC_PROBE ?
+                             rt->dst.dev->mtu : dst_mtu(&rt->dst);
+               else
+                       mtu = np->pmtudisc == IPV6_PMTUDISC_PROBE ?
+                             rt->dst.dev->mtu : dst_mtu(rt->dst.path);
                if (np->frag_size < mtu) {
                        if (np->frag_size)
                                mtu = np->frag_size;
@@ -1350,25 +1377,27 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
                        unsigned int fraglen;
                        unsigned int fraggap;
                        unsigned int alloclen;
-                       struct sk_buff *skb_prev;
 alloc_new_skb:
-                       skb_prev = skb;
-
                        /* There's no room in the current skb */
-                       if (skb_prev)
-                               fraggap = skb_prev->len - maxfraglen;
+                       if (skb)
+                               fraggap = skb->len - maxfraglen;
                        else
                                fraggap = 0;
+                       /* update mtu and maxfraglen if necessary */
+                       if (skb == NULL || skb_prev == NULL)
+                               ip6_append_data_mtu(&mtu, &maxfraglen,
+                                                   fragheaderlen, skb, rt);
+
+                       skb_prev = skb;
 
                        /*
                         * If remaining data exceeds the mtu,
                         * we know we need more fragment(s).
                         */
                        datalen = length + fraggap;
-                       if (datalen > (cork->length <= mtu && !(cork->flags & IPCORK_ALLFRAG) ? mtu : maxfraglen) - fragheaderlen)
-                               datalen = maxfraglen - fragheaderlen;
 
-                       fraglen = datalen + fragheaderlen;
+                       if (datalen > (cork->length <= mtu && !(cork->flags & IPCORK_ALLFRAG) ? mtu : maxfraglen) - fragheaderlen)
+                               datalen = maxfraglen - fragheaderlen - rt->dst.trailer_len;
                        if ((flags & MSG_MORE) &&
                            !(rt->dst.dev->features&NETIF_F_SG))
                                alloclen = mtu;
@@ -1377,13 +1406,16 @@ alloc_new_skb:
 
                        alloclen += dst_exthdrlen;
 
-                       /*
-                        * The last fragment gets additional space at tail.
-                        * Note: we overallocate on fragments with MSG_MODE
-                        * because we have no idea if we're the last one.
-                        */
-                       if (datalen == length + fraggap)
-                               alloclen += rt->dst.trailer_len;
+                       if (datalen != length + fraggap) {
+                               /*
+                                * this is not the last fragment, the trailer
+                                * space is regarded as data space.
+                                */
+                               datalen += rt->dst.trailer_len;
+                       }
+
+                       alloclen += rt->dst.trailer_len;
+                       fraglen = datalen + fragheaderlen;
 
                        /*
                         * We just reserve space for fragment header.
index 889f5d13d7ba342b5ea2a2c447b1c6858b553de2..70614e7affabded003aef1bf5ed4c097d4b515fa 100644 (file)
@@ -239,9 +239,16 @@ static int l2tp_ip_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
 {
        struct inet_sock *inet = inet_sk(sk);
        struct sockaddr_l2tpip *addr = (struct sockaddr_l2tpip *) uaddr;
-       int ret = -EINVAL;
+       int ret;
        int chk_addr_ret;
 
+       if (!sock_flag(sk, SOCK_ZAPPED))
+               return -EINVAL;
+       if (addr_len < sizeof(struct sockaddr_l2tpip))
+               return -EINVAL;
+       if (addr->l2tp_family != AF_INET)
+               return -EINVAL;
+
        ret = -EADDRINUSE;
        read_lock_bh(&l2tp_ip_lock);
        if (__l2tp_ip_bind_lookup(&init_net, addr->l2tp_addr.s_addr, sk->sk_bound_dev_if, addr->l2tp_conn_id))
@@ -272,6 +279,8 @@ static int l2tp_ip_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
        sk_del_node_init(sk);
        write_unlock_bh(&l2tp_ip_lock);
        ret = 0;
+       sock_reset_flag(sk, SOCK_ZAPPED);
+
 out:
        release_sock(sk);
 
@@ -288,6 +297,9 @@ static int l2tp_ip_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len
        struct sockaddr_l2tpip *lsa = (struct sockaddr_l2tpip *) uaddr;
        int rc;
 
+       if (sock_flag(sk, SOCK_ZAPPED)) /* Must bind first - autobinding does not work */
+               return -EINVAL;
+
        if (addr_len < sizeof(*lsa))
                return -EINVAL;
 
@@ -311,6 +323,14 @@ static int l2tp_ip_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len
        return rc;
 }
 
+static int l2tp_ip_disconnect(struct sock *sk, int flags)
+{
+       if (sock_flag(sk, SOCK_ZAPPED))
+               return 0;
+
+       return udp_disconnect(sk, flags);
+}
+
 static int l2tp_ip_getname(struct socket *sock, struct sockaddr *uaddr,
                           int *uaddr_len, int peer)
 {
@@ -530,7 +550,7 @@ static struct proto l2tp_ip_prot = {
        .close             = l2tp_ip_close,
        .bind              = l2tp_ip_bind,
        .connect           = l2tp_ip_connect,
-       .disconnect        = udp_disconnect,
+       .disconnect        = l2tp_ip_disconnect,
        .ioctl             = udp_ioctl,
        .destroy           = l2tp_ip_destroy_sock,
        .setsockopt        = ip_setsockopt,
index 0291d8d85f302f3a244a13c0d6a6a6465137882e..35e1e4bde58730d8395e2870d552230bca3a9c3d 100644 (file)
@@ -258,6 +258,10 @@ static int l2tp_ip6_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
        int addr_type;
        int err;
 
+       if (!sock_flag(sk, SOCK_ZAPPED))
+               return -EINVAL;
+       if (addr->l2tp_family != AF_INET6)
+               return -EINVAL;
        if (addr_len < sizeof(*addr))
                return -EINVAL;
 
@@ -331,6 +335,7 @@ static int l2tp_ip6_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
        sk_del_node_init(sk);
        write_unlock_bh(&l2tp_ip6_lock);
 
+       sock_reset_flag(sk, SOCK_ZAPPED);
        release_sock(sk);
        return 0;
 
@@ -354,6 +359,9 @@ static int l2tp_ip6_connect(struct sock *sk, struct sockaddr *uaddr,
        int     addr_type;
        int rc;
 
+       if (sock_flag(sk, SOCK_ZAPPED)) /* Must bind first - autobinding does not work */
+               return -EINVAL;
+
        if (addr_len < sizeof(*lsa))
                return -EINVAL;
 
@@ -383,6 +391,14 @@ static int l2tp_ip6_connect(struct sock *sk, struct sockaddr *uaddr,
        return rc;
 }
 
+static int l2tp_ip6_disconnect(struct sock *sk, int flags)
+{
+       if (sock_flag(sk, SOCK_ZAPPED))
+               return 0;
+
+       return udp_disconnect(sk, flags);
+}
+
 static int l2tp_ip6_getname(struct socket *sock, struct sockaddr *uaddr,
                            int *uaddr_len, int peer)
 {
@@ -689,7 +705,7 @@ static struct proto l2tp_ip6_prot = {
        .close             = l2tp_ip6_close,
        .bind              = l2tp_ip6_bind,
        .connect           = l2tp_ip6_connect,
-       .disconnect        = udp_disconnect,
+       .disconnect        = l2tp_ip6_disconnect,
        .ioctl             = udp_ioctl,
        .destroy           = l2tp_ip6_destroy_sock,
        .setsockopt        = ipv6_setsockopt,
index 8577264378fe0a88e6b3aadcdc6d96333782aa95..ddc553e76671bae0eac8dec5ace2e63035c45869 100644 (file)
@@ -923,5 +923,4 @@ MODULE_AUTHOR("James Chapman <jchapman@katalix.com>");
 MODULE_DESCRIPTION("L2TP netlink");
 MODULE_LICENSE("GPL");
 MODULE_VERSION("1.0");
-MODULE_ALIAS("net-pf-" __stringify(PF_NETLINK) "-proto-" \
-            __stringify(NETLINK_GENERIC) "-type-" "l2tp");
+MODULE_ALIAS_GENL_FAMILY("l2tp");
index b3b3c264ff66b970beac9b5090cd086610266419..04c3063089874fa7c8eb5ded233be3e5d7b86502 100644 (file)
@@ -1522,6 +1522,8 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata)
         * anymore. The timeout will be reset if the frame is ACKed by
         * the AP.
         */
+       ifmgd->probe_send_count++;
+
        if (sdata->local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) {
                ifmgd->nullfunc_failed = false;
                ieee80211_send_nullfunc(sdata->local, sdata, 0);
@@ -1538,7 +1540,6 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata)
                                         0, (u32) -1, true, false);
        }
 
-       ifmgd->probe_send_count++;
        ifmgd->probe_timeout = jiffies + msecs_to_jiffies(probe_wait_ms);
        run_again(ifmgd, ifmgd->probe_timeout);
        if (sdata->local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)
index 5f827a6b0d8d1f7c494424be701a95ff1b6bf701..847215bb2a6fc63c1ed010dbfc9bd2d35cb6b7c0 100644 (file)
@@ -153,7 +153,7 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx,
 
        /* Don't calculate ACKs for QoS Frames with NoAck Policy set */
        if (ieee80211_is_data_qos(hdr->frame_control) &&
-           *(ieee80211_get_qos_ctl(hdr)) | IEEE80211_QOS_CTL_ACK_POLICY_NOACK)
+           *(ieee80211_get_qos_ctl(hdr)) & IEEE80211_QOS_CTL_ACK_POLICY_NOACK)
                dur = 0;
        else
                /* Time needed to transmit ACK
index 22f2216b397ea37b5845efc359c7d0e519261aec..a44c6807df01914a04c5675d1422d765260a8c29 100644 (file)
@@ -1371,6 +1371,12 @@ int ieee80211_reconfig(struct ieee80211_local *local)
                }
        }
 
+       /* add back keys */
+       list_for_each_entry(sdata, &local->interfaces, list)
+               if (ieee80211_sdata_running(sdata))
+                       ieee80211_enable_keys(sdata);
+
+ wake_up:
        /*
         * Clear the WLAN_STA_BLOCK_BA flag so new aggregation
         * sessions can be established after a resume.
@@ -1392,12 +1398,6 @@ int ieee80211_reconfig(struct ieee80211_local *local)
                mutex_unlock(&local->sta_mtx);
        }
 
-       /* add back keys */
-       list_for_each_entry(sdata, &local->interfaces, list)
-               if (ieee80211_sdata_running(sdata))
-                       ieee80211_enable_keys(sdata);
-
- wake_up:
        ieee80211_wake_queues_by_reason(hw,
                        IEEE80211_QUEUE_STOP_REASON_SUSPEND);
 
index 8340ace837f2eb309a707d84a8d71da0fa282bbd..2cc7c1ee769046c1b45ce677caa6f8dac3e4a1ff 100644 (file)
@@ -836,7 +836,7 @@ static int ctrl_getfamily(struct sk_buff *skb, struct genl_info *info)
 #ifdef CONFIG_MODULES
                if (res == NULL) {
                        genl_unlock();
-                       request_module("net-pf-%d-proto-%d-type-%s",
+                       request_module("net-pf-%d-proto-%d-family-%s",
                                       PF_NETLINK, NETLINK_GENERIC, name);
                        genl_lock();
                        res = genl_family_find_byname(name);
index edfaaaf164ebfab0eebda8b509d91fed1735daee..8d2b3d5a7c21e5ffb2063621a569e1f6280f52ad 100644 (file)
@@ -186,8 +186,7 @@ struct rds_ib_device {
        struct work_struct      free_work;
 };
 
-#define pcidev_to_node(pcidev) pcibus_to_node(pcidev->bus)
-#define ibdev_to_node(ibdev) pcidev_to_node(to_pci_dev(ibdev->dma_device))
+#define ibdev_to_node(ibdev) dev_to_node(ibdev->dma_device)
 #define rdsibdev_to_node(rdsibdev) ibdev_to_node(rdsibdev->dev)
 
 /* bits for i_ack_flags */
index 8522a4793374136fa4ab66aa9b325b48019801ac..ca8e0a57d945dabeb51147f338cef363672040d5 100644 (file)
@@ -16,8 +16,6 @@
 #include <net/netlink.h>
 #include <net/pkt_sched.h>
 
-extern struct socket *sockfd_lookup(int fd, int *err); /* @@@ fix this */
-
 /*
  * The ATM queuing discipline provides a framework for invoking classifiers
  * (aka "filters"), which in turn select classes of this queuing discipline.
index 38f388c39dce89a5e6456514771f70ef975af1c0..107c4528654fd5867b8363ccdf66c648e9202a34 100644 (file)
@@ -381,21 +381,53 @@ gss_unwrap_kerberos_v1(struct krb5_ctx *kctx, int offset, struct xdr_buf *buf)
 }
 
 /*
- * We cannot currently handle tokens with rotated data.  We need a
- * generalized routine to rotate the data in place.  It is anticipated
- * that we won't encounter rotated data in the general case.
+ * We can shift data by up to LOCAL_BUF_LEN bytes in a pass.  If we need
+ * to do more than that, we shift repeatedly.  Kevin Coffman reports
+ * seeing 28 bytes as the value used by Microsoft clients and servers
+ * with AES, so this constant is chosen to allow handling 28 in one pass
+ * without using too much stack space.
+ *
+ * If that proves to a problem perhaps we could use a more clever
+ * algorithm.
  */
-static u32
-rotate_left(struct krb5_ctx *kctx, u32 offset, struct xdr_buf *buf, u16 rrc)
+#define LOCAL_BUF_LEN 32u
+
+static void rotate_buf_a_little(struct xdr_buf *buf, unsigned int shift)
 {
-       unsigned int realrrc = rrc % (buf->len - offset - GSS_KRB5_TOK_HDR_LEN);
+       char head[LOCAL_BUF_LEN];
+       char tmp[LOCAL_BUF_LEN];
+       unsigned int this_len, i;
+
+       BUG_ON(shift > LOCAL_BUF_LEN);
 
-       if (realrrc == 0)
-               return 0;
+       read_bytes_from_xdr_buf(buf, 0, head, shift);
+       for (i = 0; i + shift < buf->len; i += LOCAL_BUF_LEN) {
+               this_len = min(LOCAL_BUF_LEN, buf->len - (i + shift));
+               read_bytes_from_xdr_buf(buf, i+shift, tmp, this_len);
+               write_bytes_to_xdr_buf(buf, i, tmp, this_len);
+       }
+       write_bytes_to_xdr_buf(buf, buf->len - shift, head, shift);
+}
 
-       dprintk("%s: cannot process token with rotated data: "
-               "rrc %u, realrrc %u\n", __func__, rrc, realrrc);
-       return 1;
+static void _rotate_left(struct xdr_buf *buf, unsigned int shift)
+{
+       int shifted = 0;
+       int this_shift;
+
+       shift %= buf->len;
+       while (shifted < shift) {
+               this_shift = min(shift - shifted, LOCAL_BUF_LEN);
+               rotate_buf_a_little(buf, this_shift);
+               shifted += this_shift;
+       }
+}
+
+static void rotate_left(u32 base, struct xdr_buf *buf, unsigned int shift)
+{
+       struct xdr_buf subbuf;
+
+       xdr_buf_subsegment(buf, &subbuf, base, buf->len - base);
+       _rotate_left(&subbuf, shift);
 }
 
 static u32
@@ -495,11 +527,8 @@ gss_unwrap_kerberos_v2(struct krb5_ctx *kctx, int offset, struct xdr_buf *buf)
 
        seqnum = be64_to_cpup((__be64 *)(ptr + 8));
 
-       if (rrc != 0) {
-               err = rotate_left(kctx, offset, buf, rrc);
-               if (err)
-                       return GSS_S_FAILURE;
-       }
+       if (rrc != 0)
+               rotate_left(offset + 16, buf, rrc);
 
        err = (*kctx->gk5e->decrypt_v2)(kctx, offset, buf,
                                        &headskip, &tailskip);
index 28b62dbb6d1e4be36358055a9c231143f0e45d9e..73e95738660042e7a9d4e7cb252143ec74078265 100644 (file)
@@ -336,7 +336,6 @@ struct rsc {
        struct svc_cred         cred;
        struct gss_svc_seq_data seqdata;
        struct gss_ctx          *mechctx;
-       char                    *client_name;
 };
 
 static struct rsc *rsc_update(struct cache_detail *cd, struct rsc *new, struct rsc *old);
@@ -347,9 +346,7 @@ static void rsc_free(struct rsc *rsci)
        kfree(rsci->handle.data);
        if (rsci->mechctx)
                gss_delete_sec_context(&rsci->mechctx);
-       if (rsci->cred.cr_group_info)
-               put_group_info(rsci->cred.cr_group_info);
-       kfree(rsci->client_name);
+       free_svc_cred(&rsci->cred);
 }
 
 static void rsc_put(struct kref *ref)
@@ -387,7 +384,7 @@ rsc_init(struct cache_head *cnew, struct cache_head *ctmp)
        tmp->handle.data = NULL;
        new->mechctx = NULL;
        new->cred.cr_group_info = NULL;
-       new->client_name = NULL;
+       new->cred.cr_principal = NULL;
 }
 
 static void
@@ -402,8 +399,8 @@ update_rsc(struct cache_head *cnew, struct cache_head *ctmp)
        spin_lock_init(&new->seqdata.sd_lock);
        new->cred = tmp->cred;
        tmp->cred.cr_group_info = NULL;
-       new->client_name = tmp->client_name;
-       tmp->client_name = NULL;
+       new->cred.cr_principal = tmp->cred.cr_principal;
+       tmp->cred.cr_principal = NULL;
 }
 
 static struct cache_head *
@@ -501,8 +498,8 @@ static int rsc_parse(struct cache_detail *cd,
                /* get client name */
                len = qword_get(&mesg, buf, mlen);
                if (len > 0) {
-                       rsci.client_name = kstrdup(buf, GFP_KERNEL);
-                       if (!rsci.client_name)
+                       rsci.cred.cr_principal = kstrdup(buf, GFP_KERNEL);
+                       if (!rsci.cred.cr_principal)
                                goto out;
                }
 
@@ -932,16 +929,6 @@ struct gss_svc_data {
        struct rsc                      *rsci;
 };
 
-char *svc_gss_principal(struct svc_rqst *rqstp)
-{
-       struct gss_svc_data *gd = (struct gss_svc_data *)rqstp->rq_auth_data;
-
-       if (gd && gd->rsci)
-               return gd->rsci->client_name;
-       return NULL;
-}
-EXPORT_SYMBOL_GPL(svc_gss_principal);
-
 static int
 svcauth_gss_set_client(struct svc_rqst *rqstp)
 {
@@ -969,16 +956,17 @@ svcauth_gss_set_client(struct svc_rqst *rqstp)
 }
 
 static inline int
-gss_write_init_verf(struct cache_detail *cd, struct svc_rqst *rqstp, struct rsi *rsip)
+gss_write_init_verf(struct cache_detail *cd, struct svc_rqst *rqstp,
+               struct xdr_netobj *out_handle, int *major_status)
 {
        struct rsc *rsci;
        int        rc;
 
-       if (rsip->major_status != GSS_S_COMPLETE)
+       if (*major_status != GSS_S_COMPLETE)
                return gss_write_null_verf(rqstp);
-       rsci = gss_svc_searchbyctx(cd, &rsip->out_handle);
+       rsci = gss_svc_searchbyctx(cd, out_handle);
        if (rsci == NULL) {
-               rsip->major_status = GSS_S_NO_CONTEXT;
+               *major_status = GSS_S_NO_CONTEXT;
                return gss_write_null_verf(rqstp);
        }
        rc = gss_write_verf(rqstp, rsci->mechctx, GSS_SEQ_WIN);
@@ -986,22 +974,13 @@ gss_write_init_verf(struct cache_detail *cd, struct svc_rqst *rqstp, struct rsi
        return rc;
 }
 
-/*
- * Having read the cred already and found we're in the context
- * initiation case, read the verifier and initiate (or check the results
- * of) upcalls to userspace for help with context initiation.  If
- * the upcall results are available, write the verifier and result.
- * Otherwise, drop the request pending an answer to the upcall.
- */
-static int svcauth_gss_handle_init(struct svc_rqst *rqstp,
-                       struct rpc_gss_wire_cred *gc, __be32 *authp)
+static inline int
+gss_read_verf(struct rpc_gss_wire_cred *gc,
+             struct kvec *argv, __be32 *authp,
+             struct xdr_netobj *in_handle,
+             struct xdr_netobj *in_token)
 {
-       struct kvec *argv = &rqstp->rq_arg.head[0];
-       struct kvec *resv = &rqstp->rq_res.head[0];
        struct xdr_netobj tmpobj;
-       struct rsi *rsip, rsikey;
-       int ret;
-       struct sunrpc_net *sn = net_generic(rqstp->rq_xprt->xpt_net, sunrpc_net_id);
 
        /* Read the verifier; should be NULL: */
        *authp = rpc_autherr_badverf;
@@ -1011,24 +990,67 @@ static int svcauth_gss_handle_init(struct svc_rqst *rqstp,
                return SVC_DENIED;
        if (svc_getnl(argv) != 0)
                return SVC_DENIED;
-
        /* Martial context handle and token for upcall: */
        *authp = rpc_autherr_badcred;
        if (gc->gc_proc == RPC_GSS_PROC_INIT && gc->gc_ctx.len != 0)
                return SVC_DENIED;
-       memset(&rsikey, 0, sizeof(rsikey));
-       if (dup_netobj(&rsikey.in_handle, &gc->gc_ctx))
+       if (dup_netobj(in_handle, &gc->gc_ctx))
                return SVC_CLOSE;
        *authp = rpc_autherr_badverf;
        if (svc_safe_getnetobj(argv, &tmpobj)) {
-               kfree(rsikey.in_handle.data);
+               kfree(in_handle->data);
                return SVC_DENIED;
        }
-       if (dup_netobj(&rsikey.in_token, &tmpobj)) {
-               kfree(rsikey.in_handle.data);
+       if (dup_netobj(in_token, &tmpobj)) {
+               kfree(in_handle->data);
                return SVC_CLOSE;
        }
 
+       return 0;
+}
+
+static inline int
+gss_write_resv(struct kvec *resv, size_t size_limit,
+              struct xdr_netobj *out_handle, struct xdr_netobj *out_token,
+              int major_status, int minor_status)
+{
+       if (resv->iov_len + 4 > size_limit)
+               return -1;
+       svc_putnl(resv, RPC_SUCCESS);
+       if (svc_safe_putnetobj(resv, out_handle))
+               return -1;
+       if (resv->iov_len + 3 * 4 > size_limit)
+               return -1;
+       svc_putnl(resv, major_status);
+       svc_putnl(resv, minor_status);
+       svc_putnl(resv, GSS_SEQ_WIN);
+       if (svc_safe_putnetobj(resv, out_token))
+               return -1;
+       return 0;
+}
+
+/*
+ * Having read the cred already and found we're in the context
+ * initiation case, read the verifier and initiate (or check the results
+ * of) upcalls to userspace for help with context initiation.  If
+ * the upcall results are available, write the verifier and result.
+ * Otherwise, drop the request pending an answer to the upcall.
+ */
+static int svcauth_gss_handle_init(struct svc_rqst *rqstp,
+                       struct rpc_gss_wire_cred *gc, __be32 *authp)
+{
+       struct kvec *argv = &rqstp->rq_arg.head[0];
+       struct kvec *resv = &rqstp->rq_res.head[0];
+       struct rsi *rsip, rsikey;
+       int ret;
+       struct sunrpc_net *sn = net_generic(rqstp->rq_xprt->xpt_net, sunrpc_net_id);
+
+       memset(&rsikey, 0, sizeof(rsikey));
+       ret = gss_read_verf(gc, argv, authp,
+                           &rsikey.in_handle, &rsikey.in_token);
+       if (ret)
+               return ret;
+
        /* Perform upcall, or find upcall result: */
        rsip = rsi_lookup(sn->rsi_cache, &rsikey);
        rsi_free(&rsikey);
@@ -1040,19 +1062,12 @@ static int svcauth_gss_handle_init(struct svc_rqst *rqstp,
 
        ret = SVC_CLOSE;
        /* Got an answer to the upcall; use it: */
-       if (gss_write_init_verf(sn->rsc_cache, rqstp, rsip))
-               goto out;
-       if (resv->iov_len + 4 > PAGE_SIZE)
+       if (gss_write_init_verf(sn->rsc_cache, rqstp,
+                               &rsip->out_handle, &rsip->major_status))
                goto out;
-       svc_putnl(resv, RPC_SUCCESS);
-       if (svc_safe_putnetobj(resv, &rsip->out_handle))
-               goto out;
-       if (resv->iov_len + 3 * 4 > PAGE_SIZE)
-               goto out;
-       svc_putnl(resv, rsip->major_status);
-       svc_putnl(resv, rsip->minor_status);
-       svc_putnl(resv, GSS_SEQ_WIN);
-       if (svc_safe_putnetobj(resv, &rsip->out_token))
+       if (gss_write_resv(resv, PAGE_SIZE,
+                          &rsip->out_handle, &rsip->out_token,
+                          rsip->major_status, rsip->minor_status))
                goto out;
 
        ret = SVC_COMPLETE;
@@ -1192,7 +1207,7 @@ svcauth_gss_accept(struct svc_rqst *rqstp, __be32 *authp)
                }
                svcdata->rsci = rsci;
                cache_get(&rsci->h);
-               rqstp->rq_flavor = gss_svc_to_pseudoflavor(
+               rqstp->rq_cred.cr_flavor = gss_svc_to_pseudoflavor(
                                        rsci->mechctx->mech_type, gc->gc_svc);
                ret = SVC_OK;
                goto out;
index 7fee13b331d193e1a46831c257ffd6fcf6a4fde8..f56f045778aedf4a0da1fcf2566eacf69c7c6c8a 100644 (file)
@@ -1286,6 +1286,8 @@ call_reserveresult(struct rpc_task *task)
        }
 
        switch (status) {
+       case -ENOMEM:
+               rpc_delay(task, HZ >> 2);
        case -EAGAIN:   /* woken up; retry */
                task->tk_action = call_reserve;
                return;
index fd2423991c2d4dc473223b128d9a761b3da5beea..04040476082e6efd5ef08f9c7e6444c0fec77929 100644 (file)
@@ -120,7 +120,7 @@ EXPORT_SYMBOL_GPL(rpc_pipe_generic_upcall);
 
 /**
  * rpc_queue_upcall - queue an upcall message to userspace
- * @inode: inode of upcall pipe on which to queue given message
+ * @pipe: upcall pipe on which to queue given message
  * @msg: message to queue
  *
  * Call with an @inode created by rpc_mkpipe() to queue an upcall.
@@ -819,9 +819,7 @@ static int rpc_rmdir_depopulate(struct dentry *dentry,
  * @parent: dentry of directory to create new "pipe" in
  * @name: name of pipe
  * @private: private data to associate with the pipe, for the caller's use
- * @ops: operations defining the behavior of the pipe: upcall, downcall,
- *     release_pipe, open_pipe, and destroy_msg.
- * @flags: rpc_pipe flags
+ * @pipe: &rpc_pipe containing input parameters
  *
  * Data is made available for userspace to read by calls to
  * rpc_queue_upcall().  The actual reads will result in calls to
@@ -943,7 +941,7 @@ struct dentry *rpc_create_client_dir(struct dentry *dentry,
 
 /**
  * rpc_remove_client_dir - Remove a directory created with rpc_create_client_dir()
- * @clnt: rpc client
+ * @dentry: dentry for the pipe
  */
 int rpc_remove_client_dir(struct dentry *dentry)
 {
@@ -1115,7 +1113,7 @@ rpc_fill_super(struct super_block *sb, void *data, int silent)
        sb->s_op = &s_ops;
        sb->s_time_gran = 1;
 
-       inode = rpc_get_inode(sb, S_IFDIR | 0755);
+       inode = rpc_get_inode(sb, S_IFDIR | S_IRUGO | S_IXUGO);
        sb->s_root = root = d_make_root(inode);
        if (!root)
                return -ENOMEM;
index 78ac39fd9fe7556a71dc361406904cf8bb32d7b2..92509ffe15fcacce5de331cbb205a84c4f718a86 100644 (file)
@@ -180,14 +180,16 @@ void rpcb_put_local(struct net *net)
        struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
        struct rpc_clnt *clnt = sn->rpcb_local_clnt;
        struct rpc_clnt *clnt4 = sn->rpcb_local_clnt4;
-       int shutdown;
+       int shutdown = 0;
 
        spin_lock(&sn->rpcb_clnt_lock);
-       if (--sn->rpcb_users == 0) {
-               sn->rpcb_local_clnt = NULL;
-               sn->rpcb_local_clnt4 = NULL;
+       if (sn->rpcb_users) {
+               if (--sn->rpcb_users == 0) {
+                       sn->rpcb_local_clnt = NULL;
+                       sn->rpcb_local_clnt4 = NULL;
+               }
+               shutdown = !sn->rpcb_users;
        }
-       shutdown = !sn->rpcb_users;
        spin_unlock(&sn->rpcb_clnt_lock);
 
        if (shutdown) {
@@ -394,6 +396,7 @@ static int rpcb_register_call(struct rpc_clnt *clnt, struct rpc_message *msg)
 
 /**
  * rpcb_register - set or unset a port registration with the local rpcbind svc
+ * @net: target network namespace
  * @prog: RPC program number to bind
  * @vers: RPC version number to bind
  * @prot: transport protocol to register
@@ -521,6 +524,7 @@ static int rpcb_unregister_all_protofamilies(struct sunrpc_net *sn,
 
 /**
  * rpcb_v4_register - set or unset a port registration with the local rpcbind
+ * @net: target network namespace
  * @program: RPC program number of service to (un)register
  * @version: RPC version number of service to (un)register
  * @address: address family, IP address, and port to (un)register
index 017c0117d1543a784dfe5130396c74f80879131a..7e9baaa1e543e55878dcb0d9bd0378a0e51754e0 100644 (file)
@@ -407,6 +407,14 @@ static int svc_uses_rpcbind(struct svc_serv *serv)
        return 0;
 }
 
+int svc_bind(struct svc_serv *serv, struct net *net)
+{
+       if (!svc_uses_rpcbind(serv))
+               return 0;
+       return svc_rpcb_setup(serv, net);
+}
+EXPORT_SYMBOL_GPL(svc_bind);
+
 /*
  * Create an RPC service
  */
@@ -471,15 +479,8 @@ __svc_create(struct svc_program *prog, unsigned int bufsize, int npools,
                spin_lock_init(&pool->sp_lock);
        }
 
-       if (svc_uses_rpcbind(serv)) {
-               if (svc_rpcb_setup(serv, current->nsproxy->net_ns) < 0) {
-                       kfree(serv->sv_pools);
-                       kfree(serv);
-                       return NULL;
-               }
-               if (!serv->sv_shutdown)
-                       serv->sv_shutdown = svc_rpcb_cleanup;
-       }
+       if (svc_uses_rpcbind(serv) && (!serv->sv_shutdown))
+               serv->sv_shutdown = svc_rpcb_cleanup;
 
        return serv;
 }
@@ -536,8 +537,6 @@ EXPORT_SYMBOL_GPL(svc_shutdown_net);
 void
 svc_destroy(struct svc_serv *serv)
 {
-       struct net *net = current->nsproxy->net_ns;
-
        dprintk("svc: svc_destroy(%s, %d)\n",
                                serv->sv_program->pg_name,
                                serv->sv_nrthreads);
@@ -552,8 +551,6 @@ svc_destroy(struct svc_serv *serv)
 
        del_timer_sync(&serv->sv_temptimer);
 
-       svc_shutdown_net(serv, net);
-
        /*
         * The last user is gone and thus all sockets have to be destroyed to
         * the point. Check this.
index b98ee35149121602b42ace9365bfd5f21e84767a..88f2bf671960d444e73d3d9eba2998f75ac2885b 100644 (file)
@@ -598,6 +598,7 @@ int svc_recv(struct svc_rqst *rqstp, long timeout)
 
        /* now allocate needed pages.  If we get a failure, sleep briefly */
        pages = (serv->sv_max_mesg + PAGE_SIZE) / PAGE_SIZE;
+       BUG_ON(pages >= RPCSVC_MAXPAGES);
        for (i = 0; i < pages ; i++)
                while (rqstp->rq_pages[i] == NULL) {
                        struct page *p = alloc_page(GFP_KERNEL);
@@ -612,7 +613,6 @@ int svc_recv(struct svc_rqst *rqstp, long timeout)
                        rqstp->rq_pages[i] = p;
                }
        rqstp->rq_pages[i++] = NULL; /* this might be seen in nfs_read_actor */
-       BUG_ON(pages >= RPCSVC_MAXPAGES);
 
        /* Make arg->head point to first page and arg->pages point to rest */
        arg = &rqstp->rq_arg;
@@ -973,7 +973,7 @@ void svc_close_net(struct svc_serv *serv, struct net *net)
        svc_clear_pools(serv, net);
        /*
         * At this point the sp_sockets lists will stay empty, since
-        * svc_enqueue will not add new entries without taking the
+        * svc_xprt_enqueue will not add new entries without taking the
         * sp_lock and checking XPT_BUSY.
         */
        svc_clear_list(&serv->sv_tempsocks, net);
index 71ec8530ec8cb7ac10abae2fbf6c2571b14c8c98..2777fa896645de3f063aa5ad67cb054bbb75a894 100644 (file)
@@ -347,17 +347,12 @@ static inline int ip_map_update(struct net *net, struct ip_map *ipm,
        return __ip_map_update(sn->ip_map_cache, ipm, udom, expiry);
 }
 
-
-void svcauth_unix_purge(void)
+void svcauth_unix_purge(struct net *net)
 {
-       struct net *net;
-
-       for_each_net(net) {
-               struct sunrpc_net *sn;
+       struct sunrpc_net *sn;
 
-               sn = net_generic(net, sunrpc_net_id);
-               cache_purge(sn->ip_map_cache);
-       }
+       sn = net_generic(net, sunrpc_net_id);
+       cache_purge(sn->ip_map_cache);
 }
 EXPORT_SYMBOL_GPL(svcauth_unix_purge);
 
@@ -751,6 +746,7 @@ svcauth_null_accept(struct svc_rqst *rqstp, __be32 *authp)
        struct svc_cred *cred = &rqstp->rq_cred;
 
        cred->cr_group_info = NULL;
+       cred->cr_principal = NULL;
        rqstp->rq_client = NULL;
 
        if (argv->iov_len < 3*4)
@@ -778,7 +774,7 @@ svcauth_null_accept(struct svc_rqst *rqstp, __be32 *authp)
        svc_putnl(resv, RPC_AUTH_NULL);
        svc_putnl(resv, 0);
 
-       rqstp->rq_flavor = RPC_AUTH_NULL;
+       rqstp->rq_cred.cr_flavor = RPC_AUTH_NULL;
        return SVC_OK;
 }
 
@@ -816,6 +812,7 @@ svcauth_unix_accept(struct svc_rqst *rqstp, __be32 *authp)
        int             len   = argv->iov_len;
 
        cred->cr_group_info = NULL;
+       cred->cr_principal = NULL;
        rqstp->rq_client = NULL;
 
        if ((len -= 3*4) < 0)
@@ -852,7 +849,7 @@ svcauth_unix_accept(struct svc_rqst *rqstp, __be32 *authp)
        svc_putnl(resv, RPC_AUTH_NULL);
        svc_putnl(resv, 0);
 
-       rqstp->rq_flavor = RPC_AUTH_UNIX;
+       rqstp->rq_cred.cr_flavor = RPC_AUTH_UNIX;
        return SVC_OK;
 
 badcred:
index 6fe2dcead15027e6a5d4b7086121b319d8e05363..3c83035cdaa9940849fbfb1c729a6a2720b32702 100644 (file)
@@ -979,20 +979,21 @@ static void xprt_alloc_slot(struct rpc_task *task)
                list_del(&req->rq_list);
                goto out_init_req;
        }
-       req = xprt_dynamic_alloc_slot(xprt, GFP_NOWAIT);
+       req = xprt_dynamic_alloc_slot(xprt, GFP_NOWAIT|__GFP_NOWARN);
        if (!IS_ERR(req))
                goto out_init_req;
        switch (PTR_ERR(req)) {
        case -ENOMEM:
-               rpc_delay(task, HZ >> 2);
                dprintk("RPC:       dynamic allocation of request slot "
                                "failed! Retrying\n");
+               task->tk_status = -ENOMEM;
                break;
        case -EAGAIN:
                rpc_sleep_on(&xprt->backlog, task, NULL);
                dprintk("RPC:       waiting for request slot\n");
+       default:
+               task->tk_status = -EAGAIN;
        }
-       task->tk_status = -EAGAIN;
        return;
 out_init_req:
        task->tk_status = 0;
index 61ceae0b956607cf0b7fa88b61f22bc79b1f673f..a157a2e64e18de17e33ae1c515e5b1501553b256 100644 (file)
@@ -3,7 +3,7 @@
 #
 
 config WAN_ROUTER
-       tristate "WAN router"
+       tristate "WAN router (DEPRECATED)"
        depends on EXPERIMENTAL
        ---help---
          Wide Area Networks (WANs), such as X.25, frame relay and leased
index c53e8f42aa7506b897464a3716e282b0c837a549..ccfbd328a69d7948736157555bfa857236875156 100644 (file)
@@ -1921,6 +1921,9 @@ no_transform:
        }
 ok:
        xfrm_pols_put(pols, drop_pols);
+       if (dst && dst->xfrm &&
+           dst->xfrm->props.mode == XFRM_MODE_TUNNEL)
+               dst->flags |= DST_XFRM_TUNNEL;
        return dst;
 
 nopol:
index faea0ec612bfed2932ca5dc25868fe00888a5afc..e5bd60ff48e3d553ef3921256123519df7b812b8 100755 (executable)
@@ -2382,6 +2382,19 @@ sub process {
                        }
                }
 
+               if ($line =~ /\bprintk\s*\(\s*KERN_([A-Z]+)/) {
+                       my $orig = $1;
+                       my $level = lc($orig);
+                       $level = "warn" if ($level eq "warning");
+                       WARN("PREFER_PR_LEVEL",
+                            "Prefer pr_$level(... to printk(KERN_$1, ...\n" . $herecurr);
+               }
+
+               if ($line =~ /\bpr_warning\s*\(/) {
+                       WARN("PREFER_PR_LEVEL",
+                            "Prefer pr_warn(... to pr_warning(...\n" . $herecurr);
+               }
+
 # function brace can't be on same line, except for #defines of do while,
 # or if closed on same line
                if (($line=~/$Type\s*$Ident\(.*\).*\s{/) and
@@ -2448,6 +2461,13 @@ sub process {
                                     "space prohibited between function name and open parenthesis '('\n" . $herecurr);
                        }
                }
+
+# check for whitespace before a non-naked semicolon
+               if ($line =~ /^\+.*\S\s+;/) {
+                       CHK("SPACING",
+                           "space prohibited before semicolon\n" . $herecurr);
+               }
+
 # Check operator spacing.
                if (!($line=~/\#\s*include/)) {
                        my $ops = qr{
index 032daab449b0bb3007562e795593a15d247a2c58..8ea39aabe94889a224c868757196afc084c578b8 100644 (file)
@@ -490,17 +490,9 @@ static int common_mmap(int op, struct file *file, unsigned long prot,
        return common_file_perm(op, file, mask);
 }
 
-static int apparmor_file_mmap(struct file *file, unsigned long reqprot,
-                             unsigned long prot, unsigned long flags,
-                             unsigned long addr, unsigned long addr_only)
+static int apparmor_mmap_file(struct file *file, unsigned long reqprot,
+                             unsigned long prot, unsigned long flags)
 {
-       int rc = 0;
-
-       /* do DAC check */
-       rc = cap_file_mmap(file, reqprot, prot, flags, addr, addr_only);
-       if (rc || addr_only)
-               return rc;
-
        return common_mmap(OP_FMMAP, file, prot, flags);
 }
 
@@ -646,7 +638,8 @@ static struct security_operations apparmor_ops = {
        .file_permission =              apparmor_file_permission,
        .file_alloc_security =          apparmor_file_alloc_security,
        .file_free_security =           apparmor_file_free_security,
-       .file_mmap =                    apparmor_file_mmap,
+       .mmap_file =                    apparmor_mmap_file,
+       .mmap_addr =                    cap_mmap_addr,
        .file_mprotect =                apparmor_file_mprotect,
        .file_lock =                    apparmor_file_lock,
 
index fca889676c5e9e5726f6c3136fb8c26cfe85f63c..61095df8b89ac452d50528144a67dca751d4f992 100644 (file)
@@ -949,7 +949,8 @@ void __init security_fixup_ops(struct security_operations *ops)
        set_to_cap_if_null(ops, file_alloc_security);
        set_to_cap_if_null(ops, file_free_security);
        set_to_cap_if_null(ops, file_ioctl);
-       set_to_cap_if_null(ops, file_mmap);
+       set_to_cap_if_null(ops, mmap_addr);
+       set_to_cap_if_null(ops, mmap_file);
        set_to_cap_if_null(ops, file_mprotect);
        set_to_cap_if_null(ops, file_lock);
        set_to_cap_if_null(ops, file_fcntl);
index e771cb1b2d7947f0c85651b38cc7c9c1d3da11d7..6dbae4650abe20208ff66eb27015e21b964d0344 100644 (file)
@@ -958,22 +958,15 @@ int cap_vm_enough_memory(struct mm_struct *mm, long pages)
 }
 
 /*
- * cap_file_mmap - check if able to map given addr
- * @file: unused
- * @reqprot: unused
- * @prot: unused
- * @flags: unused
+ * cap_mmap_addr - check if able to map given addr
  * @addr: address attempting to be mapped
- * @addr_only: unused
  *
  * If the process is attempting to map memory below dac_mmap_min_addr they need
  * CAP_SYS_RAWIO.  The other parameters to this function are unused by the
  * capability security module.  Returns 0 if this mapping should be allowed
  * -EPERM if not.
  */
-int cap_file_mmap(struct file *file, unsigned long reqprot,
-                 unsigned long prot, unsigned long flags,
-                 unsigned long addr, unsigned long addr_only)
+int cap_mmap_addr(unsigned long addr)
 {
        int ret = 0;
 
@@ -986,3 +979,9 @@ int cap_file_mmap(struct file *file, unsigned long reqprot,
        }
        return ret;
 }
+
+int cap_mmap_file(struct file *file, unsigned long reqprot,
+                 unsigned long prot, unsigned long flags)
+{
+       return 0;
+}
index fab4f8dda6c6fdf6acdebd1385bb39caae4c8b97..c92d42b021aa47c62dea181c6b90129d80ff7e53 100644 (file)
@@ -38,7 +38,7 @@ long compat_keyctl_instantiate_key_iov(
 
        ret = compat_rw_copy_check_uvector(WRITE, _payload_iov, ioc,
                                           ARRAY_SIZE(iovstack),
-                                          iovstack, &iov, 1);
+                                          iovstack, &iov);
        if (ret < 0)
                return ret;
        if (ret == 0)
index f711b094ed412e723207e5d75ceb86a0c81e4439..3dcbf86b0d31b9c7c9c889c80dacf34f9b33adc6 100644 (file)
@@ -14,6 +14,7 @@
 
 #include <linux/sched.h>
 #include <linux/key-type.h>
+#include <linux/task_work.h>
 
 #ifdef __KDEBUG
 #define kenter(FMT, ...) \
@@ -148,6 +149,7 @@ extern key_ref_t lookup_user_key(key_serial_t id, unsigned long flags,
 #define KEY_LOOKUP_FOR_UNLINK  0x04
 
 extern long join_session_keyring(const char *name);
+extern void key_change_session_keyring(struct task_work *twork);
 
 extern struct work_struct key_gc_work;
 extern unsigned key_gc_delay;
index ddb3e05bc5fcd12fae86cf9c60edf6a1f57439c1..0f5b3f0272995dc7057f1306c346468174d3e464 100644 (file)
@@ -84,7 +84,7 @@ SYSCALL_DEFINE5(add_key, const char __user *, _type,
        vm = false;
        if (_payload) {
                ret = -ENOMEM;
-               payload = kmalloc(plen, GFP_KERNEL);
+               payload = kmalloc(plen, GFP_KERNEL | __GFP_NOWARN);
                if (!payload) {
                        if (plen <= PAGE_SIZE)
                                goto error2;
@@ -1110,7 +1110,7 @@ long keyctl_instantiate_key_iov(key_serial_t id,
                goto no_payload;
 
        ret = rw_copy_check_uvector(WRITE, _payload_iov, ioc,
-                                   ARRAY_SIZE(iovstack), iovstack, &iov, 1);
+                                   ARRAY_SIZE(iovstack), iovstack, &iov);
        if (ret < 0)
                return ret;
        if (ret == 0)
@@ -1454,50 +1454,57 @@ long keyctl_get_security(key_serial_t keyid,
  */
 long keyctl_session_to_parent(void)
 {
-#ifdef TIF_NOTIFY_RESUME
        struct task_struct *me, *parent;
        const struct cred *mycred, *pcred;
-       struct cred *cred, *oldcred;
+       struct task_work *newwork, *oldwork;
        key_ref_t keyring_r;
+       struct cred *cred;
        int ret;
 
        keyring_r = lookup_user_key(KEY_SPEC_SESSION_KEYRING, 0, KEY_LINK);
        if (IS_ERR(keyring_r))
                return PTR_ERR(keyring_r);
 
+       ret = -ENOMEM;
+       newwork = kmalloc(sizeof(struct task_work), GFP_KERNEL);
+       if (!newwork)
+               goto error_keyring;
+
        /* our parent is going to need a new cred struct, a new tgcred struct
         * and new security data, so we allocate them here to prevent ENOMEM in
         * our parent */
-       ret = -ENOMEM;
        cred = cred_alloc_blank();
        if (!cred)
-               goto error_keyring;
+               goto error_newwork;
 
        cred->tgcred->session_keyring = key_ref_to_ptr(keyring_r);
-       keyring_r = NULL;
+       init_task_work(newwork, key_change_session_keyring, cred);
 
        me = current;
        rcu_read_lock();
        write_lock_irq(&tasklist_lock);
 
-       parent = me->real_parent;
        ret = -EPERM;
+       oldwork = NULL;
+       parent = me->real_parent;
 
        /* the parent mustn't be init and mustn't be a kernel thread */
        if (parent->pid <= 1 || !parent->mm)
-               goto not_permitted;
+               goto unlock;
 
        /* the parent must be single threaded */
        if (!thread_group_empty(parent))
-               goto not_permitted;
+               goto unlock;
 
        /* the parent and the child must have different session keyrings or
         * there's no point */
        mycred = current_cred();
        pcred = __task_cred(parent);
        if (mycred == pcred ||
-           mycred->tgcred->session_keyring == pcred->tgcred->session_keyring)
-               goto already_same;
+           mycred->tgcred->session_keyring == pcred->tgcred->session_keyring) {
+               ret = 0;
+               goto unlock;
+       }
 
        /* the parent must have the same effective ownership and mustn't be
         * SUID/SGID */
@@ -1507,50 +1514,40 @@ long keyctl_session_to_parent(void)
            pcred->gid  != mycred->egid ||
            pcred->egid != mycred->egid ||
            pcred->sgid != mycred->egid)
-               goto not_permitted;
+               goto unlock;
 
        /* the keyrings must have the same UID */
        if ((pcred->tgcred->session_keyring &&
             pcred->tgcred->session_keyring->uid != mycred->euid) ||
            mycred->tgcred->session_keyring->uid != mycred->euid)
-               goto not_permitted;
+               goto unlock;
 
-       /* if there's an already pending keyring replacement, then we replace
-        * that */
-       oldcred = parent->replacement_session_keyring;
+       /* cancel an already pending keyring replacement */
+       oldwork = task_work_cancel(parent, key_change_session_keyring);
 
        /* the replacement session keyring is applied just prior to userspace
         * restarting */
-       parent->replacement_session_keyring = cred;
-       cred = NULL;
-       set_ti_thread_flag(task_thread_info(parent), TIF_NOTIFY_RESUME);
-
-       write_unlock_irq(&tasklist_lock);
-       rcu_read_unlock();
-       if (oldcred)
-               put_cred(oldcred);
-       return 0;
-
-already_same:
-       ret = 0;
-not_permitted:
+       ret = task_work_add(parent, newwork, true);
+       if (!ret)
+               newwork = NULL;
+unlock:
        write_unlock_irq(&tasklist_lock);
        rcu_read_unlock();
-       put_cred(cred);
+       if (oldwork) {
+               put_cred(oldwork->data);
+               kfree(oldwork);
+       }
+       if (newwork) {
+               put_cred(newwork->data);
+               kfree(newwork);
+       }
        return ret;
 
+error_newwork:
+       kfree(newwork);
 error_keyring:
        key_ref_put(keyring_r);
        return ret;
-
-#else /* !TIF_NOTIFY_RESUME */
-       /*
-        * To be removed when TIF_NOTIFY_RESUME has been implemented on
-        * m68k/xtensa
-        */
-#warning TIF_NOTIFY_RESUME not implemented
-       return -EOPNOTSUPP;
-#endif /* !TIF_NOTIFY_RESUME */
 }
 
 /*
index d71056db7b67501a085fd4a8feda5c841dd83094..4ad54eea1ea45554d5d931497671fdb32a33660b 100644 (file)
@@ -834,23 +834,17 @@ error:
  * Replace a process's session keyring on behalf of one of its children when
  * the target  process is about to resume userspace execution.
  */
-void key_replace_session_keyring(void)
+void key_change_session_keyring(struct task_work *twork)
 {
-       const struct cred *old;
-       struct cred *new;
-
-       if (!current->replacement_session_keyring)
-               return;
+       const struct cred *old = current_cred();
+       struct cred *new = twork->data;
 
-       write_lock_irq(&tasklist_lock);
-       new = current->replacement_session_keyring;
-       current->replacement_session_keyring = NULL;
-       write_unlock_irq(&tasklist_lock);
-
-       if (!new)
+       kfree(twork);
+       if (unlikely(current->flags & PF_EXITING)) {
+               put_cred(new);
                return;
+       }
 
-       old = current_cred();
        new->  uid      = old->  uid;
        new-> euid      = old-> euid;
        new-> suid      = old-> suid;
index cc3790315d2f15778fcc3c8a3ec7f2434282277d..000e7501752022089b82efeb153115498e55da60 100644 (file)
@@ -93,16 +93,9 @@ static void umh_keys_cleanup(struct subprocess_info *info)
 static int call_usermodehelper_keys(char *path, char **argv, char **envp,
                                        struct key *session_keyring, int wait)
 {
-       gfp_t gfp_mask = (wait == UMH_NO_WAIT) ? GFP_ATOMIC : GFP_KERNEL;
-       struct subprocess_info *info =
-               call_usermodehelper_setup(path, argv, envp, gfp_mask);
-
-       if (!info)
-               return -ENOMEM;
-
-       call_usermodehelper_setfns(info, umh_keys_init, umh_keys_cleanup,
-                                       key_get(session_keyring));
-       return call_usermodehelper_exec(info, wait);
+       return call_usermodehelper_fns(path, argv, envp, wait,
+                                      umh_keys_init, umh_keys_cleanup,
+                                      key_get(session_keyring));
 }
 
 /*
index 5497a57fba0154a24b1b87835930e6cc685f855b..3efc9b12aef44016201b02eeafcc10140f17a240 100644 (file)
@@ -20,6 +20,9 @@
 #include <linux/ima.h>
 #include <linux/evm.h>
 #include <linux/fsnotify.h>
+#include <linux/mman.h>
+#include <linux/mount.h>
+#include <linux/personality.h>
 #include <net/flow.h>
 
 #define MAX_LSM_EVM_XATTR      2
@@ -657,18 +660,56 @@ int security_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
        return security_ops->file_ioctl(file, cmd, arg);
 }
 
-int security_file_mmap(struct file *file, unsigned long reqprot,
-                       unsigned long prot, unsigned long flags,
-                       unsigned long addr, unsigned long addr_only)
+static inline unsigned long mmap_prot(struct file *file, unsigned long prot)
 {
-       int ret;
+       /*
+        * Does we have PROT_READ and does the application expect
+        * it to imply PROT_EXEC?  If not, nothing to talk about...
+        */
+       if ((prot & (PROT_READ | PROT_EXEC)) != PROT_READ)
+               return prot;
+       if (!(current->personality & READ_IMPLIES_EXEC))
+               return prot;
+       /*
+        * if that's an anonymous mapping, let it.
+        */
+       if (!file)
+               return prot | PROT_EXEC;
+       /*
+        * ditto if it's not on noexec mount, except that on !MMU we need
+        * BDI_CAP_EXEC_MMAP (== VM_MAYEXEC) in this case
+        */
+       if (!(file->f_path.mnt->mnt_flags & MNT_NOEXEC)) {
+#ifndef CONFIG_MMU
+               unsigned long caps = 0;
+               struct address_space *mapping = file->f_mapping;
+               if (mapping && mapping->backing_dev_info)
+                       caps = mapping->backing_dev_info->capabilities;
+               if (!(caps & BDI_CAP_EXEC_MAP))
+                       return prot;
+#endif
+               return prot | PROT_EXEC;
+       }
+       /* anything on noexec mount won't get PROT_EXEC */
+       return prot;
+}
 
-       ret = security_ops->file_mmap(file, reqprot, prot, flags, addr, addr_only);
+int security_mmap_file(struct file *file, unsigned long prot,
+                       unsigned long flags)
+{
+       int ret;
+       ret = security_ops->mmap_file(file, prot,
+                                       mmap_prot(file, prot), flags);
        if (ret)
                return ret;
        return ima_file_mmap(file, prot);
 }
 
+int security_mmap_addr(unsigned long addr)
+{
+       return security_ops->mmap_addr(addr);
+}
+
 int security_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot,
                            unsigned long prot)
 {
index fa2341b683314b0c5505f905e6712538555300ad..372ec6502aa8752dca83c3c507e2d0ce9cac84d1 100644 (file)
@@ -3083,9 +3083,7 @@ error:
        return rc;
 }
 
-static int selinux_file_mmap(struct file *file, unsigned long reqprot,
-                            unsigned long prot, unsigned long flags,
-                            unsigned long addr, unsigned long addr_only)
+static int selinux_mmap_addr(unsigned long addr)
 {
        int rc = 0;
        u32 sid = current_sid();
@@ -3104,10 +3102,12 @@ static int selinux_file_mmap(struct file *file, unsigned long reqprot,
        }
 
        /* do DAC check on address space usage */
-       rc = cap_file_mmap(file, reqprot, prot, flags, addr, addr_only);
-       if (rc || addr_only)
-               return rc;
+       return cap_mmap_addr(addr);
+}
 
+static int selinux_mmap_file(struct file *file, unsigned long reqprot,
+                            unsigned long prot, unsigned long flags)
+{
        if (selinux_checkreqprot)
                prot = reqprot;
 
@@ -5570,7 +5570,8 @@ static struct security_operations selinux_ops = {
        .file_alloc_security =          selinux_file_alloc_security,
        .file_free_security =           selinux_file_free_security,
        .file_ioctl =                   selinux_file_ioctl,
-       .file_mmap =                    selinux_file_mmap,
+       .mmap_file =                    selinux_mmap_file,
+       .mmap_addr =                    selinux_mmap_addr,
        .file_mprotect =                selinux_file_mprotect,
        .file_lock =                    selinux_file_lock,
        .file_fcntl =                   selinux_file_fcntl,
index 4e93f9ef970b25a78bca26ab2a49962024b3cb50..3ad2902512888282e299b64434a7790d9788e060 100644 (file)
@@ -1259,12 +1259,8 @@ static int sel_make_bools(void)
                if (!inode)
                        goto out;
 
-               ret = -EINVAL;
-               len = snprintf(page, PAGE_SIZE, "/%s/%s", BOOL_DIR_NAME, names[i]);
-               if (len < 0)
-                       goto out;
-
                ret = -ENAMETOOLONG;
+               len = snprintf(page, PAGE_SIZE, "/%s/%s", BOOL_DIR_NAME, names[i]);
                if (len >= PAGE_SIZE)
                        goto out;
 
@@ -1557,19 +1553,10 @@ static inline u32 sel_ino_to_perm(unsigned long ino)
 static ssize_t sel_read_class(struct file *file, char __user *buf,
                                size_t count, loff_t *ppos)
 {
-       ssize_t rc, len;
-       char *page;
        unsigned long ino = file->f_path.dentry->d_inode->i_ino;
-
-       page = (char *)__get_free_page(GFP_KERNEL);
-       if (!page)
-               return -ENOMEM;
-
-       len = snprintf(page, PAGE_SIZE, "%d", sel_ino_to_class(ino));
-       rc = simple_read_from_buffer(buf, count, ppos, page, len);
-       free_page((unsigned long)page);
-
-       return rc;
+       char res[TMPBUFLEN];
+       ssize_t len = snprintf(res, sizeof(res), "%d", sel_ino_to_class(ino));
+       return simple_read_from_buffer(buf, count, ppos, res, len);
 }
 
 static const struct file_operations sel_class_ops = {
@@ -1580,19 +1567,10 @@ static const struct file_operations sel_class_ops = {
 static ssize_t sel_read_perm(struct file *file, char __user *buf,
                                size_t count, loff_t *ppos)
 {
-       ssize_t rc, len;
-       char *page;
        unsigned long ino = file->f_path.dentry->d_inode->i_ino;
-
-       page = (char *)__get_free_page(GFP_KERNEL);
-       if (!page)
-               return -ENOMEM;
-
-       len = snprintf(page, PAGE_SIZE, "%d", sel_ino_to_perm(ino));
-       rc = simple_read_from_buffer(buf, count, ppos, page, len);
-       free_page((unsigned long)page);
-
-       return rc;
+       char res[TMPBUFLEN];
+       ssize_t len = snprintf(res, sizeof(res), "%d", sel_ino_to_perm(ino));
+       return simple_read_from_buffer(buf, count, ppos, res, len);
 }
 
 static const struct file_operations sel_perm_ops = {
index d583c054580889eff6f4e9080110aad7ae0370d1..ee0bb5735f35c98d6edfa7bb4c9590ef2a234cc4 100644 (file)
@@ -1171,7 +1171,7 @@ static int smack_file_fcntl(struct file *file, unsigned int cmd,
 }
 
 /**
- * smack_file_mmap :
+ * smack_mmap_file :
  * Check permissions for a mmap operation.  The @file may be NULL, e.g.
  * if mapping anonymous memory.
  * @file contains the file structure for file to map (may be NULL).
@@ -1180,10 +1180,9 @@ static int smack_file_fcntl(struct file *file, unsigned int cmd,
  * @flags contains the operational flags.
  * Return 0 if permission is granted.
  */
-static int smack_file_mmap(struct file *file,
+static int smack_mmap_file(struct file *file,
                           unsigned long reqprot, unsigned long prot,
-                          unsigned long flags, unsigned long addr,
-                          unsigned long addr_only)
+                          unsigned long flags)
 {
        struct smack_known *skp;
        struct smack_rule *srp;
@@ -1198,11 +1197,6 @@ static int smack_file_mmap(struct file *file,
        int tmay;
        int rc;
 
-       /* do DAC check on address space usage */
-       rc = cap_file_mmap(file, reqprot, prot, flags, addr, addr_only);
-       if (rc || addr_only)
-               return rc;
-
        if (file == NULL || file->f_dentry == NULL)
                return 0;
 
@@ -3482,7 +3476,8 @@ struct security_operations smack_ops = {
        .file_ioctl =                   smack_file_ioctl,
        .file_lock =                    smack_file_lock,
        .file_fcntl =                   smack_file_fcntl,
-       .file_mmap =                    smack_file_mmap,
+       .mmap_file =                    smack_mmap_file,
+       .mmap_addr =                    cap_mmap_addr,
        .file_set_fowner =              smack_file_set_fowner,
        .file_send_sigiotask =          smack_file_send_sigiotask,
        .file_receive =                 smack_file_receive,
index 0a5027b94714afb9600b94922f6d89140271492d..b8ac8710f47fb200d602bc2917598a153cad6884 100644 (file)
@@ -1988,6 +1988,13 @@ static int hdspm_get_system_sample_rate(struct hdspm *hdspm)
        period = hdspm_read(hdspm, HDSPM_RD_PLL_FREQ);
        rate = hdspm_calc_dds_value(hdspm, period);
 
+       if (rate > 207000) {
+               /* Unreasonable high sample rate as seen on PCI MADI cards.
+                * Use the cached value instead.
+                */
+               rate = hdspm->system_sample_rate;
+       }
+
        return rate;
 }
 
index cf3ed0362c9ccf76cbbdc975c8788b33725b70f7..28dd76c7cb1c08ac308ca7730d9abfc8b02b0903 100644 (file)
@@ -543,7 +543,7 @@ static int imx_ssi_probe(struct platform_device *pdev)
                        ret);
                goto failed_clk;
        }
-       clk_enable(ssi->clk);
+       clk_prepare_enable(ssi->clk);
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        if (!res) {
@@ -641,7 +641,7 @@ failed_ac97:
 failed_ioremap:
        release_mem_region(res->start, resource_size(res));
 failed_get_resource:
-       clk_disable(ssi->clk);
+       clk_disable_unprepare(ssi->clk);
        clk_put(ssi->clk);
 failed_clk:
        kfree(ssi);
@@ -664,7 +664,7 @@ static int __devexit imx_ssi_remove(struct platform_device *pdev)
 
        iounmap(ssi->base);
        release_mem_region(res->start, resource_size(res));
-       clk_disable(ssi->clk);
+       clk_disable_unprepare(ssi->clk);
        clk_put(ssi->clk);
        kfree(ssi);
 
index 7cee22515d9de55c505eae1dd460107b1eacee3a..2ef98536f1da9ecff07ebd3d6903a8bf78345029 100644 (file)
@@ -1052,6 +1052,13 @@ static int fsi_dma_quit(struct fsi_priv *fsi, struct fsi_stream *io)
        return 0;
 }
 
+static dma_addr_t fsi_dma_get_area(struct fsi_stream *io)
+{
+       struct snd_pcm_runtime *runtime = io->substream->runtime;
+
+       return io->dma + samples_to_bytes(runtime, io->buff_sample_pos);
+}
+
 static void fsi_dma_complete(void *data)
 {
        struct fsi_stream *io = (struct fsi_stream *)data;
@@ -1061,7 +1068,7 @@ static void fsi_dma_complete(void *data)
        enum dma_data_direction dir = fsi_stream_is_play(fsi, io) ?
                DMA_TO_DEVICE : DMA_FROM_DEVICE;
 
-       dma_sync_single_for_cpu(dai->dev, io->dma,
+       dma_sync_single_for_cpu(dai->dev, fsi_dma_get_area(io),
                        samples_to_bytes(runtime, io->period_samples), dir);
 
        io->buff_sample_pos += io->period_samples;
@@ -1078,13 +1085,6 @@ static void fsi_dma_complete(void *data)
        snd_pcm_period_elapsed(io->substream);
 }
 
-static dma_addr_t fsi_dma_get_area(struct fsi_stream *io)
-{
-       struct snd_pcm_runtime *runtime = io->substream->runtime;
-
-       return io->dma + samples_to_bytes(runtime, io->buff_sample_pos);
-}
-
 static void fsi_dma_do_tasklet(unsigned long data)
 {
        struct fsi_stream *io = (struct fsi_stream *)data;
@@ -1110,7 +1110,7 @@ static void fsi_dma_do_tasklet(unsigned long data)
        len     = samples_to_bytes(runtime, io->period_samples);
        buf     = fsi_dma_get_area(io);
 
-       dma_sync_single_for_device(dai->dev, io->dma, len, dir);
+       dma_sync_single_for_device(dai->dev, buf, len, dir);
 
        sg_init_table(&sg, 1);
        sg_set_page(&sg, pfn_to_page(PFN_DOWN(buf)),
@@ -1172,9 +1172,16 @@ static int fsi_dma_transfer(struct fsi_priv *fsi, struct fsi_stream *io)
 static void fsi_dma_push_start_stop(struct fsi_priv *fsi, struct fsi_stream *io,
                                 int start)
 {
+       struct fsi_master *master = fsi_get_master(fsi);
+       u32 clk  = fsi_is_port_a(fsi) ? CRA  : CRB;
        u32 enable = start ? DMA_ON : 0;
 
        fsi_reg_mask_set(fsi, OUT_DMAC, DMA_ON, enable);
+
+       dmaengine_terminate_all(io->chan);
+
+       if (fsi_is_clk_master(fsi))
+               fsi_master_mask_set(master, CLK_RST, clk, (enable) ? clk : 0);
 }
 
 static int fsi_dma_probe(struct fsi_priv *fsi, struct fsi_stream *io)
index 24839d932648c81849ecfe63214a8124d914dd0e..cdf8b7601973406c445c69c6e5cd4afbdbbdf23b 100644 (file)
@@ -788,6 +788,9 @@ static int snd_usb_pcm_check_knot(struct snd_pcm_runtime *runtime,
        int count = 0, needs_knot = 0;
        int err;
 
+       kfree(subs->rate_list.list);
+       subs->rate_list.list = NULL;
+
        list_for_each_entry(fp, &subs->fmt_list, list) {
                if (fp->rates & SNDRV_PCM_RATE_CONTINUOUS)
                        return 0;
index 998534992197591bde7328e44cd2725a7af84797..554828219c33cceaf0c5b9d3f57bd69d4a7a109f 100644 (file)
@@ -1434,8 +1434,11 @@ static int event_read_fields(struct event_format *event, struct format_field **f
 fail:
        free_token(token);
 fail_expect:
-       if (field)
+       if (field) {
+               free(field->type);
+               free(field->name);
                free(field);
+       }
        return -1;
 }
 
@@ -1712,6 +1715,8 @@ process_op(struct event_format *event, struct print_arg *arg, char **tok)
 
                if (set_op_prio(arg) == -1) {
                        event->flags |= EVENT_FL_FAILED;
+                       /* arg->op.op (= token) will be freed at out_free */
+                       arg->op.op = NULL;
                        goto out_free;
                }
 
@@ -2124,6 +2129,13 @@ process_fields(struct event_format *event, struct print_flag_sym **list, char **
 
                free_token(token);
                type = process_arg(event, arg, &token);
+
+               if (type == EVENT_OP)
+                       type = process_op(event, arg, &token);
+
+               if (type == EVENT_ERROR)
+                       goto out_free;
+
                if (test_type_token(type, token, EVENT_DELIM, ","))
                        goto out_free;
 
@@ -2288,17 +2300,18 @@ process_dynamic_array(struct event_format *event, struct print_arg *arg, char **
        arg = alloc_arg();
        type = process_arg(event, arg, &token);
        if (type == EVENT_ERROR)
-               goto out_free;
+               goto out_free_arg;
 
        if (!test_type_token(type, token, EVENT_OP, "]"))
-               goto out_free;
+               goto out_free_arg;
 
        free_token(token);
        type = read_token_item(tok);
        return type;
 
+ out_free_arg:
+       free_arg(arg);
  out_free:
-       free(arg);
        free_token(token);
        *tok = NULL;
        return EVENT_ERROR;
@@ -3362,6 +3375,7 @@ process_defined_func(struct trace_seq *s, void *data, int size,
                        break;
                }
                farg = farg->next;
+               param = param->next;
        }
 
        ret = (*func_handle->func)(s, args);
index 2d40c5ed81d66a00fde6cc09917ac32dba196525..dfcfe2c131de6e3d8ea6c26cbf452c2834fd90d7 100644 (file)
@@ -325,9 +325,8 @@ static void free_events(struct event_list *events)
 }
 
 static struct filter_arg *
-create_arg_item(struct event_format *event,
-               const char *token, enum filter_arg_type type,
-               char **error_str)
+create_arg_item(struct event_format *event, const char *token,
+               enum event_type type, char **error_str)
 {
        struct format_field *field;
        struct filter_arg *arg;
@@ -1585,7 +1584,7 @@ get_value(struct event_format *event,
                const char *name;
 
                name = get_comm(event, record);
-               return (unsigned long long)name;
+               return (unsigned long)name;
        }
 
        pevent_read_number_field(field, record->data, &val);
index 42c6fd2ae85d19ac31fa5f0c191dfcf776ca60e9..767ea2436e1cd841a762ee8cdb039ba80a7120b3 100644 (file)
 
        # Default, disable using /dev/null
        dir = /root/.debug
+
+[annotate]
+
+       # Defaults
+       hide_src_code = false
+       use_offset = true
+       jump_arrows = true
+       show_nr_jumps = false
index 1d3d513beb9b1083c60531f349d5d0c06af64dfa..0eee64cfe9a0f48ced0ee2397068979763355cd4 100644 (file)
@@ -80,7 +80,7 @@ ifeq ("$(origin DEBUG)", "command line")
   PERF_DEBUG = $(DEBUG)
 endif
 ifndef PERF_DEBUG
-  CFLAGS_OPTIMIZE = -O6
+  CFLAGS_OPTIMIZE = -O6 -D_FORTIFY_SOURCE=2
 endif
 
 ifdef PARSER_DEBUG
@@ -89,7 +89,7 @@ ifdef PARSER_DEBUG
        PARSER_DEBUG_CFLAGS := -DPARSER_DEBUG
 endif
 
-CFLAGS = -fno-omit-frame-pointer -ggdb3 -Wall -Wextra -std=gnu99 $(CFLAGS_WERROR) $(CFLAGS_OPTIMIZE) -D_FORTIFY_SOURCE=2 $(EXTRA_WARNINGS) $(EXTRA_CFLAGS) $(PARSER_DEBUG_CFLAGS)
+CFLAGS = -fno-omit-frame-pointer -ggdb3 -Wall -Wextra -std=gnu99 $(CFLAGS_WERROR) $(CFLAGS_OPTIMIZE) $(EXTRA_WARNINGS) $(EXTRA_CFLAGS) $(PARSER_DEBUG_CFLAGS)
 EXTLIBS = -lpthread -lrt -lelf -lm
 ALL_CFLAGS = $(CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE
 ALL_LDFLAGS = $(LDFLAGS)
index 806e0a286634a6bd7b7a766706c32426730c7727..67522cf874053e24ff87d4fa3fca67e6845981be 100644 (file)
@@ -215,7 +215,7 @@ static int __cmd_annotate(struct perf_annotate *ann)
        }
 
        if (total_nr_samples == 0) {
-               ui__warning("The %s file has no samples!\n", session->filename);
+               ui__error("The %s file has no samples!\n", session->filename);
                goto out_delete;
        }
 out_delete:
index e52d77ec7084e02e4fa318e873ff2cf1679a222d..acd78dc283411692f9e7e48b78085d9b5da63471 100644 (file)
@@ -116,7 +116,7 @@ static const char * const evlist_usage[] = {
 int cmd_evlist(int argc, const char **argv, const char *prefix __used)
 {
        struct perf_attr_details details = { .verbose = false, };
-       const char *input_name;
+       const char *input_name = NULL;
        const struct option options[] = {
                OPT_STRING('i', "input", &input_name, "file",
                            "Input file name"),
index e5cb08427e13f56ed7f9223d520a954ef0fc1cf2..f95840d04e4c7a224821e395600df2bbdc7e4323 100644 (file)
@@ -264,7 +264,7 @@ try_again:
                        }
 
                        if (err == ENOENT) {
-                               ui__warning("The %s event is not supported.\n",
+                               ui__error("The %s event is not supported.\n",
                                            event_name(pos));
                                exit(EXIT_FAILURE);
                        }
@@ -858,8 +858,8 @@ int cmd_record(int argc, const char **argv, const char *prefix __used)
                usage_with_options(record_usage, record_options);
 
        if (rec->force && rec->append_file) {
-               fprintf(stderr, "Can't overwrite and append at the same time."
-                               " You need to choose between -f and -A");
+               ui__error("Can't overwrite and append at the same time."
+                         " You need to choose between -f and -A");
                usage_with_options(record_usage, record_options);
        } else if (rec->append_file) {
                rec->write_mode = WRITE_APPEND;
@@ -868,8 +868,8 @@ int cmd_record(int argc, const char **argv, const char *prefix __used)
        }
 
        if (nr_cgroups && !rec->opts.target.system_wide) {
-               fprintf(stderr, "cgroup monitoring only available in"
-                       " system-wide mode\n");
+               ui__error("cgroup monitoring only available in"
+                         " system-wide mode\n");
                usage_with_options(record_usage, record_options);
        }
 
@@ -905,7 +905,7 @@ int cmd_record(int argc, const char **argv, const char *prefix __used)
                int saved_errno = errno;
 
                perf_target__strerror(&rec->opts.target, err, errbuf, BUFSIZ);
-               ui__warning("%s", errbuf);
+               ui__error("%s", errbuf);
 
                err = -saved_errno;
                goto out_free_fd;
@@ -933,7 +933,7 @@ int cmd_record(int argc, const char **argv, const char *prefix __used)
        else if (rec->opts.freq) {
                rec->opts.default_interval = rec->opts.freq;
        } else {
-               fprintf(stderr, "frequency and count are zero, aborting\n");
+               ui__error("frequency and count are zero, aborting\n");
                err = -EINVAL;
                goto out_free_fd;
        }
index d58e41445d0d6dcfb4ca26fbe36eb9b7e02c596b..8c767c6bca91b7490cb1564da230c7c9021422e6 100644 (file)
@@ -251,13 +251,13 @@ static int perf_report__setup_sample_type(struct perf_report *rep)
 
        if (!(self->sample_type & PERF_SAMPLE_CALLCHAIN)) {
                if (sort__has_parent) {
-                       ui__warning("Selected --sort parent, but no "
+                       ui__error("Selected --sort parent, but no "
                                    "callchain data. Did you call "
                                    "'perf record' without -g?\n");
                        return -EINVAL;
                }
                if (symbol_conf.use_callchain) {
-                       ui__warning("Selected -g but no callchain data. Did "
+                       ui__error("Selected -g but no callchain data. Did "
                                    "you call 'perf record' without -g?\n");
                        return -1;
                }
@@ -266,17 +266,15 @@ static int perf_report__setup_sample_type(struct perf_report *rep)
                   !symbol_conf.use_callchain) {
                        symbol_conf.use_callchain = true;
                        if (callchain_register_param(&callchain_param) < 0) {
-                               ui__warning("Can't register callchain "
-                                           "params.\n");
+                               ui__error("Can't register callchain params.\n");
                                return -EINVAL;
                        }
        }
 
        if (sort__branch_mode == 1) {
                if (!(self->sample_type & PERF_SAMPLE_BRANCH_STACK)) {
-                       fprintf(stderr, "selected -b but no branch data."
-                                       " Did you call perf record without"
-                                       " -b?\n");
+                       ui__error("Selected -b but no branch data. "
+                                 "Did you call perf record without -b?\n");
                        return -1;
                }
        }
@@ -420,7 +418,7 @@ static int __cmd_report(struct perf_report *rep)
        }
 
        if (nr_samples == 0) {
-               ui__warning("The %s file has no samples!\n", session->filename);
+               ui__error("The %s file has no samples!\n", session->filename);
                goto out_delete;
        }
 
index 6031dce0429f8f93e267d530e9a91fec3ddaf74b..871b540293e132610bb6a50bb384289d89aca75c 100644 (file)
@@ -953,22 +953,22 @@ try_again:
                                attr->config = PERF_COUNT_SW_CPU_CLOCK;
                                if (counter->name) {
                                        free(counter->name);
-                                       counter->name = strdup(event_name(counter));
+                                       counter->name = NULL;
                                }
                                goto try_again;
                        }
 
                        if (err == ENOENT) {
-                               ui__warning("The %s event is not supported.\n",
+                               ui__error("The %s event is not supported.\n",
                                            event_name(counter));
                                goto out_err;
                        } else if (err == EMFILE) {
-                               ui__warning("Too many events are opened.\n"
+                               ui__error("Too many events are opened.\n"
                                            "Try again after reducing the number of events\n");
                                goto out_err;
                        }
 
-                       ui__warning("The sys_perf_event_open() syscall "
+                       ui__error("The sys_perf_event_open() syscall "
                                    "returned with %d (%s).  /bin/dmesg "
                                    "may provide additional information.\n"
                                    "No CONFIG_PERF_EVENTS=y kernel support "
@@ -978,7 +978,7 @@ try_again:
        }
 
        if (perf_evlist__mmap(evlist, top->mmap_pages, false) < 0) {
-               ui__warning("Failed to mmap with %d (%s)\n",
+               ui__error("Failed to mmap with %d (%s)\n",
                            errno, strerror(errno));
                goto out_err;
        }
@@ -994,12 +994,12 @@ static int perf_top__setup_sample_type(struct perf_top *top)
 {
        if (!top->sort_has_symbols) {
                if (symbol_conf.use_callchain) {
-                       ui__warning("Selected -g but \"sym\" not present in --sort/-s.");
+                       ui__error("Selected -g but \"sym\" not present in --sort/-s.");
                        return -EINVAL;
                }
        } else if (!top->dont_use_callchains && callchain_param.mode != CHAIN_NONE) {
                if (callchain_register_param(&callchain_param) < 0) {
-                       ui__warning("Can't register callchain params.\n");
+                       ui__error("Can't register callchain params.\n");
                        return -EINVAL;
                }
        }
@@ -1041,7 +1041,7 @@ static int __cmd_top(struct perf_top *top)
 
        if (pthread_create(&thread, NULL, (use_browser > 0 ? display_thread_tui :
                                                            display_thread), top)) {
-               printf("Could not create display thread.\n");
+               ui__error("Could not create display thread.\n");
                exit(-1);
        }
 
@@ -1050,7 +1050,7 @@ static int __cmd_top(struct perf_top *top)
 
                param.sched_priority = top->realtime_prio;
                if (sched_setscheduler(0, SCHED_FIFO, &param)) {
-                       printf("Could not set realtime priority.\n");
+                       ui__error("Could not set realtime priority.\n");
                        exit(-1);
                }
        }
@@ -1274,7 +1274,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __used)
                int saved_errno = errno;
 
                perf_target__strerror(&top.target, status, errbuf, BUFSIZ);
-               ui__warning("%s", errbuf);
+               ui__error("%s", errbuf);
 
                status = -saved_errno;
                goto out_delete_evlist;
@@ -1288,7 +1288,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __used)
 
        if (!top.evlist->nr_entries &&
            perf_evlist__add_default(top.evlist) < 0) {
-               pr_err("Not enough memory for event selector list\n");
+               ui__error("Not enough memory for event selector list\n");
                return -ENOMEM;
        }
 
@@ -1305,7 +1305,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __used)
        else if (top.freq) {
                top.default_interval = top.freq;
        } else {
-               fprintf(stderr, "frequency and count are zero, aborting\n");
+               ui__error("frequency and count are zero, aborting\n");
                exit(EXIT_FAILURE);
        }
 
index 14f1034f14f93efbef0b7e5a7ea3f642674fc856..f960ccb2edc6f38f8a7b7a3f0f1b740cbd351c2b 100644 (file)
@@ -227,7 +227,7 @@ struct perf_record_opts {
        unsigned int freq;
        unsigned int mmap_pages;
        unsigned int user_freq;
-       int          branch_stack;
+       u64          branch_stack;
        u64          default_interval;
        u64          user_interval;
 };
index cde4d0f0ddb99c8de13e581d1595ce948a7afe85..1818a531f1d3ea83346e48a131844cb7dc8212a5 100644 (file)
@@ -35,16 +35,16 @@ int ui_browser__set_color(struct ui_browser *browser, int color)
        return ret;
 }
 
-void ui_browser__set_percent_color(struct ui_browser *self,
+void ui_browser__set_percent_color(struct ui_browser *browser,
                                   double percent, bool current)
 {
-        int color = ui_browser__percent_color(self, percent, current);
-        ui_browser__set_color(self, color);
+        int color = ui_browser__percent_color(browser, percent, current);
+        ui_browser__set_color(browser, color);
 }
 
-void ui_browser__gotorc(struct ui_browser *self, int y, int x)
+void ui_browser__gotorc(struct ui_browser *browser, int y, int x)
 {
-       SLsmg_gotorc(self->y + y, self->x + x);
+       SLsmg_gotorc(browser->y + y, browser->x + x);
 }
 
 static struct list_head *
@@ -73,23 +73,23 @@ ui_browser__list_head_filter_prev_entries(struct ui_browser *browser,
        return NULL;
 }
 
-void ui_browser__list_head_seek(struct ui_browser *self, off_t offset, int whence)
+void ui_browser__list_head_seek(struct ui_browser *browser, off_t offset, int whence)
 {
-       struct list_head *head = self->entries;
+       struct list_head *head = browser->entries;
        struct list_head *pos;
 
-       if (self->nr_entries == 0)
+       if (browser->nr_entries == 0)
                return;
 
        switch (whence) {
        case SEEK_SET:
-               pos = ui_browser__list_head_filter_entries(self, head->next);
+               pos = ui_browser__list_head_filter_entries(browser, head->next);
                break;
        case SEEK_CUR:
-               pos = self->top;
+               pos = browser->top;
                break;
        case SEEK_END:
-               pos = ui_browser__list_head_filter_prev_entries(self, head->prev);
+               pos = ui_browser__list_head_filter_prev_entries(browser, head->prev);
                break;
        default:
                return;
@@ -99,18 +99,18 @@ void ui_browser__list_head_seek(struct ui_browser *self, off_t offset, int whenc
 
        if (offset > 0) {
                while (offset-- != 0)
-                       pos = ui_browser__list_head_filter_entries(self, pos->next);
+                       pos = ui_browser__list_head_filter_entries(browser, pos->next);
        } else {
                while (offset++ != 0)
-                       pos = ui_browser__list_head_filter_prev_entries(self, pos->prev);
+                       pos = ui_browser__list_head_filter_prev_entries(browser, pos->prev);
        }
 
-       self->top = pos;
+       browser->top = pos;
 }
 
-void ui_browser__rb_tree_seek(struct ui_browser *self, off_t offset, int whence)
+void ui_browser__rb_tree_seek(struct ui_browser *browser, off_t offset, int whence)
 {
-       struct rb_root *root = self->entries;
+       struct rb_root *root = browser->entries;
        struct rb_node *nd;
 
        switch (whence) {
@@ -118,7 +118,7 @@ void ui_browser__rb_tree_seek(struct ui_browser *self, off_t offset, int whence)
                nd = rb_first(root);
                break;
        case SEEK_CUR:
-               nd = self->top;
+               nd = browser->top;
                break;
        case SEEK_END:
                nd = rb_last(root);
@@ -135,23 +135,23 @@ void ui_browser__rb_tree_seek(struct ui_browser *self, off_t offset, int whence)
                        nd = rb_prev(nd);
        }
 
-       self->top = nd;
+       browser->top = nd;
 }
 
-unsigned int ui_browser__rb_tree_refresh(struct ui_browser *self)
+unsigned int ui_browser__rb_tree_refresh(struct ui_browser *browser)
 {
        struct rb_node *nd;
        int row = 0;
 
-       if (self->top == NULL)
-                self->top = rb_first(self->entries);
+       if (browser->top == NULL)
+                browser->top = rb_first(browser->entries);
 
-       nd = self->top;
+       nd = browser->top;
 
        while (nd != NULL) {
-               ui_browser__gotorc(self, row, 0);
-               self->write(self, nd, row);
-               if (++row == self->height)
+               ui_browser__gotorc(browser, row, 0);
+               browser->write(browser, nd, row);
+               if (++row == browser->height)
                        break;
                nd = rb_next(nd);
        }
@@ -159,17 +159,17 @@ unsigned int ui_browser__rb_tree_refresh(struct ui_browser *self)
        return row;
 }
 
-bool ui_browser__is_current_entry(struct ui_browser *self, unsigned row)
+bool ui_browser__is_current_entry(struct ui_browser *browser, unsigned row)
 {
-       return self->top_idx + row == self->index;
+       return browser->top_idx + row == browser->index;
 }
 
-void ui_browser__refresh_dimensions(struct ui_browser *self)
+void ui_browser__refresh_dimensions(struct ui_browser *browser)
 {
-       self->width = SLtt_Screen_Cols - 1;
-       self->height = SLtt_Screen_Rows - 2;
-       self->y = 1;
-       self->x = 0;
+       browser->width = SLtt_Screen_Cols - 1;
+       browser->height = SLtt_Screen_Rows - 2;
+       browser->y = 1;
+       browser->x = 0;
 }
 
 void ui_browser__handle_resize(struct ui_browser *browser)
@@ -225,10 +225,10 @@ bool ui_browser__dialog_yesno(struct ui_browser *browser, const char *text)
        return key == K_ENTER || toupper(key) == 'Y';
 }
 
-void ui_browser__reset_index(struct ui_browser *self)
+void ui_browser__reset_index(struct ui_browser *browser)
 {
-       self->index = self->top_idx = 0;
-       self->seek(self, 0, SEEK_SET);
+       browser->index = browser->top_idx = 0;
+       browser->seek(browser, 0, SEEK_SET);
 }
 
 void __ui_browser__show_title(struct ui_browser *browser, const char *title)
@@ -245,26 +245,26 @@ void ui_browser__show_title(struct ui_browser *browser, const char *title)
        pthread_mutex_unlock(&ui__lock);
 }
 
-int ui_browser__show(struct ui_browser *self, const char *title,
+int ui_browser__show(struct ui_browser *browser, const char *title,
                     const char *helpline, ...)
 {
        int err;
        va_list ap;
 
-       ui_browser__refresh_dimensions(self);
+       ui_browser__refresh_dimensions(browser);
 
        pthread_mutex_lock(&ui__lock);
-       __ui_browser__show_title(self, title);
+       __ui_browser__show_title(browser, title);
 
-       self->title = title;
-       free(self->helpline);
-       self->helpline = NULL;
+       browser->title = title;
+       free(browser->helpline);
+       browser->helpline = NULL;
 
        va_start(ap, helpline);
-       err = vasprintf(&self->helpline, helpline, ap);
+       err = vasprintf(&browser->helpline, helpline, ap);
        va_end(ap);
        if (err > 0)
-               ui_helpline__push(self->helpline);
+               ui_helpline__push(browser->helpline);
        pthread_mutex_unlock(&ui__lock);
        return err ? 0 : -1;
 }
@@ -350,7 +350,7 @@ void ui_browser__update_nr_entries(struct ui_browser *browser, u32 nr_entries)
        browser->seek(browser, browser->top_idx, SEEK_SET);
 }
 
-int ui_browser__run(struct ui_browser *self, int delay_secs)
+int ui_browser__run(struct ui_browser *browser, int delay_secs)
 {
        int err, key;
 
@@ -358,7 +358,7 @@ int ui_browser__run(struct ui_browser *self, int delay_secs)
                off_t offset;
 
                pthread_mutex_lock(&ui__lock);
-               err = __ui_browser__refresh(self);
+               err = __ui_browser__refresh(browser);
                SLsmg_refresh();
                pthread_mutex_unlock(&ui__lock);
                if (err < 0)
@@ -368,18 +368,18 @@ int ui_browser__run(struct ui_browser *self, int delay_secs)
 
                if (key == K_RESIZE) {
                        ui__refresh_dimensions(false);
-                       ui_browser__refresh_dimensions(self);
-                       __ui_browser__show_title(self, self->title);
-                       ui_helpline__puts(self->helpline);
+                       ui_browser__refresh_dimensions(browser);
+                       __ui_browser__show_title(browser, browser->title);
+                       ui_helpline__puts(browser->helpline);
                        continue;
                }
 
-               if (self->use_navkeypressed && !self->navkeypressed) {
+               if (browser->use_navkeypressed && !browser->navkeypressed) {
                        if (key == K_DOWN || key == K_UP ||
                            key == K_PGDN || key == K_PGUP ||
                            key == K_HOME || key == K_END ||
                            key == ' ') {
-                               self->navkeypressed = true;
+                               browser->navkeypressed = true;
                                continue;
                        } else
                                return key;
@@ -387,59 +387,59 @@ int ui_browser__run(struct ui_browser *self, int delay_secs)
 
                switch (key) {
                case K_DOWN:
-                       if (self->index == self->nr_entries - 1)
+                       if (browser->index == browser->nr_entries - 1)
                                break;
-                       ++self->index;
-                       if (self->index == self->top_idx + self->height) {
-                               ++self->top_idx;
-                               self->seek(self, +1, SEEK_CUR);
+                       ++browser->index;
+                       if (browser->index == browser->top_idx + browser->height) {
+                               ++browser->top_idx;
+                               browser->seek(browser, +1, SEEK_CUR);
                        }
                        break;
                case K_UP:
-                       if (self->index == 0)
+                       if (browser->index == 0)
                                break;
-                       --self->index;
-                       if (self->index < self->top_idx) {
-                               --self->top_idx;
-                               self->seek(self, -1, SEEK_CUR);
+                       --browser->index;
+                       if (browser->index < browser->top_idx) {
+                               --browser->top_idx;
+                               browser->seek(browser, -1, SEEK_CUR);
                        }
                        break;
                case K_PGDN:
                case ' ':
-                       if (self->top_idx + self->height > self->nr_entries - 1)
+                       if (browser->top_idx + browser->height > browser->nr_entries - 1)
                                break;
 
-                       offset = self->height;
-                       if (self->index + offset > self->nr_entries - 1)
-                               offset = self->nr_entries - 1 - self->index;
-                       self->index += offset;
-                       self->top_idx += offset;
-                       self->seek(self, +offset, SEEK_CUR);
+                       offset = browser->height;
+                       if (browser->index + offset > browser->nr_entries - 1)
+                               offset = browser->nr_entries - 1 - browser->index;
+                       browser->index += offset;
+                       browser->top_idx += offset;
+                       browser->seek(browser, +offset, SEEK_CUR);
                        break;
                case K_PGUP:
-                       if (self->top_idx == 0)
+                       if (browser->top_idx == 0)
                                break;
 
-                       if (self->top_idx < self->height)
-                               offset = self->top_idx;
+                       if (browser->top_idx < browser->height)
+                               offset = browser->top_idx;
                        else
-                               offset = self->height;
+                               offset = browser->height;
 
-                       self->index -= offset;
-                       self->top_idx -= offset;
-                       self->seek(self, -offset, SEEK_CUR);
+                       browser->index -= offset;
+                       browser->top_idx -= offset;
+                       browser->seek(browser, -offset, SEEK_CUR);
                        break;
                case K_HOME:
-                       ui_browser__reset_index(self);
+                       ui_browser__reset_index(browser);
                        break;
                case K_END:
-                       offset = self->height - 1;
-                       if (offset >= self->nr_entries)
-                               offset = self->nr_entries - 1;
+                       offset = browser->height - 1;
+                       if (offset >= browser->nr_entries)
+                               offset = browser->nr_entries - 1;
 
-                       self->index = self->nr_entries - 1;
-                       self->top_idx = self->index - offset;
-                       self->seek(self, -offset, SEEK_END);
+                       browser->index = browser->nr_entries - 1;
+                       browser->top_idx = browser->index - offset;
+                       browser->seek(browser, -offset, SEEK_END);
                        break;
                default:
                        return key;
@@ -448,22 +448,22 @@ int ui_browser__run(struct ui_browser *self, int delay_secs)
        return -1;
 }
 
-unsigned int ui_browser__list_head_refresh(struct ui_browser *self)
+unsigned int ui_browser__list_head_refresh(struct ui_browser *browser)
 {
        struct list_head *pos;
-       struct list_head *head = self->entries;
+       struct list_head *head = browser->entries;
        int row = 0;
 
-       if (self->top == NULL || self->top == self->entries)
-                self->top = ui_browser__list_head_filter_entries(self, head->next);
+       if (browser->top == NULL || browser->top == browser->entries)
+                browser->top = ui_browser__list_head_filter_entries(browser, head->next);
 
-       pos = self->top;
+       pos = browser->top;
 
        list_for_each_from(pos, head) {
-               if (!self->filter || !self->filter(self, pos)) {
-                       ui_browser__gotorc(self, row, 0);
-                       self->write(self, pos, row);
-                       if (++row == self->height)
+               if (!browser->filter || !browser->filter(browser, pos)) {
+                       ui_browser__gotorc(browser, row, 0);
+                       browser->write(browser, pos, row);
+                       if (++row == browser->height)
                                break;
                }
        }
@@ -708,4 +708,6 @@ void ui_browser__init(void)
                struct ui_browser__colorset *c = &ui_browser__colorsets[i++];
                sltt_set_color(c->colorset, c->name, c->fg, c->bg);
        }
+
+       annotate_browser__init();
 }
index dd96d82299022c0bb67924752177d3e6c2eaa096..af70314605e54e2468fe774822cdeecd2945cc7c 100644 (file)
@@ -69,4 +69,5 @@ void ui_browser__list_head_seek(struct ui_browser *self, off_t offset, int whenc
 unsigned int ui_browser__list_head_refresh(struct ui_browser *self);
 
 void ui_browser__init(void);
+void annotate_browser__init(void);
 #endif /* _PERF_UI_BROWSER_H_ */
index 6e0ef79be16907a781bcf31be96377671412f440..4deea6aaf9274f65887997fcd175fe9fd3bfc459 100644 (file)
@@ -19,6 +19,16 @@ struct browser_disasm_line {
        int             jump_sources;
 };
 
+static struct annotate_browser_opt {
+       bool hide_src_code,
+            use_offset,
+            jump_arrows,
+            show_nr_jumps;
+} annotate_browser__opts = {
+       .use_offset     = true,
+       .jump_arrows    = true,
+};
+
 struct annotate_browser {
        struct ui_browser b;
        struct rb_root    entries;
@@ -30,10 +40,6 @@ struct annotate_browser {
        int                 nr_entries;
        int                 max_jump_sources;
        int                 nr_jumps;
-       bool                hide_src_code;
-       bool                use_offset;
-       bool                jump_arrows;
-       bool                show_nr_jumps;
        bool                searching_backwards;
        u8                  addr_width;
        u8                  jumps_width;
@@ -48,11 +54,9 @@ static inline struct browser_disasm_line *disasm_line__browser(struct disasm_lin
        return (struct browser_disasm_line *)(dl + 1);
 }
 
-static bool disasm_line__filter(struct ui_browser *browser, void *entry)
+static bool disasm_line__filter(struct ui_browser *browser __used, void *entry)
 {
-       struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
-
-       if (ab->hide_src_code) {
+       if (annotate_browser__opts.hide_src_code) {
                struct disasm_line *dl = list_entry(entry, struct disasm_line, node);
                return dl->offset == -1;
        }
@@ -79,30 +83,30 @@ static int annotate_browser__set_jumps_percent_color(struct annotate_browser *br
         return ui_browser__set_color(&browser->b, color);
 }
 
-static void annotate_browser__write(struct ui_browser *self, void *entry, int row)
+static void annotate_browser__write(struct ui_browser *browser, void *entry, int row)
 {
-       struct annotate_browser *ab = container_of(self, struct annotate_browser, b);
+       struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
        struct disasm_line *dl = list_entry(entry, struct disasm_line, node);
        struct browser_disasm_line *bdl = disasm_line__browser(dl);
-       bool current_entry = ui_browser__is_current_entry(self, row);
-       bool change_color = (!ab->hide_src_code &&
-                            (!current_entry || (self->use_navkeypressed &&
-                                                !self->navkeypressed)));
-       int width = self->width, printed;
+       bool current_entry = ui_browser__is_current_entry(browser, row);
+       bool change_color = (!annotate_browser__opts.hide_src_code &&
+                            (!current_entry || (browser->use_navkeypressed &&
+                                                !browser->navkeypressed)));
+       int width = browser->width, printed;
        char bf[256];
 
        if (dl->offset != -1 && bdl->percent != 0.0) {
-               ui_browser__set_percent_color(self, bdl->percent, current_entry);
+               ui_browser__set_percent_color(browser, bdl->percent, current_entry);
                slsmg_printf("%6.2f ", bdl->percent);
        } else {
-               ui_browser__set_percent_color(self, 0, current_entry);
+               ui_browser__set_percent_color(browser, 0, current_entry);
                slsmg_write_nstring(" ", 7);
        }
 
        SLsmg_write_char(' ');
 
        /* The scroll bar isn't being used */
-       if (!self->navkeypressed)
+       if (!browser->navkeypressed)
                width += 1;
 
        if (!*dl->line)
@@ -116,14 +120,14 @@ static void annotate_browser__write(struct ui_browser *self, void *entry, int ro
                u64 addr = dl->offset;
                int color = -1;
 
-               if (!ab->use_offset)
+               if (!annotate_browser__opts.use_offset)
                        addr += ab->start;
 
-               if (!ab->use_offset) {
+               if (!annotate_browser__opts.use_offset) {
                        printed = scnprintf(bf, sizeof(bf), "%" PRIx64 ": ", addr);
                } else {
                        if (bdl->jump_sources) {
-                               if (ab->show_nr_jumps) {
+                               if (annotate_browser__opts.show_nr_jumps) {
                                        int prev;
                                        printed = scnprintf(bf, sizeof(bf), "%*d ",
                                                            ab->jumps_width,
@@ -131,7 +135,7 @@ static void annotate_browser__write(struct ui_browser *self, void *entry, int ro
                                        prev = annotate_browser__set_jumps_percent_color(ab, bdl->jump_sources,
                                                                                         current_entry);
                                        slsmg_write_nstring(bf, printed);
-                                       ui_browser__set_color(self, prev);
+                                       ui_browser__set_color(browser, prev);
                                }
 
                                printed = scnprintf(bf, sizeof(bf), "%*" PRIx64 ": ",
@@ -143,19 +147,19 @@ static void annotate_browser__write(struct ui_browser *self, void *entry, int ro
                }
 
                if (change_color)
-                       color = ui_browser__set_color(self, HE_COLORSET_ADDR);
+                       color = ui_browser__set_color(browser, HE_COLORSET_ADDR);
                slsmg_write_nstring(bf, printed);
                if (change_color)
-                       ui_browser__set_color(self, color);
+                       ui_browser__set_color(browser, color);
                if (dl->ins && dl->ins->ops->scnprintf) {
                        if (ins__is_jump(dl->ins)) {
                                bool fwd = dl->ops.target.offset > (u64)dl->offset;
 
-                               ui_browser__write_graph(self, fwd ? SLSMG_DARROW_CHAR :
+                               ui_browser__write_graph(browser, fwd ? SLSMG_DARROW_CHAR :
                                                                    SLSMG_UARROW_CHAR);
                                SLsmg_write_char(' ');
                        } else if (ins__is_call(dl->ins)) {
-                               ui_browser__write_graph(self, SLSMG_RARROW_CHAR);
+                               ui_browser__write_graph(browser, SLSMG_RARROW_CHAR);
                                SLsmg_write_char(' ');
                        } else {
                                slsmg_write_nstring(" ", 2);
@@ -164,12 +168,12 @@ static void annotate_browser__write(struct ui_browser *self, void *entry, int ro
                        if (strcmp(dl->name, "retq")) {
                                slsmg_write_nstring(" ", 2);
                        } else {
-                               ui_browser__write_graph(self, SLSMG_LARROW_CHAR);
+                               ui_browser__write_graph(browser, SLSMG_LARROW_CHAR);
                                SLsmg_write_char(' ');
                        }
                }
 
-               disasm_line__scnprintf(dl, bf, sizeof(bf), !ab->use_offset);
+               disasm_line__scnprintf(dl, bf, sizeof(bf), !annotate_browser__opts.use_offset);
                slsmg_write_nstring(bf, width - 10 - printed);
        }
 
@@ -184,7 +188,7 @@ static void annotate_browser__draw_current_jump(struct ui_browser *browser)
        struct browser_disasm_line *btarget, *bcursor;
        unsigned int from, to;
 
-       if (!cursor->ins || !ins__is_jump(cursor->ins) ||
+       if (!cursor || !cursor->ins || !ins__is_jump(cursor->ins) ||
            !disasm_line__has_offset(cursor))
                return;
 
@@ -195,7 +199,7 @@ static void annotate_browser__draw_current_jump(struct ui_browser *browser)
        bcursor = disasm_line__browser(cursor);
        btarget = disasm_line__browser(target);
 
-       if (ab->hide_src_code) {
+       if (annotate_browser__opts.hide_src_code) {
                from = bcursor->idx_asm;
                to = btarget->idx_asm;
        } else {
@@ -209,10 +213,9 @@ static void annotate_browser__draw_current_jump(struct ui_browser *browser)
 
 static unsigned int annotate_browser__refresh(struct ui_browser *browser)
 {
-       struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
        int ret = ui_browser__list_head_refresh(browser);
 
-       if (ab->jump_arrows)
+       if (annotate_browser__opts.jump_arrows)
                annotate_browser__draw_current_jump(browser);
 
        ui_browser__set_color(browser, HE_COLORSET_NORMAL);
@@ -272,27 +275,27 @@ static void disasm_rb_tree__insert(struct rb_root *root, struct browser_disasm_l
        rb_insert_color(&bdl->rb_node, root);
 }
 
-static void annotate_browser__set_top(struct annotate_browser *self,
+static void annotate_browser__set_top(struct annotate_browser *browser,
                                      struct disasm_line *pos, u32 idx)
 {
        unsigned back;
 
-       ui_browser__refresh_dimensions(&self->b);
-       back = self->b.height / 2;
-       self->b.top_idx = self->b.index = idx;
+       ui_browser__refresh_dimensions(&browser->b);
+       back = browser->b.height / 2;
+       browser->b.top_idx = browser->b.index = idx;
 
-       while (self->b.top_idx != 0 && back != 0) {
+       while (browser->b.top_idx != 0 && back != 0) {
                pos = list_entry(pos->node.prev, struct disasm_line, node);
 
-               if (disasm_line__filter(&self->b, &pos->node))
+               if (disasm_line__filter(&browser->b, &pos->node))
                        continue;
 
-               --self->b.top_idx;
+               --browser->b.top_idx;
                --back;
        }
 
-       self->b.top = pos;
-       self->b.navkeypressed = true;
+       browser->b.top = pos;
+       browser->b.navkeypressed = true;
 }
 
 static void annotate_browser__set_rb_top(struct annotate_browser *browser,
@@ -300,10 +303,14 @@ static void annotate_browser__set_rb_top(struct annotate_browser *browser,
 {
        struct browser_disasm_line *bpos;
        struct disasm_line *pos;
+       u32 idx;
 
        bpos = rb_entry(nd, struct browser_disasm_line, rb_node);
        pos = ((struct disasm_line *)bpos) - 1;
-       annotate_browser__set_top(browser, pos, bpos->idx);
+       idx = bpos->idx;
+       if (annotate_browser__opts.hide_src_code)
+               idx = bpos->idx_asm;
+       annotate_browser__set_top(browser, pos, idx);
        browser->curr_hot = nd;
 }
 
@@ -343,12 +350,12 @@ static bool annotate_browser__toggle_source(struct annotate_browser *browser)
        dl = list_entry(browser->b.top, struct disasm_line, node);
        bdl = disasm_line__browser(dl);
 
-       if (browser->hide_src_code) {
+       if (annotate_browser__opts.hide_src_code) {
                if (bdl->idx_asm < offset)
                        offset = bdl->idx;
 
                browser->b.nr_entries = browser->nr_entries;
-               browser->hide_src_code = false;
+               annotate_browser__opts.hide_src_code = false;
                browser->b.seek(&browser->b, -offset, SEEK_CUR);
                browser->b.top_idx = bdl->idx - offset;
                browser->b.index = bdl->idx;
@@ -363,7 +370,7 @@ static bool annotate_browser__toggle_source(struct annotate_browser *browser)
                        offset = bdl->idx_asm;
 
                browser->b.nr_entries = browser->nr_asm_entries;
-               browser->hide_src_code = true;
+               annotate_browser__opts.hide_src_code = true;
                browser->b.seek(&browser->b, -offset, SEEK_CUR);
                browser->b.top_idx = bdl->idx_asm - offset;
                browser->b.index = bdl->idx_asm;
@@ -372,6 +379,12 @@ static bool annotate_browser__toggle_source(struct annotate_browser *browser)
        return true;
 }
 
+static void annotate_browser__init_asm_mode(struct annotate_browser *browser)
+{
+       ui_browser__reset_index(&browser->b);
+       browser->b.nr_entries = browser->nr_asm_entries;
+}
+
 static bool annotate_browser__callq(struct annotate_browser *browser,
                                    int evidx, void (*timer)(void *arg),
                                    void *arg, int delay_secs)
@@ -574,33 +587,46 @@ bool annotate_browser__continue_search_reverse(struct annotate_browser *browser,
        return __annotate_browser__search_reverse(browser);
 }
 
-static int annotate_browser__run(struct annotate_browser *self, int evidx,
+static void annotate_browser__update_addr_width(struct annotate_browser *browser)
+{
+       if (annotate_browser__opts.use_offset)
+               browser->target_width = browser->min_addr_width;
+       else
+               browser->target_width = browser->max_addr_width;
+
+       browser->addr_width = browser->target_width;
+
+       if (annotate_browser__opts.show_nr_jumps)
+               browser->addr_width += browser->jumps_width + 1;
+}
+
+static int annotate_browser__run(struct annotate_browser *browser, int evidx,
                                 void(*timer)(void *arg),
                                 void *arg, int delay_secs)
 {
        struct rb_node *nd = NULL;
-       struct map_symbol *ms = self->b.priv;
+       struct map_symbol *ms = browser->b.priv;
        struct symbol *sym = ms->sym;
        const char *help = "Press 'h' for help on key bindings";
        int key;
 
-       if (ui_browser__show(&self->b, sym->name, help) < 0)
+       if (ui_browser__show(&browser->b, sym->name, help) < 0)
                return -1;
 
-       annotate_browser__calc_percent(self, evidx);
+       annotate_browser__calc_percent(browser, evidx);
 
-       if (self->curr_hot) {
-               annotate_browser__set_rb_top(self, self->curr_hot);
-               self->b.navkeypressed = false;
+       if (browser->curr_hot) {
+               annotate_browser__set_rb_top(browser, browser->curr_hot);
+               browser->b.navkeypressed = false;
        }
 
-       nd = self->curr_hot;
+       nd = browser->curr_hot;
 
        while (1) {
-               key = ui_browser__run(&self->b, delay_secs);
+               key = ui_browser__run(&browser->b, delay_secs);
 
                if (delay_secs != 0) {
-                       annotate_browser__calc_percent(self, evidx);
+                       annotate_browser__calc_percent(browser, evidx);
                        /*
                         * Current line focus got out of the list of most active
                         * lines, NULL it so that if TAB|UNTAB is pressed, we
@@ -622,21 +648,21 @@ static int annotate_browser__run(struct annotate_browser *self, int evidx,
                        if (nd != NULL) {
                                nd = rb_prev(nd);
                                if (nd == NULL)
-                                       nd = rb_last(&self->entries);
+                                       nd = rb_last(&browser->entries);
                        } else
-                               nd = self->curr_hot;
+                               nd = browser->curr_hot;
                        break;
                case K_UNTAB:
                        if (nd != NULL)
                                nd = rb_next(nd);
                                if (nd == NULL)
-                                       nd = rb_first(&self->entries);
+                                       nd = rb_first(&browser->entries);
                        else
-                               nd = self->curr_hot;
+                               nd = browser->curr_hot;
                        break;
                case K_F1:
                case 'h':
-                       ui_browser__help_window(&self->b,
+                       ui_browser__help_window(&browser->b,
                "UP/DOWN/PGUP\n"
                "PGDN/SPACE    Navigate\n"
                "q/ESC/CTRL+C  Exit\n\n"
@@ -652,57 +678,62 @@ static int annotate_browser__run(struct annotate_browser *self, int evidx,
                "?             Search previous string\n");
                        continue;
                case 'H':
-                       nd = self->curr_hot;
+                       nd = browser->curr_hot;
                        break;
                case 's':
-                       if (annotate_browser__toggle_source(self))
+                       if (annotate_browser__toggle_source(browser))
                                ui_helpline__puts(help);
                        continue;
                case 'o':
-                       self->use_offset = !self->use_offset;
-                       if (self->use_offset)
-                               self->target_width = self->min_addr_width;
-                       else
-                               self->target_width = self->max_addr_width;
-update_addr_width:
-                       self->addr_width = self->target_width;
-                       if (self->show_nr_jumps)
-                               self->addr_width += self->jumps_width + 1;
+                       annotate_browser__opts.use_offset = !annotate_browser__opts.use_offset;
+                       annotate_browser__update_addr_width(browser);
                        continue;
                case 'j':
-                       self->jump_arrows = !self->jump_arrows;
+                       annotate_browser__opts.jump_arrows = !annotate_browser__opts.jump_arrows;
                        continue;
                case 'J':
-                       self->show_nr_jumps = !self->show_nr_jumps;
-                       goto update_addr_width;
+                       annotate_browser__opts.show_nr_jumps = !annotate_browser__opts.show_nr_jumps;
+                       annotate_browser__update_addr_width(browser);
+                       continue;
                case '/':
-                       if (annotate_browser__search(self, delay_secs)) {
+                       if (annotate_browser__search(browser, delay_secs)) {
 show_help:
                                ui_helpline__puts(help);
                        }
                        continue;
                case 'n':
-                       if (self->searching_backwards ?
-                           annotate_browser__continue_search_reverse(self, delay_secs) :
-                           annotate_browser__continue_search(self, delay_secs))
+                       if (browser->searching_backwards ?
+                           annotate_browser__continue_search_reverse(browser, delay_secs) :
+                           annotate_browser__continue_search(browser, delay_secs))
                                goto show_help;
                        continue;
                case '?':
-                       if (annotate_browser__search_reverse(self, delay_secs))
+                       if (annotate_browser__search_reverse(browser, delay_secs))
                                goto show_help;
                        continue;
+               case 'D': {
+                       static int seq;
+                       ui_helpline__pop();
+                       ui_helpline__fpush("%d: nr_ent=%d, height=%d, idx=%d, top_idx=%d, nr_asm_entries=%d",
+                                          seq++, browser->b.nr_entries,
+                                          browser->b.height,
+                                          browser->b.index,
+                                          browser->b.top_idx,
+                                          browser->nr_asm_entries);
+               }
+                       continue;
                case K_ENTER:
                case K_RIGHT:
-                       if (self->selection == NULL)
+                       if (browser->selection == NULL)
                                ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
-                       else if (self->selection->offset == -1)
+                       else if (browser->selection->offset == -1)
                                ui_helpline__puts("Actions are only available for assembly lines.");
-                       else if (!self->selection->ins) {
-                               if (strcmp(self->selection->name, "retq"))
+                       else if (!browser->selection->ins) {
+                               if (strcmp(browser->selection->name, "retq"))
                                        goto show_sup_ins;
                                goto out;
-                       } else if (!(annotate_browser__jump(self) ||
-                                    annotate_browser__callq(self, evidx, timer, arg, delay_secs))) {
+                       } else if (!(annotate_browser__jump(browser) ||
+                                    annotate_browser__callq(browser, evidx, timer, arg, delay_secs))) {
 show_sup_ins:
                                ui_helpline__puts("Actions are only available for 'callq', 'retq' & jump instructions.");
                        }
@@ -717,10 +748,10 @@ show_sup_ins:
                }
 
                if (nd != NULL)
-                       annotate_browser__set_rb_top(self, nd);
+                       annotate_browser__set_rb_top(browser, nd);
        }
 out:
-       ui_browser__hide(&self->b);
+       ui_browser__hide(&browser->b);
        return key;
 }
 
@@ -797,8 +828,6 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
                        .priv    = &ms,
                        .use_navkeypressed = true,
                },
-               .use_offset = true,
-               .jump_arrows = true,
        };
        int ret = -1;
 
@@ -855,6 +884,12 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
        browser.b.nr_entries = browser.nr_entries;
        browser.b.entries = &notes->src->source,
        browser.b.width += 18; /* Percentage */
+
+       if (annotate_browser__opts.hide_src_code)
+               annotate_browser__init_asm_mode(&browser);
+
+       annotate_browser__update_addr_width(&browser);
+
        ret = annotate_browser__run(&browser, evidx, timer, arg, delay_secs);
        list_for_each_entry_safe(pos, n, &notes->src->source, node) {
                list_del(&pos->node);
@@ -865,3 +900,52 @@ out_free_offsets:
        free(browser.offsets);
        return ret;
 }
+
+#define ANNOTATE_CFG(n) \
+       { .name = #n, .value = &annotate_browser__opts.n, }
+       
+/*
+ * Keep the entries sorted, they are bsearch'ed
+ */
+static struct annotate__config {
+       const char *name;
+       bool *value;
+} annotate__configs[] = {
+       ANNOTATE_CFG(hide_src_code),
+       ANNOTATE_CFG(jump_arrows),
+       ANNOTATE_CFG(show_nr_jumps),
+       ANNOTATE_CFG(use_offset),
+};
+
+#undef ANNOTATE_CFG
+
+static int annotate_config__cmp(const void *name, const void *cfgp)
+{
+       const struct annotate__config *cfg = cfgp;
+
+       return strcmp(name, cfg->name);
+}
+
+static int annotate__config(const char *var, const char *value, void *data __used)
+{
+       struct annotate__config *cfg;
+       const char *name;
+
+       if (prefixcmp(var, "annotate.") != 0)
+               return 0;
+
+       name = var + 9;
+       cfg = bsearch(name, annotate__configs, ARRAY_SIZE(annotate__configs),
+                     sizeof(struct annotate__config), annotate_config__cmp);
+
+       if (cfg == NULL)
+               return -1;
+
+       *cfg->value = perf_config_bool(name, value);
+       return 0;
+}
+
+void annotate_browser__init(void)
+{
+       perf_config(annotate__config, NULL);
+}
index a372a4b026354b4d9f3984bc7fd3b96a5523f95a..53f6697d014e788396b474c6be957893b8fd09ca 100644 (file)
@@ -26,21 +26,21 @@ struct hist_browser {
        bool                 has_symbols;
 };
 
-static int hists__browser_title(struct hists *self, char *bf, size_t size,
+static int hists__browser_title(struct hists *hists, char *bf, size_t size,
                                const char *ev_name);
 
-static void hist_browser__refresh_dimensions(struct hist_browser *self)
+static void hist_browser__refresh_dimensions(struct hist_browser *browser)
 {
        /* 3 == +/- toggle symbol before actual hist_entry rendering */
-       self->b.width = 3 + (hists__sort_list_width(self->hists) +
+       browser->b.width = 3 + (hists__sort_list_width(browser->hists) +
                             sizeof("[k]"));
 }
 
-static void hist_browser__reset(struct hist_browser *self)
+static void hist_browser__reset(struct hist_browser *browser)
 {
-       self->b.nr_entries = self->hists->nr_entries;
-       hist_browser__refresh_dimensions(self);
-       ui_browser__reset_index(&self->b);
+       browser->b.nr_entries = browser->hists->nr_entries;
+       hist_browser__refresh_dimensions(browser);
+       ui_browser__reset_index(&browser->b);
 }
 
 static char tree__folded_sign(bool unfolded)
@@ -48,32 +48,32 @@ static char tree__folded_sign(bool unfolded)
        return unfolded ? '-' : '+';
 }
 
-static char map_symbol__folded(const struct map_symbol *self)
+static char map_symbol__folded(const struct map_symbol *ms)
 {
-       return self->has_children ? tree__folded_sign(self->unfolded) : ' ';
+       return ms->has_children ? tree__folded_sign(ms->unfolded) : ' ';
 }
 
-static char hist_entry__folded(const struct hist_entry *self)
+static char hist_entry__folded(const struct hist_entry *he)
 {
-       return map_symbol__folded(&self->ms);
+       return map_symbol__folded(&he->ms);
 }
 
-static char callchain_list__folded(const struct callchain_list *self)
+static char callchain_list__folded(const struct callchain_list *cl)
 {
-       return map_symbol__folded(&self->ms);
+       return map_symbol__folded(&cl->ms);
 }
 
-static void map_symbol__set_folding(struct map_symbol *self, bool unfold)
+static void map_symbol__set_folding(struct map_symbol *ms, bool unfold)
 {
-       self->unfolded = unfold ? self->has_children : false;
+       ms->unfolded = unfold ? ms->has_children : false;
 }
 
-static int callchain_node__count_rows_rb_tree(struct callchain_node *self)
+static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
 {
        int n = 0;
        struct rb_node *nd;
 
-       for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) {
+       for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
                struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
                struct callchain_list *chain;
                char folded_sign = ' '; /* No children */
@@ -123,23 +123,23 @@ static int callchain__count_rows(struct rb_root *chain)
        return n;
 }
 
-static bool map_symbol__toggle_fold(struct map_symbol *self)
+static bool map_symbol__toggle_fold(struct map_symbol *ms)
 {
-       if (!self)
+       if (!ms)
                return false;
 
-       if (!self->has_children)
+       if (!ms->has_children)
                return false;
 
-       self->unfolded = !self->unfolded;
+       ms->unfolded = !ms->unfolded;
        return true;
 }
 
-static void callchain_node__init_have_children_rb_tree(struct callchain_node *self)
+static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
 {
-       struct rb_node *nd = rb_first(&self->rb_root);
+       struct rb_node *nd = rb_first(&node->rb_root);
 
-       for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) {
+       for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
                struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
                struct callchain_list *chain;
                bool first = true;
@@ -158,49 +158,49 @@ static void callchain_node__init_have_children_rb_tree(struct callchain_node *se
        }
 }
 
-static void callchain_node__init_have_children(struct callchain_node *self)
+static void callchain_node__init_have_children(struct callchain_node *node)
 {
        struct callchain_list *chain;
 
-       list_for_each_entry(chain, &self->val, list)
-               chain->ms.has_children = !RB_EMPTY_ROOT(&self->rb_root);
+       list_for_each_entry(chain, &node->val, list)
+               chain->ms.has_children = !RB_EMPTY_ROOT(&node->rb_root);
 
-       callchain_node__init_have_children_rb_tree(self);
+       callchain_node__init_have_children_rb_tree(node);
 }
 
-static void callchain__init_have_children(struct rb_root *self)
+static void callchain__init_have_children(struct rb_root *root)
 {
        struct rb_node *nd;
 
-       for (nd = rb_first(self); nd; nd = rb_next(nd)) {
+       for (nd = rb_first(root); nd; nd = rb_next(nd)) {
                struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
                callchain_node__init_have_children(node);
        }
 }
 
-static void hist_entry__init_have_children(struct hist_entry *self)
+static void hist_entry__init_have_children(struct hist_entry *he)
 {
-       if (!self->init_have_children) {
-               self->ms.has_children = !RB_EMPTY_ROOT(&self->sorted_chain);
-               callchain__init_have_children(&self->sorted_chain);
-               self->init_have_children = true;
+       if (!he->init_have_children) {
+               he->ms.has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
+               callchain__init_have_children(&he->sorted_chain);
+               he->init_have_children = true;
        }
 }
 
-static bool hist_browser__toggle_fold(struct hist_browser *self)
+static bool hist_browser__toggle_fold(struct hist_browser *browser)
 {
-       if (map_symbol__toggle_fold(self->selection)) {
-               struct hist_entry *he = self->he_selection;
+       if (map_symbol__toggle_fold(browser->selection)) {
+               struct hist_entry *he = browser->he_selection;
 
                hist_entry__init_have_children(he);
-               self->hists->nr_entries -= he->nr_rows;
+               browser->hists->nr_entries -= he->nr_rows;
 
                if (he->ms.unfolded)
                        he->nr_rows = callchain__count_rows(&he->sorted_chain);
                else
                        he->nr_rows = 0;
-               self->hists->nr_entries += he->nr_rows;
-               self->b.nr_entries = self->hists->nr_entries;
+               browser->hists->nr_entries += he->nr_rows;
+               browser->b.nr_entries = browser->hists->nr_entries;
 
                return true;
        }
@@ -209,12 +209,12 @@ static bool hist_browser__toggle_fold(struct hist_browser *self)
        return false;
 }
 
-static int callchain_node__set_folding_rb_tree(struct callchain_node *self, bool unfold)
+static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
 {
        int n = 0;
        struct rb_node *nd;
 
-       for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) {
+       for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
                struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
                struct callchain_list *chain;
                bool has_children = false;
@@ -263,37 +263,37 @@ static int callchain__set_folding(struct rb_root *chain, bool unfold)
        return n;
 }
 
-static void hist_entry__set_folding(struct hist_entry *self, bool unfold)
+static void hist_entry__set_folding(struct hist_entry *he, bool unfold)
 {
-       hist_entry__init_have_children(self);
-       map_symbol__set_folding(&self->ms, unfold);
+       hist_entry__init_have_children(he);
+       map_symbol__set_folding(&he->ms, unfold);
 
-       if (self->ms.has_children) {
-               int n = callchain__set_folding(&self->sorted_chain, unfold);
-               self->nr_rows = unfold ? n : 0;
+       if (he->ms.has_children) {
+               int n = callchain__set_folding(&he->sorted_chain, unfold);
+               he->nr_rows = unfold ? n : 0;
        } else
-               self->nr_rows = 0;
+               he->nr_rows = 0;
 }
 
-static void hists__set_folding(struct hists *self, bool unfold)
+static void hists__set_folding(struct hists *hists, bool unfold)
 {
        struct rb_node *nd;
 
-       self->nr_entries = 0;
+       hists->nr_entries = 0;
 
-       for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) {
+       for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
                struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
                hist_entry__set_folding(he, unfold);
-               self->nr_entries += 1 + he->nr_rows;
+               hists->nr_entries += 1 + he->nr_rows;
        }
 }
 
-static void hist_browser__set_folding(struct hist_browser *self, bool unfold)
+static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
 {
-       hists__set_folding(self->hists, unfold);
-       self->b.nr_entries = self->hists->nr_entries;
+       hists__set_folding(browser->hists, unfold);
+       browser->b.nr_entries = browser->hists->nr_entries;
        /* Go to the start, we may be way after valid entries after a collapse */
-       ui_browser__reset_index(&self->b);
+       ui_browser__reset_index(&browser->b);
 }
 
 static void ui_browser__warn_lost_events(struct ui_browser *browser)
@@ -305,64 +305,64 @@ static void ui_browser__warn_lost_events(struct ui_browser *browser)
                "Or reduce the sampling frequency.");
 }
 
-static int hist_browser__run(struct hist_browser *self, const char *ev_name,
+static int hist_browser__run(struct hist_browser *browser, const char *ev_name,
                             void(*timer)(void *arg), void *arg, int delay_secs)
 {
        int key;
        char title[160];
 
-       self->b.entries = &self->hists->entries;
-       self->b.nr_entries = self->hists->nr_entries;
+       browser->b.entries = &browser->hists->entries;
+       browser->b.nr_entries = browser->hists->nr_entries;
 
-       hist_browser__refresh_dimensions(self);
-       hists__browser_title(self->hists, title, sizeof(title), ev_name);
+       hist_browser__refresh_dimensions(browser);
+       hists__browser_title(browser->hists, title, sizeof(title), ev_name);
 
-       if (ui_browser__show(&self->b, title,
+       if (ui_browser__show(&browser->b, title,
                             "Press '?' for help on key bindings") < 0)
                return -1;
 
        while (1) {
-               key = ui_browser__run(&self->b, delay_secs);
+               key = ui_browser__run(&browser->b, delay_secs);
 
                switch (key) {
                case K_TIMER:
                        timer(arg);
-                       ui_browser__update_nr_entries(&self->b, self->hists->nr_entries);
+                       ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
 
-                       if (self->hists->stats.nr_lost_warned !=
-                           self->hists->stats.nr_events[PERF_RECORD_LOST]) {
-                               self->hists->stats.nr_lost_warned =
-                                       self->hists->stats.nr_events[PERF_RECORD_LOST];
-                               ui_browser__warn_lost_events(&self->b);
+                       if (browser->hists->stats.nr_lost_warned !=
+                           browser->hists->stats.nr_events[PERF_RECORD_LOST]) {
+                               browser->hists->stats.nr_lost_warned =
+                                       browser->hists->stats.nr_events[PERF_RECORD_LOST];
+                               ui_browser__warn_lost_events(&browser->b);
                        }
 
-                       hists__browser_title(self->hists, title, sizeof(title), ev_name);
-                       ui_browser__show_title(&self->b, title);
+                       hists__browser_title(browser->hists, title, sizeof(title), ev_name);
+                       ui_browser__show_title(&browser->b, title);
                        continue;
                case 'D': { /* Debug */
                        static int seq;
-                       struct hist_entry *h = rb_entry(self->b.top,
+                       struct hist_entry *h = rb_entry(browser->b.top,
                                                        struct hist_entry, rb_node);
                        ui_helpline__pop();
                        ui_helpline__fpush("%d: nr_ent=(%d,%d), height=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
-                                          seq++, self->b.nr_entries,
-                                          self->hists->nr_entries,
-                                          self->b.height,
-                                          self->b.index,
-                                          self->b.top_idx,
+                                          seq++, browser->b.nr_entries,
+                                          browser->hists->nr_entries,
+                                          browser->b.height,
+                                          browser->b.index,
+                                          browser->b.top_idx,
                                           h->row_offset, h->nr_rows);
                }
                        break;
                case 'C':
                        /* Collapse the whole world. */
-                       hist_browser__set_folding(self, false);
+                       hist_browser__set_folding(browser, false);
                        break;
                case 'E':
                        /* Expand the whole world. */
-                       hist_browser__set_folding(self, true);
+                       hist_browser__set_folding(browser, true);
                        break;
                case K_ENTER:
-                       if (hist_browser__toggle_fold(self))
+                       if (hist_browser__toggle_fold(browser))
                                break;
                        /* fall thru */
                default:
@@ -370,23 +370,23 @@ static int hist_browser__run(struct hist_browser *self, const char *ev_name,
                }
        }
 out:
-       ui_browser__hide(&self->b);
+       ui_browser__hide(&browser->b);
        return key;
 }
 
-static char *callchain_list__sym_name(struct callchain_list *self,
+static char *callchain_list__sym_name(struct callchain_list *cl,
                                      char *bf, size_t bfsize)
 {
-       if (self->ms.sym)
-               return self->ms.sym->name;
+       if (cl->ms.sym)
+               return cl->ms.sym->name;
 
-       snprintf(bf, bfsize, "%#" PRIx64, self->ip);
+       snprintf(bf, bfsize, "%#" PRIx64, cl->ip);
        return bf;
 }
 
 #define LEVEL_OFFSET_STEP 3
 
-static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *self,
+static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *browser,
                                                     struct callchain_node *chain_node,
                                                     u64 total, int level,
                                                     unsigned short row,
@@ -444,21 +444,21 @@ static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *self,
                        }
 
                        color = HE_COLORSET_NORMAL;
-                       width = self->b.width - (offset + extra_offset + 2);
-                       if (ui_browser__is_current_entry(&self->b, row)) {
-                               self->selection = &chain->ms;
+                       width = browser->b.width - (offset + extra_offset + 2);
+                       if (ui_browser__is_current_entry(&browser->b, row)) {
+                               browser->selection = &chain->ms;
                                color = HE_COLORSET_SELECTED;
                                *is_current_entry = true;
                        }
 
-                       ui_browser__set_color(&self->b, color);
-                       ui_browser__gotorc(&self->b, row, 0);
+                       ui_browser__set_color(&browser->b, color);
+                       ui_browser__gotorc(&browser->b, row, 0);
                        slsmg_write_nstring(" ", offset + extra_offset);
                        slsmg_printf("%c ", folded_sign);
                        slsmg_write_nstring(str, width);
                        free(alloc_str);
 
-                       if (++row == self->b.height)
+                       if (++row == browser->b.height)
                                goto out;
 do_next:
                        if (folded_sign == '+')
@@ -467,11 +467,11 @@ do_next:
 
                if (folded_sign == '-') {
                        const int new_level = level + (extra_offset ? 2 : 1);
-                       row += hist_browser__show_callchain_node_rb_tree(self, child, new_total,
+                       row += hist_browser__show_callchain_node_rb_tree(browser, child, new_total,
                                                                         new_level, row, row_offset,
                                                                         is_current_entry);
                }
-               if (row == self->b.height)
+               if (row == browser->b.height)
                        goto out;
                node = next;
        }
@@ -479,7 +479,7 @@ out:
        return row - first_row;
 }
 
-static int hist_browser__show_callchain_node(struct hist_browser *self,
+static int hist_browser__show_callchain_node(struct hist_browser *browser,
                                             struct callchain_node *node,
                                             int level, unsigned short row,
                                             off_t *row_offset,
@@ -488,7 +488,7 @@ static int hist_browser__show_callchain_node(struct hist_browser *self,
        struct callchain_list *chain;
        int first_row = row,
             offset = level * LEVEL_OFFSET_STEP,
-            width = self->b.width - offset;
+            width = browser->b.width - offset;
        char folded_sign = ' ';
 
        list_for_each_entry(chain, &node->val, list) {
@@ -503,26 +503,26 @@ static int hist_browser__show_callchain_node(struct hist_browser *self,
                }
 
                color = HE_COLORSET_NORMAL;
-               if (ui_browser__is_current_entry(&self->b, row)) {
-                       self->selection = &chain->ms;
+               if (ui_browser__is_current_entry(&browser->b, row)) {
+                       browser->selection = &chain->ms;
                        color = HE_COLORSET_SELECTED;
                        *is_current_entry = true;
                }
 
                s = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
-               ui_browser__gotorc(&self->b, row, 0);
-               ui_browser__set_color(&self->b, color);
+               ui_browser__gotorc(&browser->b, row, 0);
+               ui_browser__set_color(&browser->b, color);
                slsmg_write_nstring(" ", offset);
                slsmg_printf("%c ", folded_sign);
                slsmg_write_nstring(s, width - 2);
 
-               if (++row == self->b.height)
+               if (++row == browser->b.height)
                        goto out;
        }
 
        if (folded_sign == '-')
-               row += hist_browser__show_callchain_node_rb_tree(self, node,
-                                                                self->hists->stats.total_period,
+               row += hist_browser__show_callchain_node_rb_tree(browser, node,
+                                                                browser->hists->stats.total_period,
                                                                 level + 1, row,
                                                                 row_offset,
                                                                 is_current_entry);
@@ -530,7 +530,7 @@ out:
        return row - first_row;
 }
 
-static int hist_browser__show_callchain(struct hist_browser *self,
+static int hist_browser__show_callchain(struct hist_browser *browser,
                                        struct rb_root *chain,
                                        int level, unsigned short row,
                                        off_t *row_offset,
@@ -542,31 +542,31 @@ static int hist_browser__show_callchain(struct hist_browser *self,
        for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
                struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
 
-               row += hist_browser__show_callchain_node(self, node, level,
+               row += hist_browser__show_callchain_node(browser, node, level,
                                                         row, row_offset,
                                                         is_current_entry);
-               if (row == self->b.height)
+               if (row == browser->b.height)
                        break;
        }
 
        return row - first_row;
 }
 
-static int hist_browser__show_entry(struct hist_browser *self,
+static int hist_browser__show_entry(struct hist_browser *browser,
                                    struct hist_entry *entry,
                                    unsigned short row)
 {
        char s[256];
        double percent;
        int printed = 0;
-       int width = self->b.width - 6; /* The percentage */
+       int width = browser->b.width - 6; /* The percentage */
        char folded_sign = ' ';
-       bool current_entry = ui_browser__is_current_entry(&self->b, row);
+       bool current_entry = ui_browser__is_current_entry(&browser->b, row);
        off_t row_offset = entry->row_offset;
 
        if (current_entry) {
-               self->he_selection = entry;
-               self->selection = &entry->ms;
+               browser->he_selection = entry;
+               browser->selection = &entry->ms;
        }
 
        if (symbol_conf.use_callchain) {
@@ -575,11 +575,11 @@ static int hist_browser__show_entry(struct hist_browser *self,
        }
 
        if (row_offset == 0) {
-               hist_entry__snprintf(entry, s, sizeof(s), self->hists);
-               percent = (entry->period * 100.0) / self->hists->stats.total_period;
+               hist_entry__snprintf(entry, s, sizeof(s), browser->hists);
+               percent = (entry->period * 100.0) / browser->hists->stats.total_period;
 
-               ui_browser__set_percent_color(&self->b, percent, current_entry);
-               ui_browser__gotorc(&self->b, row, 0);
+               ui_browser__set_percent_color(&browser->b, percent, current_entry);
+               ui_browser__gotorc(&browser->b, row, 0);
                if (symbol_conf.use_callchain) {
                        slsmg_printf("%c ", folded_sign);
                        width -= 2;
@@ -588,11 +588,11 @@ static int hist_browser__show_entry(struct hist_browser *self,
                slsmg_printf(" %5.2f%%", percent);
 
                /* The scroll bar isn't being used */
-               if (!self->b.navkeypressed)
+               if (!browser->b.navkeypressed)
                        width += 1;
 
-               if (!current_entry || !self->b.navkeypressed)
-                       ui_browser__set_color(&self->b, HE_COLORSET_NORMAL);
+               if (!current_entry || !browser->b.navkeypressed)
+                       ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
 
                if (symbol_conf.show_nr_samples) {
                        slsmg_printf(" %11u", entry->nr_events);
@@ -610,12 +610,12 @@ static int hist_browser__show_entry(struct hist_browser *self,
        } else
                --row_offset;
 
-       if (folded_sign == '-' && row != self->b.height) {
-               printed += hist_browser__show_callchain(self, &entry->sorted_chain,
+       if (folded_sign == '-' && row != browser->b.height) {
+               printed += hist_browser__show_callchain(browser, &entry->sorted_chain,
                                                        1, row, &row_offset,
                                                        &current_entry);
                if (current_entry)
-                       self->he_selection = entry;
+                       browser->he_selection = entry;
        }
 
        return printed;
@@ -631,22 +631,22 @@ static void ui_browser__hists_init_top(struct ui_browser *browser)
        }
 }
 
-static unsigned int hist_browser__refresh(struct ui_browser *self)
+static unsigned int hist_browser__refresh(struct ui_browser *browser)
 {
        unsigned row = 0;
        struct rb_node *nd;
-       struct hist_browser *hb = container_of(self, struct hist_browser, b);
+       struct hist_browser *hb = container_of(browser, struct hist_browser, b);
 
-       ui_browser__hists_init_top(self);
+       ui_browser__hists_init_top(browser);
 
-       for (nd = self->top; nd; nd = rb_next(nd)) {
+       for (nd = browser->top; nd; nd = rb_next(nd)) {
                struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
 
                if (h->filtered)
                        continue;
 
                row += hist_browser__show_entry(hb, h, row);
-               if (row == self->height)
+               if (row == browser->height)
                        break;
        }
 
@@ -679,27 +679,27 @@ static struct rb_node *hists__filter_prev_entries(struct rb_node *nd)
        return NULL;
 }
 
-static void ui_browser__hists_seek(struct ui_browser *self,
+static void ui_browser__hists_seek(struct ui_browser *browser,
                                   off_t offset, int whence)
 {
        struct hist_entry *h;
        struct rb_node *nd;
        bool first = true;
 
-       if (self->nr_entries == 0)
+       if (browser->nr_entries == 0)
                return;
 
-       ui_browser__hists_init_top(self);
+       ui_browser__hists_init_top(browser);
 
        switch (whence) {
        case SEEK_SET:
-               nd = hists__filter_entries(rb_first(self->entries));
+               nd = hists__filter_entries(rb_first(browser->entries));
                break;
        case SEEK_CUR:
-               nd = self->top;
+               nd = browser->top;
                goto do_offset;
        case SEEK_END:
-               nd = hists__filter_prev_entries(rb_last(self->entries));
+               nd = hists__filter_prev_entries(rb_last(browser->entries));
                first = false;
                break;
        default:
@@ -710,7 +710,7 @@ static void ui_browser__hists_seek(struct ui_browser *self,
         * Moves not relative to the first visible entry invalidates its
         * row_offset:
         */
-       h = rb_entry(self->top, struct hist_entry, rb_node);
+       h = rb_entry(browser->top, struct hist_entry, rb_node);
        h->row_offset = 0;
 
        /*
@@ -738,7 +738,7 @@ do_offset:
                                } else {
                                        h->row_offset += offset;
                                        offset = 0;
-                                       self->top = nd;
+                                       browser->top = nd;
                                        break;
                                }
                        }
@@ -746,7 +746,7 @@ do_offset:
                        if (nd == NULL)
                                break;
                        --offset;
-                       self->top = nd;
+                       browser->top = nd;
                } while (offset != 0);
        } else if (offset < 0) {
                while (1) {
@@ -759,7 +759,7 @@ do_offset:
                                        } else {
                                                h->row_offset += offset;
                                                offset = 0;
-                                               self->top = nd;
+                                               browser->top = nd;
                                                break;
                                        }
                                } else {
@@ -769,7 +769,7 @@ do_offset:
                                        } else {
                                                h->row_offset = h->nr_rows + offset;
                                                offset = 0;
-                                               self->top = nd;
+                                               browser->top = nd;
                                                break;
                                        }
                                }
@@ -779,7 +779,7 @@ do_offset:
                        if (nd == NULL)
                                break;
                        ++offset;
-                       self->top = nd;
+                       browser->top = nd;
                        if (offset == 0) {
                                /*
                                 * Last unfiltered hist_entry, check if it is
@@ -794,7 +794,7 @@ do_offset:
                        first = false;
                }
        } else {
-               self->top = nd;
+               browser->top = nd;
                h = rb_entry(nd, struct hist_entry, rb_node);
                h->row_offset = 0;
        }
@@ -802,46 +802,46 @@ do_offset:
 
 static struct hist_browser *hist_browser__new(struct hists *hists)
 {
-       struct hist_browser *self = zalloc(sizeof(*self));
+       struct hist_browser *browser = zalloc(sizeof(*browser));
 
-       if (self) {
-               self->hists = hists;
-               self->b.refresh = hist_browser__refresh;
-               self->b.seek = ui_browser__hists_seek;
-               self->b.use_navkeypressed = true;
+       if (browser) {
+               browser->hists = hists;
+               browser->b.refresh = hist_browser__refresh;
+               browser->b.seek = ui_browser__hists_seek;
+               browser->b.use_navkeypressed = true;
                if (sort__branch_mode == 1)
-                       self->has_symbols = sort_sym_from.list.next != NULL;
+                       browser->has_symbols = sort_sym_from.list.next != NULL;
                else
-                       self->has_symbols = sort_sym.list.next != NULL;
+                       browser->has_symbols = sort_sym.list.next != NULL;
        }
 
-       return self;
+       return browser;
 }
 
-static void hist_browser__delete(struct hist_browser *self)
+static void hist_browser__delete(struct hist_browser *browser)
 {
-       free(self);
+       free(browser);
 }
 
-static struct hist_entry *hist_browser__selected_entry(struct hist_browser *self)
+static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
 {
-       return self->he_selection;
+       return browser->he_selection;
 }
 
-static struct thread *hist_browser__selected_thread(struct hist_browser *self)
+static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
 {
-       return self->he_selection->thread;
+       return browser->he_selection->thread;
 }
 
-static int hists__browser_title(struct hists *self, char *bf, size_t size,
+static int hists__browser_title(struct hists *hists, char *bf, size_t size,
                                const char *ev_name)
 {
        char unit;
        int printed;
-       const struct dso *dso = self->dso_filter;
-       const struct thread *thread = self->thread_filter;
-       unsigned long nr_samples = self->stats.nr_events[PERF_RECORD_SAMPLE];
-       u64 nr_events = self->stats.total_period;
+       const struct dso *dso = hists->dso_filter;
+       const struct thread *thread = hists->thread_filter;
+       unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
+       u64 nr_events = hists->stats.total_period;
 
        nr_samples = convert_unit(nr_samples, &unit);
        printed = scnprintf(bf, size,
@@ -849,9 +849,9 @@ static int hists__browser_title(struct hists *self, char *bf, size_t size,
                           nr_samples, unit, ev_name, nr_events);
 
 
-       if (self->uid_filter_str)
+       if (hists->uid_filter_str)
                printed += snprintf(bf + printed, size - printed,
-                                   ", UID: %s", self->uid_filter_str);
+                                   ", UID: %s", hists->uid_filter_str);
        if (thread)
                printed += scnprintf(bf + printed, size - printed,
                                    ", Thread: %s(%d)",
@@ -879,8 +879,8 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
                                    void(*timer)(void *arg), void *arg,
                                    int delay_secs)
 {
-       struct hists *self = &evsel->hists;
-       struct hist_browser *browser = hist_browser__new(self);
+       struct hists *hists = &evsel->hists;
+       struct hist_browser *browser = hist_browser__new(hists);
        struct branch_info *bi;
        struct pstack *fstack;
        char *options[16];
@@ -946,8 +946,8 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
                                        "Please enter the name of symbol you want to see",
                                        buf, "ENTER: OK, ESC: Cancel",
                                        delay_secs * 2) == K_ENTER) {
-                               self->symbol_filter_str = *buf ? buf : NULL;
-                               hists__filter_by_symbol(self);
+                               hists->symbol_filter_str = *buf ? buf : NULL;
+                               hists__filter_by_symbol(hists);
                                hist_browser__reset(browser);
                        }
                        continue;
@@ -1128,7 +1128,7 @@ zoom_out_dso:
                                sort_dso.elide = true;
                                pstack__push(fstack, &browser->hists->dso_filter);
                        }
-                       hists__filter_by_dso(self);
+                       hists__filter_by_dso(hists);
                        hist_browser__reset(browser);
                } else if (choice == zoom_thread) {
 zoom_thread:
@@ -1146,7 +1146,7 @@ zoom_out_thread:
                                sort_thread.elide = true;
                                pstack__push(fstack, &browser->hists->thread_filter);
                        }
-                       hists__filter_by_thread(self);
+                       hists__filter_by_thread(hists);
                        hist_browser__reset(browser);
                }
        }
index 9f5f888f73e30723d5f9c0fd8281345f38e0635e..791fb15ce3507c2d42d695be12c1f87a16affa0f 100644 (file)
@@ -22,6 +22,7 @@ void setup_browser(bool fallback_to_pager)
                        break;
                /* fall through */
        default:
+               use_browser = 0;
                if (fallback_to_pager)
                        setup_pager();
                break;
index 0deac6a14b652df87c998b38203b0ab003f541ed..6faa3a18bfbd8514001e2d2dd25a9f9a52ce7910 100644 (file)
@@ -120,7 +120,7 @@ static char *parse_value(void)
 
 static inline int iskeychar(int c)
 {
-       return isalnum(c) || c == '-';
+       return isalnum(c) || c == '-' || c == '_';
 }
 
 static int get_value(config_fn_t fn, void *data, char *name, unsigned int len)
index 57e4ce57bbcc03faf7245f40a0ba3e20b43851d8..91d19138f3ec3891620b755fe0faf470d61d2319 100644 (file)
@@ -15,6 +15,7 @@
 #include "cpumap.h"
 #include "thread_map.h"
 #include "target.h"
+#include "../../include/linux/perf_event.h"
 
 #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y))
 #define GROUP_FD(group_fd, cpu) (*(int *)xyarray__entry(group_fd, cpu, 0))
@@ -64,6 +65,95 @@ struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx)
        return evsel;
 }
 
+static const char *perf_evsel__hw_names[PERF_COUNT_HW_MAX] = {
+       "cycles",
+       "instructions",
+       "cache-references",
+       "cache-misses",
+       "branches",
+       "branch-misses",
+       "bus-cycles",
+       "stalled-cycles-frontend",
+       "stalled-cycles-backend",
+       "ref-cycles",
+};
+
+const char *__perf_evsel__hw_name(u64 config)
+{
+       if (config < PERF_COUNT_HW_MAX && perf_evsel__hw_names[config])
+               return perf_evsel__hw_names[config];
+
+       return "unknown-hardware";
+}
+
+static int perf_evsel__hw_name(struct perf_evsel *evsel, char *bf, size_t size)
+{
+       int colon = 0;
+       struct perf_event_attr *attr = &evsel->attr;
+       int r = scnprintf(bf, size, "%s", __perf_evsel__hw_name(attr->config));
+       bool exclude_guest_default = false;
+
+#define MOD_PRINT(context, mod)        do {                                    \
+               if (!attr->exclude_##context) {                         \
+                       if (!colon) colon = r++;                        \
+                       r += scnprintf(bf + r, size - r, "%c", mod);    \
+               } } while(0)
+
+       if (attr->exclude_kernel || attr->exclude_user || attr->exclude_hv) {
+               MOD_PRINT(kernel, 'k');
+               MOD_PRINT(user, 'u');
+               MOD_PRINT(hv, 'h');
+               exclude_guest_default = true;
+       }
+
+       if (attr->precise_ip) {
+               if (!colon)
+                       colon = r++;
+               r += scnprintf(bf + r, size - r, "%.*s", attr->precise_ip, "ppp");
+               exclude_guest_default = true;
+       }
+
+       if (attr->exclude_host || attr->exclude_guest == exclude_guest_default) {
+               MOD_PRINT(host, 'H');
+               MOD_PRINT(guest, 'G');
+       }
+#undef MOD_PRINT
+       if (colon)
+               bf[colon] = ':';
+       return r;
+}
+
+int perf_evsel__name(struct perf_evsel *evsel, char *bf, size_t size)
+{
+       int ret;
+
+       switch (evsel->attr.type) {
+       case PERF_TYPE_RAW:
+               ret = scnprintf(bf, size, "raw 0x%" PRIx64, evsel->attr.config);
+               break;
+
+       case PERF_TYPE_HARDWARE:
+               ret = perf_evsel__hw_name(evsel, bf, size);
+               break;
+       default:
+               /*
+                * FIXME
+                *
+                * This is the minimal perf_evsel__name so that we can
+                * reconstruct event names taking into account event modifiers.
+                *
+                * The old event_name uses it now for raw anr hw events, so that
+                * we don't drag all the parsing stuff into the python binding.
+                *
+                * On the next devel cycle the rest of the event naming will be
+                * brought here.
+                */
+               return 0;
+       }
+
+       return ret;
+}
+
 void perf_evsel__config(struct perf_evsel *evsel, struct perf_record_opts *opts,
                        struct perf_evsel *first)
 {
index 3d6b3e4cb66bb9bfbb32b75958ef1f23a4880163..4ba8b564e6f47f039652ebac739d9d899d5881f5 100644 (file)
@@ -83,6 +83,9 @@ void perf_evsel__config(struct perf_evsel *evsel,
                        struct perf_record_opts *opts,
                        struct perf_evsel *first);
 
+const char* __perf_evsel__hw_name(u64 config);
+int perf_evsel__name(struct perf_evsel *evsel, char *bf, size_t size);
+
 int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads);
 int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads);
 int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus);
index fac7d59309b83698cf22829936e86e1c511eb31e..05dbc8b3c767217ceb3204c7be46f5aedbb205c4 100644 (file)
@@ -62,19 +62,6 @@ static struct event_symbol event_symbols[] = {
 #define PERF_EVENT_TYPE(config)                __PERF_EVENT_FIELD(config, TYPE)
 #define PERF_EVENT_ID(config)          __PERF_EVENT_FIELD(config, EVENT)
 
-static const char *hw_event_names[PERF_COUNT_HW_MAX] = {
-       "cycles",
-       "instructions",
-       "cache-references",
-       "cache-misses",
-       "branches",
-       "branch-misses",
-       "bus-cycles",
-       "stalled-cycles-frontend",
-       "stalled-cycles-backend",
-       "ref-cycles",
-};
-
 static const char *sw_event_names[PERF_COUNT_SW_MAX] = {
        "cpu-clock",
        "task-clock",
@@ -300,6 +287,16 @@ const char *event_name(struct perf_evsel *evsel)
        u64 config = evsel->attr.config;
        int type = evsel->attr.type;
 
+       if (type == PERF_TYPE_RAW || type == PERF_TYPE_HARDWARE) {
+               /*
+                * XXX minimal fix, see comment on perf_evsen__name, this static buffer
+                * will go away together with event_name in the next devel cycle.
+                */
+               static char bf[128];
+               perf_evsel__name(evsel, bf, sizeof(bf));
+               return bf;
+       }
+
        if (evsel->name)
                return evsel->name;
 
@@ -317,9 +314,7 @@ const char *__event_name(int type, u64 config)
 
        switch (type) {
        case PERF_TYPE_HARDWARE:
-               if (config < PERF_COUNT_HW_MAX && hw_event_names[config])
-                       return hw_event_names[config];
-               return "unknown-hardware";
+               return __perf_evsel__hw_name(config);
 
        case PERF_TYPE_HW_CACHE: {
                u8 cache_type, cache_op, cache_result;
index 84d9bd7820049cdcb641e0efc96f22a28b8f210a..9b5f856cc28096b865d1c34c12073a23e5eb5a9c 100644 (file)
@@ -188,28 +188,27 @@ static struct thread_map *thread_map__new_by_pid_str(const char *pid_str)
                nt = realloc(threads, (sizeof(*threads) +
                                       sizeof(pid_t) * total_tasks));
                if (nt == NULL)
-                       goto out_free_threads;
+                       goto out_free_namelist;
 
                threads = nt;
 
-               if (threads) {
-                       for (i = 0; i < items; i++)
-                               threads->map[j++] = atoi(namelist[i]->d_name);
-                       threads->nr = total_tasks;
-               }
-
-               for (i = 0; i < items; i++)
+               for (i = 0; i < items; i++) {
+                       threads->map[j++] = atoi(namelist[i]->d_name);
                        free(namelist[i]);
+               }
+               threads->nr = total_tasks;
                free(namelist);
-
-               if (!threads)
-                       break;
        }
 
 out:
        strlist__delete(slist);
        return threads;
 
+out_free_namelist:
+       for (i = 0; i < items; i++)
+               free(namelist[i]);
+       free(namelist);
+
 out_free_threads:
        free(threads);
        threads = NULL;
index 28bc57ee757cf04d7b2166dc3e4b236b5fd19de6..a4162e15c25f89f32862a1f4fb2630c32f8c1c60 100644 (file)
@@ -1,4 +1,4 @@
-TARGETS = breakpoints vm
+TARGETS = breakpoints kcmp mqueue vm
 
 all:
        for TARGET in $(TARGETS); do \
diff --git a/tools/testing/selftests/kcmp/Makefile b/tools/testing/selftests/kcmp/Makefile
new file mode 100644 (file)
index 0000000..dc79b86
--- /dev/null
@@ -0,0 +1,29 @@
+uname_M := $(shell uname -m 2>/dev/null || echo not)
+ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/i386/)
+ifeq ($(ARCH),i386)
+        ARCH := X86
+       CFLAGS := -DCONFIG_X86_32 -D__i386__
+endif
+ifeq ($(ARCH),x86_64)
+       ARCH := X86
+       CFLAGS := -DCONFIG_X86_64 -D__x86_64__
+endif
+
+CFLAGS += -I../../../../arch/x86/include/generated/
+CFLAGS += -I../../../../include/
+CFLAGS += -I../../../../usr/include/
+CFLAGS += -I../../../../arch/x86/include/
+
+all:
+ifeq ($(ARCH),X86)
+       gcc $(CFLAGS) kcmp_test.c -o run_test
+else
+       echo "Not an x86 target, can't build kcmp selftest"
+endif
+
+run-tests: all
+       ./kcmp_test
+
+clean:
+       rm -fr ./run_test
+       rm -fr ./test-file
diff --git a/tools/testing/selftests/kcmp/kcmp_test.c b/tools/testing/selftests/kcmp/kcmp_test.c
new file mode 100644 (file)
index 0000000..358cc6b
--- /dev/null
@@ -0,0 +1,94 @@
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <limits.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <fcntl.h>
+
+#include <linux/unistd.h>
+#include <linux/kcmp.h>
+
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+static long sys_kcmp(int pid1, int pid2, int type, int fd1, int fd2)
+{
+       return syscall(__NR_kcmp, pid1, pid2, type, fd1, fd2);
+}
+
+int main(int argc, char **argv)
+{
+       const char kpath[] = "kcmp-test-file";
+       int pid1, pid2;
+       int fd1, fd2;
+       int status;
+
+       fd1 = open(kpath, O_RDWR | O_CREAT | O_TRUNC, 0644);
+       pid1 = getpid();
+
+       if (fd1 < 0) {
+               perror("Can't create file");
+               exit(1);
+       }
+
+       pid2 = fork();
+       if (pid2 < 0) {
+               perror("fork failed");
+               exit(1);
+       }
+
+       if (!pid2) {
+               int pid2 = getpid();
+               int ret;
+
+               fd2 = open(kpath, O_RDWR, 0644);
+               if (fd2 < 0) {
+                       perror("Can't open file");
+                       exit(1);
+               }
+
+               /* An example of output and arguments */
+               printf("pid1: %6d pid2: %6d FD: %2ld FILES: %2ld VM: %2ld "
+                      "FS: %2ld SIGHAND: %2ld IO: %2ld SYSVSEM: %2ld "
+                      "INV: %2ld\n",
+                      pid1, pid2,
+                      sys_kcmp(pid1, pid2, KCMP_FILE,          fd1, fd2),
+                      sys_kcmp(pid1, pid2, KCMP_FILES,         0, 0),
+                      sys_kcmp(pid1, pid2, KCMP_VM,            0, 0),
+                      sys_kcmp(pid1, pid2, KCMP_FS,            0, 0),
+                      sys_kcmp(pid1, pid2, KCMP_SIGHAND,       0, 0),
+                      sys_kcmp(pid1, pid2, KCMP_IO,            0, 0),
+                      sys_kcmp(pid1, pid2, KCMP_SYSVSEM,       0, 0),
+
+                       /* This one should fail */
+                      sys_kcmp(pid1, pid2, KCMP_TYPES + 1,     0, 0));
+
+               /* This one should return same fd */
+               ret = sys_kcmp(pid1, pid2, KCMP_FILE, fd1, fd1);
+               if (ret) {
+                       printf("FAIL: 0 expected but %d returned\n", ret);
+                       ret = -1;
+               } else
+                       printf("PASS: 0 returned as expected\n");
+
+               /* Compare with self */
+               ret = sys_kcmp(pid1, pid1, KCMP_VM, 0, 0);
+               if (ret) {
+                       printf("FAIL: 0 expected but %li returned\n", ret);
+                       ret = -1;
+               } else
+                       printf("PASS: 0 returned as expected\n");
+
+               exit(ret);
+       }
+
+       waitpid(pid2, &status, P_ALL);
+
+       return 0;
+}
diff --git a/tools/testing/selftests/mqueue/.gitignore b/tools/testing/selftests/mqueue/.gitignore
new file mode 100644 (file)
index 0000000..d8d4237
--- /dev/null
@@ -0,0 +1,2 @@
+mq_open_tests
+mq_perf_tests
diff --git a/tools/testing/selftests/mqueue/Makefile b/tools/testing/selftests/mqueue/Makefile
new file mode 100644 (file)
index 0000000..54c0aad
--- /dev/null
@@ -0,0 +1,10 @@
+all:
+       gcc -O2 -lrt mq_open_tests.c -o mq_open_tests
+       gcc -O2 -lrt -lpthread -lpopt -o mq_perf_tests mq_perf_tests.c
+
+run_tests:
+       ./mq_open_tests /test1
+       ./mq_perf_tests
+
+clean:
+       rm -f mq_open_tests mq_perf_tests
diff --git a/tools/testing/selftests/mqueue/mq_open_tests.c b/tools/testing/selftests/mqueue/mq_open_tests.c
new file mode 100644 (file)
index 0000000..711cc29
--- /dev/null
@@ -0,0 +1,492 @@
+/*
+ * This application is Copyright 2012 Red Hat, Inc.
+ *     Doug Ledford <dledford@redhat.com>
+ *
+ * mq_open_tests 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, version 3.
+ *
+ * mq_open_tests 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.
+ *
+ * For the full text of the license, see <http://www.gnu.org/licenses/>.
+ *
+ * mq_open_tests.c
+ *   Tests the various situations that should either succeed or fail to
+ *   open a posix message queue and then reports whether or not they
+ *   did as they were supposed to.
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <limits.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <mqueue.h>
+
+static char *usage =
+"Usage:\n"
+"  %s path\n"
+"\n"
+"      path    Path name of the message queue to create\n"
+"\n"
+"      Note: this program must be run as root in order to enable all tests\n"
+"\n";
+
+char *DEF_MSGS = "/proc/sys/fs/mqueue/msg_default";
+char *DEF_MSGSIZE = "/proc/sys/fs/mqueue/msgsize_default";
+char *MAX_MSGS = "/proc/sys/fs/mqueue/msg_max";
+char *MAX_MSGSIZE = "/proc/sys/fs/mqueue/msgsize_max";
+
+int default_settings;
+struct rlimit saved_limits, cur_limits;
+int saved_def_msgs, saved_def_msgsize, saved_max_msgs, saved_max_msgsize;
+int cur_def_msgs, cur_def_msgsize, cur_max_msgs, cur_max_msgsize;
+FILE *def_msgs, *def_msgsize, *max_msgs, *max_msgsize;
+char *queue_path;
+mqd_t queue = -1;
+
+static inline void __set(FILE *stream, int value, char *err_msg);
+void shutdown(int exit_val, char *err_cause, int line_no);
+static inline int get(FILE *stream);
+static inline void set(FILE *stream, int value);
+static inline void getr(int type, struct rlimit *rlim);
+static inline void setr(int type, struct rlimit *rlim);
+void validate_current_settings();
+static inline void test_queue(struct mq_attr *attr, struct mq_attr *result);
+static inline int test_queue_fail(struct mq_attr *attr, struct mq_attr *result);
+
+static inline void __set(FILE *stream, int value, char *err_msg)
+{
+       rewind(stream);
+       if (fprintf(stream, "%d", value) < 0)
+               perror(err_msg);
+}
+
+
+void shutdown(int exit_val, char *err_cause, int line_no)
+{
+       static int in_shutdown = 0;
+
+       /* In case we get called recursively by a set() call below */
+       if (in_shutdown++)
+               return;
+
+       seteuid(0);
+
+       if (queue != -1)
+               if (mq_close(queue))
+                       perror("mq_close() during shutdown");
+       if (queue_path)
+               /*
+                * Be silent if this fails, if we cleaned up already it's
+                * expected to fail
+                */
+               mq_unlink(queue_path);
+       if (default_settings) {
+               if (saved_def_msgs)
+                       __set(def_msgs, saved_def_msgs,
+                             "failed to restore saved_def_msgs");
+               if (saved_def_msgsize)
+                       __set(def_msgsize, saved_def_msgsize,
+                             "failed to restore saved_def_msgsize");
+       }
+       if (saved_max_msgs)
+               __set(max_msgs, saved_max_msgs,
+                     "failed to restore saved_max_msgs");
+       if (saved_max_msgsize)
+               __set(max_msgsize, saved_max_msgsize,
+                     "failed to restore saved_max_msgsize");
+       if (exit_val)
+               error(exit_val, errno, "%s at %d", err_cause, line_no);
+       exit(0);
+}
+
+static inline int get(FILE *stream)
+{
+       int value;
+       rewind(stream);
+       if (fscanf(stream, "%d", &value) != 1)
+               shutdown(4, "Error reading /proc entry", __LINE__ - 1);
+       return value;
+}
+
+static inline void set(FILE *stream, int value)
+{
+       int new_value;
+
+       rewind(stream);
+       if (fprintf(stream, "%d", value) < 0)
+               return shutdown(5, "Failed writing to /proc file",
+                               __LINE__ - 1);
+       new_value = get(stream);
+       if (new_value != value)
+               return shutdown(5, "We didn't get what we wrote to /proc back",
+                               __LINE__ - 1);
+}
+
+static inline void getr(int type, struct rlimit *rlim)
+{
+       if (getrlimit(type, rlim))
+               shutdown(6, "getrlimit()", __LINE__ - 1);
+}
+
+static inline void setr(int type, struct rlimit *rlim)
+{
+       if (setrlimit(type, rlim))
+               shutdown(7, "setrlimit()", __LINE__ - 1);
+}
+
+void validate_current_settings()
+{
+       int rlim_needed;
+
+       if (cur_limits.rlim_cur < 4096) {
+               printf("Current rlimit value for POSIX message queue bytes is "
+                      "unreasonably low,\nincreasing.\n\n");
+               cur_limits.rlim_cur = 8192;
+               cur_limits.rlim_max = 16384;
+               setr(RLIMIT_MSGQUEUE, &cur_limits);
+       }
+
+       if (default_settings) {
+               rlim_needed = (cur_def_msgs + 1) * (cur_def_msgsize + 1 +
+                                                   2 * sizeof(void *));
+               if (rlim_needed > cur_limits.rlim_cur) {
+                       printf("Temporarily lowering default queue parameters "
+                              "to something that will work\n"
+                              "with the current rlimit values.\n\n");
+                       set(def_msgs, 10);
+                       cur_def_msgs = 10;
+                       set(def_msgsize, 128);
+                       cur_def_msgsize = 128;
+               }
+       } else {
+               rlim_needed = (cur_max_msgs + 1) * (cur_max_msgsize + 1 +
+                                                   2 * sizeof(void *));
+               if (rlim_needed > cur_limits.rlim_cur) {
+                       printf("Temporarily lowering maximum queue parameters "
+                              "to something that will work\n"
+                              "with the current rlimit values in case this is "
+                              "a kernel that ties the default\n"
+                              "queue parameters to the maximum queue "
+                              "parameters.\n\n");
+                       set(max_msgs, 10);
+                       cur_max_msgs = 10;
+                       set(max_msgsize, 128);
+                       cur_max_msgsize = 128;
+               }
+       }
+}
+
+/*
+ * test_queue - Test opening a queue, shutdown if we fail.  This should
+ * only be called in situations that should never fail.  We clean up
+ * after ourselves and return the queue attributes in *result.
+ */
+static inline void test_queue(struct mq_attr *attr, struct mq_attr *result)
+{
+       int flags = O_RDWR | O_EXCL | O_CREAT;
+       int perms = DEFFILEMODE;
+
+       if ((queue = mq_open(queue_path, flags, perms, attr)) == -1)
+               shutdown(1, "mq_open()", __LINE__);
+       if (mq_getattr(queue, result))
+               shutdown(1, "mq_getattr()", __LINE__);
+       if (mq_close(queue))
+               shutdown(1, "mq_close()", __LINE__);
+       queue = -1;
+       if (mq_unlink(queue_path))
+               shutdown(1, "mq_unlink()", __LINE__);
+}
+
+/*
+ * Same as test_queue above, but failure is not fatal.
+ * Returns:
+ * 0 - Failed to create a queue
+ * 1 - Created a queue, attributes in *result
+ */
+static inline int test_queue_fail(struct mq_attr *attr, struct mq_attr *result)
+{
+       int flags = O_RDWR | O_EXCL | O_CREAT;
+       int perms = DEFFILEMODE;
+
+       if ((queue = mq_open(queue_path, flags, perms, attr)) == -1)
+               return 0;
+       if (mq_getattr(queue, result))
+               shutdown(1, "mq_getattr()", __LINE__);
+       if (mq_close(queue))
+               shutdown(1, "mq_close()", __LINE__);
+       queue = -1;
+       if (mq_unlink(queue_path))
+               shutdown(1, "mq_unlink()", __LINE__);
+       return 1;
+}
+
+int main(int argc, char *argv[])
+{
+       struct mq_attr attr, result;
+
+       if (argc != 2) {
+               fprintf(stderr, "Must pass a valid queue name\n\n");
+               fprintf(stderr, usage, argv[0]);
+               exit(1);
+       }
+
+       /*
+        * Although we can create a msg queue with a non-absolute path name,
+        * unlink will fail.  So, if the name doesn't start with a /, add one
+        * when we save it.
+        */
+       if (*argv[1] == '/')
+               queue_path = strdup(argv[1]);
+       else {
+               queue_path = malloc(strlen(argv[1]) + 2);
+               if (!queue_path) {
+                       perror("malloc()");
+                       exit(1);
+               }
+               queue_path[0] = '/';
+               queue_path[1] = 0;
+               strcat(queue_path, argv[1]);
+       }
+
+       if (getuid() != 0) {
+               fprintf(stderr, "Not running as root, but almost all tests "
+                       "require root in order to modify\nsystem settings.  "
+                       "Exiting.\n");
+               exit(1);
+       }
+
+       /* Find out what files there are for us to make tweaks in */
+       def_msgs = fopen(DEF_MSGS, "r+");
+       def_msgsize = fopen(DEF_MSGSIZE, "r+");
+       max_msgs = fopen(MAX_MSGS, "r+");
+       max_msgsize = fopen(MAX_MSGSIZE, "r+");
+
+       if (!max_msgs)
+               shutdown(2, "Failed to open msg_max", __LINE__);
+       if (!max_msgsize)
+               shutdown(2, "Failed to open msgsize_max", __LINE__);
+       if (def_msgs || def_msgsize)
+               default_settings = 1;
+
+       /* Load up the current system values for everything we can */
+       getr(RLIMIT_MSGQUEUE, &saved_limits);
+       cur_limits = saved_limits;
+       if (default_settings) {
+               saved_def_msgs = cur_def_msgs = get(def_msgs);
+               saved_def_msgsize = cur_def_msgsize = get(def_msgsize);
+       }
+       saved_max_msgs = cur_max_msgs = get(max_msgs);
+       saved_max_msgsize = cur_max_msgsize = get(max_msgsize);
+
+       /* Tell the user our initial state */
+       printf("\nInitial system state:\n");
+       printf("\tUsing queue path:\t\t%s\n", queue_path);
+       printf("\tRLIMIT_MSGQUEUE(soft):\t\t%d\n", saved_limits.rlim_cur);
+       printf("\tRLIMIT_MSGQUEUE(hard):\t\t%d\n", saved_limits.rlim_max);
+       printf("\tMaximum Message Size:\t\t%d\n", saved_max_msgsize);
+       printf("\tMaximum Queue Size:\t\t%d\n", saved_max_msgs);
+       if (default_settings) {
+               printf("\tDefault Message Size:\t\t%d\n", saved_def_msgsize);
+               printf("\tDefault Queue Size:\t\t%d\n", saved_def_msgs);
+       } else {
+               printf("\tDefault Message Size:\t\tNot Supported\n");
+               printf("\tDefault Queue Size:\t\tNot Supported\n");
+       }
+       printf("\n");
+
+       validate_current_settings();
+
+       printf("Adjusted system state for testing:\n");
+       printf("\tRLIMIT_MSGQUEUE(soft):\t\t%d\n", cur_limits.rlim_cur);
+       printf("\tRLIMIT_MSGQUEUE(hard):\t\t%d\n", cur_limits.rlim_max);
+       printf("\tMaximum Message Size:\t\t%d\n", cur_max_msgsize);
+       printf("\tMaximum Queue Size:\t\t%d\n", cur_max_msgs);
+       if (default_settings) {
+               printf("\tDefault Message Size:\t\t%d\n", cur_def_msgsize);
+               printf("\tDefault Queue Size:\t\t%d\n", cur_def_msgs);
+       }
+
+       printf("\n\nTest series 1, behavior when no attr struct "
+              "passed to mq_open:\n");
+       if (!default_settings) {
+               test_queue(NULL, &result);
+               printf("Given sane system settings, mq_open without an attr "
+                      "struct succeeds:\tPASS\n");
+               if (result.mq_maxmsg != cur_max_msgs ||
+                   result.mq_msgsize != cur_max_msgsize) {
+                       printf("Kernel does not support setting the default "
+                              "mq attributes,\nbut also doesn't tie the "
+                              "defaults to the maximums:\t\t\tPASS\n");
+               } else {
+                       set(max_msgs, ++cur_max_msgs);
+                       set(max_msgsize, ++cur_max_msgsize);
+                       test_queue(NULL, &result);
+                       if (result.mq_maxmsg == cur_max_msgs &&
+                           result.mq_msgsize == cur_max_msgsize)
+                               printf("Kernel does not support setting the "
+                                      "default mq attributes and\n"
+                                      "also ties system wide defaults to "
+                                      "the system wide maximums:\t\t"
+                                      "FAIL\n");
+                       else
+                               printf("Kernel does not support setting the "
+                                      "default mq attributes,\n"
+                                      "but also doesn't tie the defaults to "
+                                      "the maximums:\t\t\tPASS\n");
+               }
+       } else {
+               printf("Kernel supports setting defaults separately from "
+                      "maximums:\t\tPASS\n");
+               /*
+                * While we are here, go ahead and test that the kernel
+                * properly follows the default settings
+                */
+               test_queue(NULL, &result);
+               printf("Given sane values, mq_open without an attr struct "
+                      "succeeds:\t\tPASS\n");
+               if (result.mq_maxmsg != cur_def_msgs ||
+                   result.mq_msgsize != cur_def_msgsize)
+                       printf("Kernel supports setting defaults, but does "
+                              "not actually honor them:\tFAIL\n\n");
+               else {
+                       set(def_msgs, ++cur_def_msgs);
+                       set(def_msgsize, ++cur_def_msgsize);
+                       /* In case max was the same as the default */
+                       set(max_msgs, ++cur_max_msgs);
+                       set(max_msgsize, ++cur_max_msgsize);
+                       test_queue(NULL, &result);
+                       if (result.mq_maxmsg != cur_def_msgs ||
+                           result.mq_msgsize != cur_def_msgsize)
+                               printf("Kernel supports setting defaults, but "
+                                      "does not actually honor them:\t"
+                                      "FAIL\n");
+                       else
+                               printf("Kernel properly honors default setting "
+                                      "knobs:\t\t\t\tPASS\n");
+               }
+               set(def_msgs, cur_max_msgs + 1);
+               cur_def_msgs = cur_max_msgs + 1;
+               set(def_msgsize, cur_max_msgsize + 1);
+               cur_def_msgsize = cur_max_msgsize + 1;
+               if (cur_def_msgs * (cur_def_msgsize + 2 * sizeof(void *)) >=
+                   cur_limits.rlim_cur) {
+                       cur_limits.rlim_cur = (cur_def_msgs + 2) *
+                               (cur_def_msgsize + 2 * sizeof(void *));
+                       cur_limits.rlim_max = 2 * cur_limits.rlim_cur;
+                       setr(RLIMIT_MSGQUEUE, &cur_limits);
+               }
+               if (test_queue_fail(NULL, &result)) {
+                       if (result.mq_maxmsg == cur_max_msgs &&
+                           result.mq_msgsize == cur_max_msgsize)
+                               printf("Kernel properly limits default values "
+                                      "to lesser of default/max:\t\tPASS\n");
+                       else
+                               printf("Kernel does not properly set default "
+                                      "queue parameters when\ndefaults > "
+                                      "max:\t\t\t\t\t\t\t\tFAIL\n");
+               } else
+                       printf("Kernel fails to open mq because defaults are "
+                              "greater than maximums:\tFAIL\n");
+               set(def_msgs, --cur_def_msgs);
+               set(def_msgsize, --cur_def_msgsize);
+               cur_limits.rlim_cur = cur_limits.rlim_max = cur_def_msgs *
+                       cur_def_msgsize;
+               setr(RLIMIT_MSGQUEUE, &cur_limits);
+               if (test_queue_fail(NULL, &result))
+                       printf("Kernel creates queue even though defaults "
+                              "would exceed\nrlimit setting:"
+                              "\t\t\t\t\t\t\t\tFAIL\n");
+               else
+                       printf("Kernel properly fails to create queue when "
+                              "defaults would\nexceed rlimit:"
+                              "\t\t\t\t\t\t\t\tPASS\n");
+       }
+
+       /*
+        * Test #2 - open with an attr struct that exceeds rlimit
+        */
+       printf("\n\nTest series 2, behavior when attr struct is "
+              "passed to mq_open:\n");
+       cur_max_msgs = 32;
+       cur_max_msgsize = cur_limits.rlim_max >> 4;
+       set(max_msgs, cur_max_msgs);
+       set(max_msgsize, cur_max_msgsize);
+       attr.mq_maxmsg = cur_max_msgs;
+       attr.mq_msgsize = cur_max_msgsize;
+       if (test_queue_fail(&attr, &result))
+               printf("Queue open in excess of rlimit max when euid = 0 "
+                      "succeeded:\t\tFAIL\n");
+       else
+               printf("Queue open in excess of rlimit max when euid = 0 "
+                      "failed:\t\tPASS\n");
+       attr.mq_maxmsg = cur_max_msgs + 1;
+       attr.mq_msgsize = 10;
+       if (test_queue_fail(&attr, &result))
+               printf("Queue open with mq_maxmsg > limit when euid = 0 "
+                      "succeeded:\t\tPASS\n");
+       else
+               printf("Queue open with mq_maxmsg > limit when euid = 0 "
+                      "failed:\t\tFAIL\n");
+       attr.mq_maxmsg = 1;
+       attr.mq_msgsize = cur_max_msgsize + 1;
+       if (test_queue_fail(&attr, &result))
+               printf("Queue open with mq_msgsize > limit when euid = 0 "
+                      "succeeded:\t\tPASS\n");
+       else
+               printf("Queue open with mq_msgsize > limit when euid = 0 "
+                      "failed:\t\tFAIL\n");
+       attr.mq_maxmsg = 65536;
+       attr.mq_msgsize = 65536;
+       if (test_queue_fail(&attr, &result))
+               printf("Queue open with total size > 2GB when euid = 0 "
+                      "succeeded:\t\tFAIL\n");
+       else
+               printf("Queue open with total size > 2GB when euid = 0 "
+                      "failed:\t\t\tPASS\n");
+       seteuid(99);
+       attr.mq_maxmsg = cur_max_msgs;
+       attr.mq_msgsize = cur_max_msgsize;
+       if (test_queue_fail(&attr, &result))
+               printf("Queue open in excess of rlimit max when euid = 99 "
+                      "succeeded:\t\tFAIL\n");
+       else
+               printf("Queue open in excess of rlimit max when euid = 99 "
+                      "failed:\t\tPASS\n");
+       attr.mq_maxmsg = cur_max_msgs + 1;
+       attr.mq_msgsize = 10;
+       if (test_queue_fail(&attr, &result))
+               printf("Queue open with mq_maxmsg > limit when euid = 99 "
+                      "succeeded:\t\tFAIL\n");
+       else
+               printf("Queue open with mq_maxmsg > limit when euid = 99 "
+                      "failed:\t\tPASS\n");
+       attr.mq_maxmsg = 1;
+       attr.mq_msgsize = cur_max_msgsize + 1;
+       if (test_queue_fail(&attr, &result))
+               printf("Queue open with mq_msgsize > limit when euid = 99 "
+                      "succeeded:\t\tFAIL\n");
+       else
+               printf("Queue open with mq_msgsize > limit when euid = 99 "
+                      "failed:\t\tPASS\n");
+       attr.mq_maxmsg = 65536;
+       attr.mq_msgsize = 65536;
+       if (test_queue_fail(&attr, &result))
+               printf("Queue open with total size > 2GB when euid = 99 "
+                      "succeeded:\t\tFAIL\n");
+       else
+               printf("Queue open with total size > 2GB when euid = 99 "
+                      "failed:\t\t\tPASS\n");
+
+       shutdown(0,"",0);
+}
diff --git a/tools/testing/selftests/mqueue/mq_perf_tests.c b/tools/testing/selftests/mqueue/mq_perf_tests.c
new file mode 100644 (file)
index 0000000..2fadd4b
--- /dev/null
@@ -0,0 +1,741 @@
+/*
+ * This application is Copyright 2012 Red Hat, Inc.
+ *     Doug Ledford <dledford@redhat.com>
+ *
+ * mq_perf_tests 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, version 3.
+ *
+ * mq_perf_tests 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.
+ *
+ * For the full text of the license, see <http://www.gnu.org/licenses/>.
+ *
+ * mq_perf_tests.c
+ *   Tests various types of message queue workloads, concentrating on those
+ *   situations that invole large message sizes, large message queue depths,
+ *   or both, and reports back useful metrics about kernel message queue
+ *   performance.
+ *
+ */
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <limits.h>
+#include <errno.h>
+#include <signal.h>
+#include <pthread.h>
+#include <sched.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <mqueue.h>
+#include <popt.h>
+
+static char *usage =
+"Usage:\n"
+"  %s [-c #[,#..] -f] path\n"
+"\n"
+"      -c #    Skip most tests and go straight to a high queue depth test\n"
+"              and then run that test continuously (useful for running at\n"
+"              the same time as some other workload to see how much the\n"
+"              cache thrashing caused by adding messages to a very deep\n"
+"              queue impacts the performance of other programs).  The number\n"
+"              indicates which CPU core we should bind the process to during\n"
+"              the run.  If you have more than one physical CPU, then you\n"
+"              will need one copy per physical CPU package, and you should\n"
+"              specify the CPU cores to pin ourself to via a comma separated\n"
+"              list of CPU values.\n"
+"      -f      Only usable with continuous mode.  Pin ourself to the CPUs\n"
+"              as requested, then instead of looping doing a high mq\n"
+"              workload, just busy loop.  This will allow us to lock up a\n"
+"              single CPU just like we normally would, but without actually\n"
+"              thrashing the CPU cache.  This is to make it easier to get\n"
+"              comparable numbers from some other workload running on the\n"
+"              other CPUs.  One set of numbers with # CPUs locked up running\n"
+"              an mq workload, and another set of numbers with those same\n"
+"              CPUs locked away from the test workload, but not doing\n"
+"              anything to trash the cache like the mq workload might.\n"
+"      path    Path name of the message queue to create\n"
+"\n"
+"      Note: this program must be run as root in order to enable all tests\n"
+"\n";
+
+char *MAX_MSGS = "/proc/sys/fs/mqueue/msg_max";
+char *MAX_MSGSIZE = "/proc/sys/fs/mqueue/msgsize_max";
+
+#define min(a, b) ((a) < (b) ? (a) : (b))
+#define MAX_CPUS 64
+char *cpu_option_string;
+int cpus_to_pin[MAX_CPUS];
+int num_cpus_to_pin;
+pthread_t cpu_threads[MAX_CPUS];
+pthread_t main_thread;
+cpu_set_t *cpu_set;
+int cpu_set_size;
+int cpus_online;
+
+#define MSG_SIZE 16
+#define TEST1_LOOPS 10000000
+#define TEST2_LOOPS 100000
+int continuous_mode;
+int continuous_mode_fake;
+
+struct rlimit saved_limits, cur_limits;
+int saved_max_msgs, saved_max_msgsize;
+int cur_max_msgs, cur_max_msgsize;
+FILE *max_msgs, *max_msgsize;
+int cur_nice;
+char *queue_path = "/mq_perf_tests";
+mqd_t queue = -1;
+struct mq_attr result;
+int mq_prio_max;
+
+const struct poptOption options[] = {
+       {
+               .longName = "continuous",
+               .shortName = 'c',
+               .argInfo = POPT_ARG_STRING,
+               .arg = &cpu_option_string,
+               .val = 'c',
+               .descrip = "Run continuous tests at a high queue depth in "
+                       "order to test the effects of cache thrashing on "
+                       "other tasks on the system.  This test is intended "
+                       "to be run on one core of each physical CPU while "
+                       "some other CPU intensive task is run on all the other "
+                       "cores of that same physical CPU and the other task "
+                       "is timed.  It is assumed that the process of adding "
+                       "messages to the message queue in a tight loop will "
+                       "impact that other task to some degree.  Once the "
+                       "tests are performed in this way, you should then "
+                       "re-run the tests using fake mode in order to check "
+                       "the difference in time required to perform the CPU "
+                       "intensive task",
+               .argDescrip = "cpu[,cpu]",
+       },
+       {
+               .longName = "fake",
+               .shortName = 'f',
+               .argInfo = POPT_ARG_NONE,
+               .arg = &continuous_mode_fake,
+               .val = 0,
+               .descrip = "Tie up the CPUs that we would normally tie up in"
+                       "continuous mode, but don't actually do any mq stuff, "
+                       "just keep the CPU busy so it can't be used to process "
+                       "system level tasks as this would free up resources on "
+                       "the other CPU cores and skew the comparison between "
+                       "the no-mqueue work and mqueue work tests",
+               .argDescrip = NULL,
+       },
+       {
+               .longName = "path",
+               .shortName = 'p',
+               .argInfo = POPT_ARG_STRING | POPT_ARGFLAG_SHOW_DEFAULT,
+               .arg = &queue_path,
+               .val = 'p',
+               .descrip = "The name of the path to use in the mqueue "
+                       "filesystem for our tests",
+               .argDescrip = "pathname",
+       },
+       POPT_AUTOHELP
+       POPT_TABLEEND
+};
+
+static inline void __set(FILE *stream, int value, char *err_msg);
+void shutdown(int exit_val, char *err_cause, int line_no);
+void sig_action_SIGUSR1(int signum, siginfo_t *info, void *context);
+void sig_action(int signum, siginfo_t *info, void *context);
+static inline int get(FILE *stream);
+static inline void set(FILE *stream, int value);
+static inline int try_set(FILE *stream, int value);
+static inline void getr(int type, struct rlimit *rlim);
+static inline void setr(int type, struct rlimit *rlim);
+static inline void open_queue(struct mq_attr *attr);
+void increase_limits(void);
+
+static inline void __set(FILE *stream, int value, char *err_msg)
+{
+       rewind(stream);
+       if (fprintf(stream, "%d", value) < 0)
+               perror(err_msg);
+}
+
+
+void shutdown(int exit_val, char *err_cause, int line_no)
+{
+       static int in_shutdown = 0;
+       int errno_at_shutdown = errno;
+       int i;
+
+       /* In case we get called by multiple threads or from an sighandler */
+       if (in_shutdown++)
+               return;
+
+       for (i = 0; i < num_cpus_to_pin; i++)
+               if (cpu_threads[i]) {
+                       pthread_kill(cpu_threads[i], SIGUSR1);
+                       pthread_join(cpu_threads[i], NULL);
+               }
+
+       if (queue != -1)
+               if (mq_close(queue))
+                       perror("mq_close() during shutdown");
+       if (queue_path)
+               /*
+                * Be silent if this fails, if we cleaned up already it's
+                * expected to fail
+                */
+               mq_unlink(queue_path);
+       if (saved_max_msgs)
+               __set(max_msgs, saved_max_msgs,
+                     "failed to restore saved_max_msgs");
+       if (saved_max_msgsize)
+               __set(max_msgsize, saved_max_msgsize,
+                     "failed to restore saved_max_msgsize");
+       if (exit_val)
+               error(exit_val, errno_at_shutdown, "%s at %d",
+                     err_cause, line_no);
+       exit(0);
+}
+
+void sig_action_SIGUSR1(int signum, siginfo_t *info, void *context)
+{
+       if (pthread_self() != main_thread)
+               pthread_exit(0);
+       else {
+               fprintf(stderr, "Caught signal %d in SIGUSR1 handler, "
+                               "exiting\n", signum);
+               shutdown(0, "", 0);
+               fprintf(stderr, "\n\nReturned from shutdown?!?!\n\n");
+               exit(0);
+       }
+}
+
+void sig_action(int signum, siginfo_t *info, void *context)
+{
+       if (pthread_self() != main_thread)
+               pthread_kill(main_thread, signum);
+       else {
+               fprintf(stderr, "Caught signal %d, exiting\n", signum);
+               shutdown(0, "", 0);
+               fprintf(stderr, "\n\nReturned from shutdown?!?!\n\n");
+               exit(0);
+       }
+}
+
+static inline int get(FILE *stream)
+{
+       int value;
+       rewind(stream);
+       if (fscanf(stream, "%d", &value) != 1)
+               shutdown(4, "Error reading /proc entry", __LINE__);
+       return value;
+}
+
+static inline void set(FILE *stream, int value)
+{
+       int new_value;
+
+       rewind(stream);
+       if (fprintf(stream, "%d", value) < 0)
+               return shutdown(5, "Failed writing to /proc file", __LINE__);
+       new_value = get(stream);
+       if (new_value != value)
+               return shutdown(5, "We didn't get what we wrote to /proc back",
+                               __LINE__);
+}
+
+static inline int try_set(FILE *stream, int value)
+{
+       int new_value;
+
+       rewind(stream);
+       fprintf(stream, "%d", value);
+       new_value = get(stream);
+       return new_value == value;
+}
+
+static inline void getr(int type, struct rlimit *rlim)
+{
+       if (getrlimit(type, rlim))
+               shutdown(6, "getrlimit()", __LINE__);
+}
+
+static inline void setr(int type, struct rlimit *rlim)
+{
+       if (setrlimit(type, rlim))
+               shutdown(7, "setrlimit()", __LINE__);
+}
+
+/**
+ * open_queue - open the global queue for testing
+ * @attr - An attr struct specifying the desired queue traits
+ * @result - An attr struct that lists the actual traits the queue has
+ *
+ * This open is not allowed to fail, failure will result in an orderly
+ * shutdown of the program.  The global queue_path is used to set what
+ * queue to open, the queue descriptor is saved in the global queue
+ * variable.
+ */
+static inline void open_queue(struct mq_attr *attr)
+{
+       int flags = O_RDWR | O_EXCL | O_CREAT | O_NONBLOCK;
+       int perms = DEFFILEMODE;
+
+       queue = mq_open(queue_path, flags, perms, attr);
+       if (queue == -1)
+               shutdown(1, "mq_open()", __LINE__);
+       if (mq_getattr(queue, &result))
+               shutdown(1, "mq_getattr()", __LINE__);
+       printf("\n\tQueue %s created:\n", queue_path);
+       printf("\t\tmq_flags:\t\t\t%s\n", result.mq_flags & O_NONBLOCK ?
+              "O_NONBLOCK" : "(null)");
+       printf("\t\tmq_maxmsg:\t\t\t%d\n", result.mq_maxmsg);
+       printf("\t\tmq_msgsize:\t\t\t%d\n", result.mq_msgsize);
+       printf("\t\tmq_curmsgs:\t\t\t%d\n", result.mq_curmsgs);
+}
+
+void *fake_cont_thread(void *arg)
+{
+       int i;
+
+       for (i = 0; i < num_cpus_to_pin; i++)
+               if (cpu_threads[i] == pthread_self())
+                       break;
+       printf("\tStarted fake continuous mode thread %d on CPU %d\n", i,
+              cpus_to_pin[i]);
+       while (1)
+               ;
+}
+
+void *cont_thread(void *arg)
+{
+       char buff[MSG_SIZE];
+       int i, priority;
+
+       for (i = 0; i < num_cpus_to_pin; i++)
+               if (cpu_threads[i] == pthread_self())
+                       break;
+       printf("\tStarted continuous mode thread %d on CPU %d\n", i,
+              cpus_to_pin[i]);
+       while (1) {
+               while (mq_send(queue, buff, sizeof(buff), 0) == 0)
+                       ;
+               mq_receive(queue, buff, sizeof(buff), &priority);
+       }
+}
+
+#define drain_queue() \
+       while (mq_receive(queue, buff, MSG_SIZE, &prio_in) == MSG_SIZE)
+
+#define do_untimed_send() \
+       do { \
+               if (mq_send(queue, buff, MSG_SIZE, prio_out)) \
+                       shutdown(3, "Test send failure", __LINE__); \
+       } while (0)
+
+#define do_send_recv() \
+       do { \
+               clock_gettime(clock, &start); \
+               if (mq_send(queue, buff, MSG_SIZE, prio_out)) \
+                       shutdown(3, "Test send failure", __LINE__); \
+               clock_gettime(clock, &middle); \
+               if (mq_receive(queue, buff, MSG_SIZE, &prio_in) != MSG_SIZE) \
+                       shutdown(3, "Test receive failure", __LINE__); \
+               clock_gettime(clock, &end); \
+               nsec = ((middle.tv_sec - start.tv_sec) * 1000000000) + \
+                       (middle.tv_nsec - start.tv_nsec); \
+               send_total.tv_nsec += nsec; \
+               if (send_total.tv_nsec >= 1000000000) { \
+                       send_total.tv_sec++; \
+                       send_total.tv_nsec -= 1000000000; \
+               } \
+               nsec = ((end.tv_sec - middle.tv_sec) * 1000000000) + \
+                       (end.tv_nsec - middle.tv_nsec); \
+               recv_total.tv_nsec += nsec; \
+               if (recv_total.tv_nsec >= 1000000000) { \
+                       recv_total.tv_sec++; \
+                       recv_total.tv_nsec -= 1000000000; \
+               } \
+       } while (0)
+
+struct test {
+       char *desc;
+       void (*func)(int *);
+};
+
+void const_prio(int *prio)
+{
+       return;
+}
+
+void inc_prio(int *prio)
+{
+       if (++*prio == mq_prio_max)
+               *prio = 0;
+}
+
+void dec_prio(int *prio)
+{
+       if (--*prio < 0)
+               *prio = mq_prio_max - 1;
+}
+
+void random_prio(int *prio)
+{
+       *prio = random() % mq_prio_max;
+}
+
+struct test test2[] = {
+       {"\n\tTest #2a: Time send/recv message, queue full, constant prio\n",
+               const_prio},
+       {"\n\tTest #2b: Time send/recv message, queue full, increasing prio\n",
+               inc_prio},
+       {"\n\tTest #2c: Time send/recv message, queue full, decreasing prio\n",
+               dec_prio},
+       {"\n\tTest #2d: Time send/recv message, queue full, random prio\n",
+               random_prio},
+       {NULL, NULL}
+};
+
+/**
+ * Tests to perform (all done with MSG_SIZE messages):
+ *
+ * 1) Time to add/remove message with 0 messages on queue
+ * 1a) with constant prio
+ * 2) Time to add/remove message when queue close to capacity:
+ * 2a) with constant prio
+ * 2b) with increasing prio
+ * 2c) with decreasing prio
+ * 2d) with random prio
+ * 3) Test limits of priorities honored (double check _SC_MQ_PRIO_MAX)
+ */
+void *perf_test_thread(void *arg)
+{
+       char buff[MSG_SIZE];
+       int prio_out, prio_in;
+       int i;
+       clockid_t clock;
+       pthread_t *t;
+       struct timespec res, start, middle, end, send_total, recv_total;
+       unsigned long long nsec;
+       struct test *cur_test;
+
+       t = &cpu_threads[0];
+       printf("\n\tStarted mqueue performance test thread on CPU %d\n",
+              cpus_to_pin[0]);
+       mq_prio_max = sysconf(_SC_MQ_PRIO_MAX);
+       if (mq_prio_max == -1)
+               shutdown(2, "sysconf(_SC_MQ_PRIO_MAX)", __LINE__);
+       if (pthread_getcpuclockid(cpu_threads[0], &clock) != 0)
+               shutdown(2, "pthread_getcpuclockid", __LINE__);
+
+       if (clock_getres(clock, &res))
+               shutdown(2, "clock_getres()", __LINE__);
+
+       printf("\t\tMax priorities:\t\t\t%d\n", mq_prio_max);
+       printf("\t\tClock resolution:\t\t%d nsec%s\n", res.tv_nsec,
+              res.tv_nsec > 1 ? "s" : "");
+
+
+
+       printf("\n\tTest #1: Time send/recv message, queue empty\n");
+       printf("\t\t(%d iterations)\n", TEST1_LOOPS);
+       prio_out = 0;
+       send_total.tv_sec = 0;
+       send_total.tv_nsec = 0;
+       recv_total.tv_sec = 0;
+       recv_total.tv_nsec = 0;
+       for (i = 0; i < TEST1_LOOPS; i++)
+               do_send_recv();
+       printf("\t\tSend msg:\t\t\t%d.%ds total time\n",
+              send_total.tv_sec, send_total.tv_nsec);
+       nsec = ((unsigned long long)send_total.tv_sec * 1000000000 +
+                send_total.tv_nsec) / TEST1_LOOPS;
+       printf("\t\t\t\t\t\t%d nsec/msg\n", nsec);
+       printf("\t\tRecv msg:\t\t\t%d.%ds total time\n",
+              recv_total.tv_sec, recv_total.tv_nsec);
+       nsec = ((unsigned long long)recv_total.tv_sec * 1000000000 +
+               recv_total.tv_nsec) / TEST1_LOOPS;
+       printf("\t\t\t\t\t\t%d nsec/msg\n", nsec);
+
+
+       for (cur_test = test2; cur_test->desc != NULL; cur_test++) {
+               printf(cur_test->desc);
+               printf("\t\t(%d iterations)\n", TEST2_LOOPS);
+               prio_out = 0;
+               send_total.tv_sec = 0;
+               send_total.tv_nsec = 0;
+               recv_total.tv_sec = 0;
+               recv_total.tv_nsec = 0;
+               printf("\t\tFilling queue...");
+               fflush(stdout);
+               clock_gettime(clock, &start);
+               for (i = 0; i < result.mq_maxmsg - 1; i++) {
+                       do_untimed_send();
+                       cur_test->func(&prio_out);
+               }
+               clock_gettime(clock, &end);
+               nsec = ((unsigned long long)(end.tv_sec - start.tv_sec) *
+                       1000000000) + (end.tv_nsec - start.tv_nsec);
+               printf("done.\t\t%lld.%llds\n", nsec / 1000000000,
+                      nsec % 1000000000);
+               printf("\t\tTesting...");
+               fflush(stdout);
+               for (i = 0; i < TEST2_LOOPS; i++) {
+                       do_send_recv();
+                       cur_test->func(&prio_out);
+               }
+               printf("done.\n");
+               printf("\t\tSend msg:\t\t\t%d.%ds total time\n",
+                      send_total.tv_sec, send_total.tv_nsec);
+               nsec = ((unsigned long long)send_total.tv_sec * 1000000000 +
+                        send_total.tv_nsec) / TEST2_LOOPS;
+               printf("\t\t\t\t\t\t%d nsec/msg\n", nsec);
+               printf("\t\tRecv msg:\t\t\t%d.%ds total time\n",
+                      recv_total.tv_sec, recv_total.tv_nsec);
+               nsec = ((unsigned long long)recv_total.tv_sec * 1000000000 +
+                       recv_total.tv_nsec) / TEST2_LOOPS;
+               printf("\t\t\t\t\t\t%d nsec/msg\n", nsec);
+               printf("\t\tDraining queue...");
+               fflush(stdout);
+               clock_gettime(clock, &start);
+               drain_queue();
+               clock_gettime(clock, &end);
+               nsec = ((unsigned long long)(end.tv_sec - start.tv_sec) *
+                       1000000000) + (end.tv_nsec - start.tv_nsec);
+               printf("done.\t\t%lld.%llds\n", nsec / 1000000000,
+                      nsec % 1000000000);
+       }
+       return 0;
+}
+
+void increase_limits(void)
+{
+       cur_limits.rlim_cur = RLIM_INFINITY;
+       cur_limits.rlim_max = RLIM_INFINITY;
+       setr(RLIMIT_MSGQUEUE, &cur_limits);
+       while (try_set(max_msgs, cur_max_msgs += 10))
+               ;
+       cur_max_msgs = get(max_msgs);
+       while (try_set(max_msgsize, cur_max_msgsize += 1024))
+               ;
+       cur_max_msgsize = get(max_msgsize);
+       if (setpriority(PRIO_PROCESS, 0, -20) != 0)
+               shutdown(2, "setpriority()", __LINE__);
+       cur_nice = -20;
+}
+
+int main(int argc, char *argv[])
+{
+       struct mq_attr attr;
+       char *option, *next_option;
+       int i, cpu;
+       struct sigaction sa;
+       poptContext popt_context;
+       char rc;
+       void *retval;
+
+       main_thread = pthread_self();
+       num_cpus_to_pin = 0;
+
+       if (sysconf(_SC_NPROCESSORS_ONLN) == -1) {
+               perror("sysconf(_SC_NPROCESSORS_ONLN)");
+               exit(1);
+       }
+       cpus_online = min(MAX_CPUS, sysconf(_SC_NPROCESSORS_ONLN));
+       cpu_set = CPU_ALLOC(cpus_online);
+       if (cpu_set == NULL) {
+               perror("CPU_ALLOC()");
+               exit(1);
+       }
+       cpu_set_size = CPU_ALLOC_SIZE(cpus_online);
+       CPU_ZERO_S(cpu_set_size, cpu_set);
+
+       popt_context = poptGetContext(NULL, argc, (const char **)argv,
+                                     options, 0);
+
+       while ((rc = poptGetNextOpt(popt_context)) > 0) {
+               switch (rc) {
+               case 'c':
+                       continuous_mode = 1;
+                       option = cpu_option_string;
+                       do {
+                               next_option = strchr(option, ',');
+                               if (next_option)
+                                       *next_option = '\0';
+                               cpu = atoi(option);
+                               if (cpu >= cpus_online)
+                                       fprintf(stderr, "CPU %d exceeds "
+                                               "cpus online, ignoring.\n",
+                                               cpu);
+                               else
+                                       cpus_to_pin[num_cpus_to_pin++] = cpu;
+                               if (next_option)
+                                       option = ++next_option;
+                       } while (next_option && num_cpus_to_pin < MAX_CPUS);
+                       /* Double check that they didn't give us the same CPU
+                        * more than once */
+                       for (cpu = 0; cpu < num_cpus_to_pin; cpu++) {
+                               if (CPU_ISSET_S(cpus_to_pin[cpu], cpu_set_size,
+                                               cpu_set)) {
+                                       fprintf(stderr, "Any given CPU may "
+                                               "only be given once.\n");
+                                       exit(1);
+                               } else
+                                       CPU_SET_S(cpus_to_pin[cpu],
+                                                 cpu_set_size, cpu_set);
+                       }
+                       break;
+               case 'p':
+                       /*
+                        * Although we can create a msg queue with a
+                        * non-absolute path name, unlink will fail.  So,
+                        * if the name doesn't start with a /, add one
+                        * when we save it.
+                        */
+                       option = queue_path;
+                       if (*option != '/') {
+                               queue_path = malloc(strlen(option) + 2);
+                               if (!queue_path) {
+                                       perror("malloc()");
+                                       exit(1);
+                               }
+                               queue_path[0] = '/';
+                               queue_path[1] = 0;
+                               strcat(queue_path, option);
+                               free(option);
+                       }
+                       break;
+               }
+       }
+
+       if (continuous_mode && num_cpus_to_pin == 0) {
+               fprintf(stderr, "Must pass at least one CPU to continuous "
+                       "mode.\n");
+               poptPrintUsage(popt_context, stderr, 0);
+               exit(1);
+       } else if (!continuous_mode) {
+               num_cpus_to_pin = 1;
+               cpus_to_pin[0] = cpus_online - 1;
+       }
+
+       if (getuid() != 0) {
+               fprintf(stderr, "Not running as root, but almost all tests "
+                       "require root in order to modify\nsystem settings.  "
+                       "Exiting.\n");
+               exit(1);
+       }
+
+       max_msgs = fopen(MAX_MSGS, "r+");
+       max_msgsize = fopen(MAX_MSGSIZE, "r+");
+       if (!max_msgs)
+               shutdown(2, "Failed to open msg_max", __LINE__);
+       if (!max_msgsize)
+               shutdown(2, "Failed to open msgsize_max", __LINE__);
+
+       /* Load up the current system values for everything we can */
+       getr(RLIMIT_MSGQUEUE, &saved_limits);
+       cur_limits = saved_limits;
+       saved_max_msgs = cur_max_msgs = get(max_msgs);
+       saved_max_msgsize = cur_max_msgsize = get(max_msgsize);
+       errno = 0;
+       cur_nice = getpriority(PRIO_PROCESS, 0);
+       if (errno)
+               shutdown(2, "getpriority()", __LINE__);
+
+       /* Tell the user our initial state */
+       printf("\nInitial system state:\n");
+       printf("\tUsing queue path:\t\t\t%s\n", queue_path);
+       printf("\tRLIMIT_MSGQUEUE(soft):\t\t\t%d\n", saved_limits.rlim_cur);
+       printf("\tRLIMIT_MSGQUEUE(hard):\t\t\t%d\n", saved_limits.rlim_max);
+       printf("\tMaximum Message Size:\t\t\t%d\n", saved_max_msgsize);
+       printf("\tMaximum Queue Size:\t\t\t%d\n", saved_max_msgs);
+       printf("\tNice value:\t\t\t\t%d\n", cur_nice);
+       printf("\n");
+
+       increase_limits();
+
+       printf("Adjusted system state for testing:\n");
+       if (cur_limits.rlim_cur == RLIM_INFINITY) {
+               printf("\tRLIMIT_MSGQUEUE(soft):\t\t\t(unlimited)\n");
+               printf("\tRLIMIT_MSGQUEUE(hard):\t\t\t(unlimited)\n");
+       } else {
+               printf("\tRLIMIT_MSGQUEUE(soft):\t\t\t%d\n",
+                      cur_limits.rlim_cur);
+               printf("\tRLIMIT_MSGQUEUE(hard):\t\t\t%d\n",
+                      cur_limits.rlim_max);
+       }
+       printf("\tMaximum Message Size:\t\t\t%d\n", cur_max_msgsize);
+       printf("\tMaximum Queue Size:\t\t\t%d\n", cur_max_msgs);
+       printf("\tNice value:\t\t\t\t%d\n", cur_nice);
+       printf("\tContinuous mode:\t\t\t(%s)\n", continuous_mode ?
+              (continuous_mode_fake ? "fake mode" : "enabled") :
+              "disabled");
+       printf("\tCPUs to pin:\t\t\t\t%d", cpus_to_pin[0]);
+       for (cpu = 1; cpu < num_cpus_to_pin; cpu++)
+                       printf(",%d", cpus_to_pin[cpu]);
+       printf("\n");
+
+       sa.sa_sigaction = sig_action_SIGUSR1;
+       sigemptyset(&sa.sa_mask);
+       sigaddset(&sa.sa_mask, SIGHUP);
+       sigaddset(&sa.sa_mask, SIGINT);
+       sigaddset(&sa.sa_mask, SIGQUIT);
+       sigaddset(&sa.sa_mask, SIGTERM);
+       sa.sa_flags = SA_SIGINFO;
+       if (sigaction(SIGUSR1, &sa, NULL) == -1)
+               shutdown(1, "sigaction(SIGUSR1)", __LINE__);
+       sa.sa_sigaction = sig_action;
+       if (sigaction(SIGHUP, &sa, NULL) == -1)
+               shutdown(1, "sigaction(SIGHUP)", __LINE__);
+       if (sigaction(SIGINT, &sa, NULL) == -1)
+               shutdown(1, "sigaction(SIGINT)", __LINE__);
+       if (sigaction(SIGQUIT, &sa, NULL) == -1)
+               shutdown(1, "sigaction(SIGQUIT)", __LINE__);
+       if (sigaction(SIGTERM, &sa, NULL) == -1)
+               shutdown(1, "sigaction(SIGTERM)", __LINE__);
+
+       if (!continuous_mode_fake) {
+               attr.mq_flags = O_NONBLOCK;
+               attr.mq_maxmsg = cur_max_msgs;
+               attr.mq_msgsize = MSG_SIZE;
+               open_queue(&attr);
+       }
+       for (i = 0; i < num_cpus_to_pin; i++) {
+               pthread_attr_t thread_attr;
+               void *thread_func;
+
+               if (continuous_mode_fake)
+                       thread_func = &fake_cont_thread;
+               else if (continuous_mode)
+                       thread_func = &cont_thread;
+               else
+                       thread_func = &perf_test_thread;
+
+               CPU_ZERO_S(cpu_set_size, cpu_set);
+               CPU_SET_S(cpus_to_pin[i], cpu_set_size, cpu_set);
+               pthread_attr_init(&thread_attr);
+               pthread_attr_setaffinity_np(&thread_attr, cpu_set_size,
+                                           cpu_set);
+               if (pthread_create(&cpu_threads[i], &thread_attr, thread_func,
+                                  NULL))
+                       shutdown(1, "pthread_create()", __LINE__);
+               pthread_attr_destroy(&thread_attr);
+       }
+
+       if (!continuous_mode) {
+               pthread_join(cpu_threads[0], &retval);
+               shutdown((long)retval, "perf_test_thread()", __LINE__);
+       } else {
+               while (1)
+                       sleep(1);
+       }
+       shutdown(0, "", 0);
+}
index 7dab7b25b5c6175fc536b46ddd87f3f7acd7b81c..f576971f6556f0b08dcd30ef24af4fef61d01e91 100644 (file)
@@ -35,6 +35,7 @@
 #include <sys/mount.h>
 #include <sys/statfs.h>
 #include "../../include/linux/magic.h"
+#include "../../include/linux/kernel-page-flags.h"
 
 
 #ifndef MAX_PATH
 #define KPF_BYTES              8
 #define PROC_KPAGEFLAGS                "/proc/kpageflags"
 
-/* copied from kpageflags_read() */
-#define KPF_LOCKED             0
-#define KPF_ERROR              1
-#define KPF_REFERENCED         2
-#define KPF_UPTODATE           3
-#define KPF_DIRTY              4
-#define KPF_LRU                        5
-#define KPF_ACTIVE             6
-#define KPF_SLAB               7
-#define KPF_WRITEBACK          8
-#define KPF_RECLAIM            9
-#define KPF_BUDDY              10
-
-/* [11-20] new additions in 2.6.31 */
-#define KPF_MMAP               11
-#define KPF_ANON               12
-#define KPF_SWAPCACHE          13
-#define KPF_SWAPBACKED         14
-#define KPF_COMPOUND_HEAD      15
-#define KPF_COMPOUND_TAIL      16
-#define KPF_HUGE               17
-#define KPF_UNEVICTABLE                18
-#define KPF_HWPOISON           19
-#define KPF_NOPAGE             20
-#define KPF_KSM                        21
-#define KPF_THP                        22
-
 /* [32-] kernel hacking assistances */
 #define KPF_RESERVED           32
 #define KPF_MLOCKED            33
@@ -326,7 +300,7 @@ static char *page_flag_name(uint64_t flags)
 {
        static char buf[65];
        int present;
-       int i, j;
+       size_t i, j;
 
        for (i = 0, j = 0; i < ARRAY_SIZE(page_flag_names); i++) {
                present = (flags >> i) & 1;
@@ -344,7 +318,7 @@ static char *page_flag_name(uint64_t flags)
 static char *page_flag_longname(uint64_t flags)
 {
        static char buf[1024];
-       int i, n;
+       size_t i, n;
 
        for (i = 0, n = 0; i < ARRAY_SIZE(page_flag_names); i++) {
                if (!page_flag_names[i])
@@ -402,7 +376,7 @@ static void show_page(unsigned long voffset,
 
 static void show_summary(void)
 {
-       int i;
+       size_t i;
 
        printf("             flags\tpage-count       MB"
                "  symbolic-flags\t\t\tlong-symbolic-flags\n");
@@ -500,7 +474,7 @@ static int debugfs_valid_mountpoint(const char *debugfs)
 /* find the path to the mounted debugfs */
 static const char *debugfs_find_mountpoint(void)
 {
-       const char **ptr;
+       const char *const *ptr;
        char type[100];
        FILE *fp;
 
@@ -537,7 +511,7 @@ static const char *debugfs_find_mountpoint(void)
 
 static void debugfs_mount(void)
 {
-       const char **ptr;
+       const char *const *ptr;
 
        /* see if it's already mounted */
        if (debugfs_find_mountpoint())
@@ -614,10 +588,10 @@ static int unpoison_page(unsigned long offset)
  * page frame walker
  */
 
-static int hash_slot(uint64_t flags)
+static size_t hash_slot(uint64_t flags)
 {
-       int k = HASH_KEY(flags);
-       int i;
+       size_t k = HASH_KEY(flags);
+       size_t i;
 
        /* Explicitly reserve slot 0 for flags 0: the following logic
         * cannot distinguish an unoccupied slot from slot (flags==0).
@@ -670,7 +644,7 @@ static void walk_pfn(unsigned long voffset,
 {
        uint64_t buf[KPAGEFLAGS_BATCH];
        unsigned long batch;
-       long pages;
+       unsigned long pages;
        unsigned long i;
 
        while (count) {
@@ -779,7 +753,7 @@ static const char *page_flag_type(uint64_t flag)
 
 static void usage(void)
 {
-       int i, j;
+       size_t i, j;
 
        printf(
 "page-types [options]\n"
@@ -938,7 +912,7 @@ static void add_bits_filter(uint64_t mask, uint64_t bits)
 
 static uint64_t parse_flag_name(const char *str, int len)
 {
-       int i;
+       size_t i;
 
        if (!*str || !len)
                return 0;
index 65b845bd4e3e792ca09ce01aa31da92ced51a302..085872bb2bb593502024d020316d2978c2cf9031 100644 (file)
@@ -134,7 +134,7 @@ config INITRAMFS_COMPRESSION_BZIP2
        depends on RD_BZIP2
        help
          Its compression ratio and speed is intermediate.
-         Decompression speed is slowest among the four.  The initramfs
+         Decompression speed is slowest among the choices.  The initramfs
          size is about 10% smaller with bzip2, in comparison to gzip.
          Bzip2 uses a large amount of memory. For modern kernels you
          will need at least 8MB RAM or more for booting.
@@ -143,9 +143,9 @@ config INITRAMFS_COMPRESSION_LZMA
        bool "LZMA"
        depends on RD_LZMA
        help
-         The most recent compression algorithm.
-         Its ratio is best, decompression speed is between the other
-         three. Compression is slowest. The initramfs size is about 33%
+         This algorithm's compression ratio is best.
+         Decompression speed is between the other choices.
+         Compression is slowest. The initramfs size is about 33%
          smaller with LZMA in comparison to gzip.
 
 config INITRAMFS_COMPRESSION_XZ
@@ -161,7 +161,7 @@ config INITRAMFS_COMPRESSION_LZO
        bool "LZO"
        depends on RD_LZO
        help
-         Its compression ratio is the poorest among the four. The kernel
+         Its compression ratio is the poorest among the choices. The kernel
          size is about 10% bigger than gzip; however its speed
          (both compression and decompression) is the fastest.